-module(oaspec@openapi@capability_check). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/oaspec/openapi/capability_check.gleam"). -export([check_preserved/1, check/1]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -file("src/oaspec/openapi/capability_check.gleam", 191). ?DOC(" Check security schemes for unsupported types (e.g. mutualTLS).\n"). -spec check_security_schemes( oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:resolved()) ) -> list(oaspec@openapi@diagnostic:diagnostic()). check_security_schemes(Spec) -> case erlang:element(5, Spec) of none -> []; {some, Components} -> _pipe = maps:to_list(erlang:element(6, Components)), gleam@list:filter_map( _pipe, fun(Entry) -> {Name, Ref_or} = Entry, case Ref_or of {value, {unsupported_scheme, Scheme_type}} -> {ok, oaspec@openapi@diagnostic:capability( <<"components.securitySchemes."/utf8, Name/binary>>, <<<<"Unsupported security scheme type: '"/utf8, Scheme_type/binary>>/binary, "'. Supported types: apiKey, http, oauth2, openIdConnect."/utf8>>, severity_error, target_both, {some, <<"Use apiKey, http (bearer), oauth2, or openIdConnect instead."/utf8>>} )}; _ -> {error, nil} end end ) end. -file("src/oaspec/openapi/capability_check.gleam", 220). ?DOC( " Check for parsed-but-unused AST features, emitting warnings.\n" " This is the single source of truth for \"parsed but not generated\" diagnostics.\n" " Requires Context because some checks iterate over resolved operations.\n" ). -spec check_preserved(oaspec@codegen@context:context()) -> list(oaspec@openapi@diagnostic:diagnostic()). check_preserved(Ctx) -> Webhook_warnings = case gleam@dict:is_empty( erlang:element(8, oaspec@codegen@context:spec(Ctx)) ) of true -> []; false -> [oaspec@openapi@diagnostic:capability( <<"webhooks"/utf8>>, <<"Webhooks are parsed but not used by code generation."/utf8>>, severity_warning, target_both, {some, <<"Webhooks will not appear in generated code. No action needed unless you expected them."/utf8>>} )] end, Ops = oaspec@openapi@operations:collect_operations(Ctx), Response_warnings = gleam@list:flat_map( Ops, fun(Op) -> {Op_id, Operation, _, _} = Op, Entries = maps:to_list(erlang:element(8, Operation)), gleam@list:flat_map( Entries, fun(Entry) -> {Status_code, Ref_or} = Entry, case Ref_or of {value, Response} -> Base_path = <<<>/binary, (oaspec@util@http:status_code_to_string( Status_code ))/binary>>, Multi_content_warnings = case {oaspec@config:mode( oaspec@codegen@context:config(Ctx) ), erlang:length( maps:to_list(erlang:element(3, Response)) )} of {client, _} -> []; {_, N} when N > 1 -> [oaspec@openapi@diagnostic:capability( <>, <<"Multiple response content types are not fully supported for server code generation. Generated server responses lose the content-type header."/utf8>>, severity_warning, target_server, {some, <<"Use a single content type per response for full server code generation support."/utf8>>} )]; {_, _} -> [] end, Header_warnings = case gleam@dict:is_empty( erlang:element(4, Response) ) of true -> []; false -> [oaspec@openapi@diagnostic:capability( <>, <<"Response headers are parsed but not used by code generation."/utf8>>, severity_warning, target_both, {some, <<"Response headers will not appear in generated code. No action needed."/utf8>>} )] end, Link_warnings = case gleam@dict:is_empty( erlang:element(5, Response) ) of true -> []; false -> [oaspec@openapi@diagnostic:capability( <>, <<"Response links are parsed but not used by code generation."/utf8>>, severity_warning, target_both, {some, <<"Response links will not appear in generated code. No action needed."/utf8>>} )] end, Content_entries = maps:to_list( erlang:element(3, Response) ), Encoding_warnings = gleam@list:flat_map( Content_entries, fun(Ce) -> {Media_type_name, Media_type} = Ce, case gleam@dict:is_empty( erlang:element(5, Media_type) ) of true -> []; false -> [oaspec@openapi@diagnostic:capability( <<<<<>/binary, Media_type_name/binary>>/binary, ".encoding"/utf8>>, <<"MediaType encoding is parsed but not used by code generation."/utf8>>, severity_warning, target_both, {some, <<"Encoding settings will not affect generated code. No action needed."/utf8>>} )] end end ), lists:append( [Multi_content_warnings, Header_warnings, Link_warnings, Encoding_warnings] ); _ -> [] end end ) end ), External_docs_warnings = case erlang:element( 10, oaspec@codegen@context:spec(Ctx) ) of {some, _} -> [oaspec@openapi@diagnostic:capability( <<"externalDocs"/utf8>>, <<"External docs are parsed but not used by code generation."/utf8>>, severity_warning, target_both, {some, <<"External docs will not appear in generated code. Include documentation in descriptions instead."/utf8>>} )]; none -> [] end, Tag_warnings = case gleam@list:is_empty( erlang:element(9, oaspec@codegen@context:spec(Ctx)) ) of true -> []; false -> [oaspec@openapi@diagnostic:capability( <<"tags"/utf8>>, <<"Top-level tags are parsed but not used by code generation."/utf8>>, severity_warning, target_both, {some, <<"Tags will not appear in generated code. No action needed."/utf8>>} )] end, Operation_server_warnings = [], Path_server_warnings = [], Component_warnings = case erlang:element( 5, oaspec@codegen@context:spec(Ctx) ) of {some, Components} -> Header_w = case gleam@dict:is_empty(erlang:element(8, Components)) of true -> []; false -> [oaspec@openapi@diagnostic:capability( <<"components.headers"/utf8>>, <<"Component headers are parsed but not used by code generation."/utf8>>, severity_warning, target_both, {some, <<"Component headers will not appear in generated code. No action needed."/utf8>>} )] end, Example_w = case gleam@dict:is_empty(erlang:element(9, Components)) of true -> []; false -> [oaspec@openapi@diagnostic:capability( <<"components.examples"/utf8>>, <<"Component examples are parsed but not used by code generation."/utf8>>, severity_warning, target_both, {some, <<"Component examples will not appear in generated code. Include examples in descriptions instead."/utf8>>} )] end, Link_w = case gleam@dict:is_empty(erlang:element(10, Components)) of true -> []; false -> [oaspec@openapi@diagnostic:capability( <<"components.links"/utf8>>, <<"Component links are parsed but not used by code generation."/utf8>>, severity_warning, target_both, {some, <<"Component links will not appear in generated code. No action needed."/utf8>>} )] end, lists:append([Header_w, Example_w, Link_w]); none -> [] end, lists:append( [Webhook_warnings, Response_warnings, External_docs_warnings, Tag_warnings, Operation_server_warnings, Path_server_warnings, Component_warnings] ). -file("src/oaspec/openapi/capability_check.gleam", 134). ?DOC(" Check a single schema and recurse into children.\n"). -spec check_schema(binary(), oaspec@openapi@schema:schema_object()) -> list(oaspec@openapi@diagnostic:diagnostic()). check_schema(Path, Schema_obj) -> Metadata = oaspec@openapi@schema:get_metadata(Schema_obj), Keyword_errors = case erlang:element(12, Metadata) of [] -> []; Keywords -> Keyword_list = gleam@string:join(Keywords, <<"', '"/utf8>>), [oaspec@openapi@diagnostic:capability( Path, <<<<<<"Unsupported JSON Schema keyword '"/utf8, Keyword_list/binary>>/binary, "' found. Remove or model differently. See: "/utf8>>/binary, (gleam@string:join( gleam@list:filter_map( Keywords, fun(K) -> _pipe = gleam@list:find( oaspec@capability:registry(), fun(C) -> erlang:element(2, C) =:= K end ), gleam@result:map( _pipe, fun(C@1) -> erlang:element(5, C@1) end ) end ), <<"; "/utf8>> ))/binary>>, severity_error, target_both, {some, <<"Remove the keyword or restructure the schema using supported constructs."/utf8>>} )] end, Child_errors = case Schema_obj of {object_schema, _, Properties, _, Additional_properties, _, _} -> Prop_errors = begin _pipe@1 = maps:to_list(Properties), gleam@list:flat_map( _pipe@1, fun(E) -> check_schema_ref( <<<>/binary, (erlang:element(1, E))/binary>>, erlang:element(2, E) ) end ) end, Ap_errors = case Additional_properties of {typed, Ap} -> check_schema_ref( <>, Ap ); _ -> [] end, lists:append(Prop_errors, Ap_errors); {array_schema, _, Items, _, _, _} -> check_schema_ref(<>, Items); {all_of_schema, _, Schemas} -> gleam@list:flat_map( Schemas, fun(S) -> check_schema_ref(<>, S) end ); {one_of_schema, _, Schemas@1, _} -> gleam@list:flat_map( Schemas@1, fun(S@1) -> check_schema_ref(<>, S@1) end ); {any_of_schema, _, Schemas@2, _} -> gleam@list:flat_map( Schemas@2, fun(S@2) -> check_schema_ref(<>, S@2) end ); _ -> [] end, lists:append(Keyword_errors, Child_errors). -file("src/oaspec/openapi/capability_check.gleam", 126). ?DOC(" Check a SchemaRef recursively for unsupported keywords.\n"). -spec check_schema_ref(binary(), oaspec@openapi@schema:schema_ref()) -> list(oaspec@openapi@diagnostic:diagnostic()). check_schema_ref(Path, Ref) -> case Ref of {reference, _, _} -> []; {inline, Schema_obj} -> check_schema(Path, Schema_obj) end. -file("src/oaspec/openapi/capability_check.gleam", 79). -spec check_operation_schemas( binary(), oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()) ) -> list(oaspec@openapi@diagnostic:diagnostic()). check_operation_schemas(Base_path, Op) -> Rb_errors = case erlang:element(7, Op) of {some, {value, Rb}} -> _pipe = maps:to_list(erlang:element(3, Rb)), gleam@list:flat_map( _pipe, fun(Entry) -> {Ct, Mt} = Entry, case erlang:element(2, Mt) of {some, Sr} -> check_schema_ref( <<<>/binary, Ct/binary>>, Sr ); none -> [] end end ); _ -> [] end, Resp_errors = begin _pipe@1 = maps:to_list(erlang:element(8, Op)), gleam@list:flat_map( _pipe@1, fun(Entry@1) -> {Code, Ref_or} = Entry@1, case Ref_or of {value, Resp} -> _pipe@2 = maps:to_list(erlang:element(3, Resp)), gleam@list:flat_map( _pipe@2, fun(Ct_entry) -> {Ct@1, Mt@1} = Ct_entry, case erlang:element(2, Mt@1) of {some, Sr@1} -> check_schema_ref( <<<<<<<>/binary, (oaspec@util@http:status_code_to_string( Code ))/binary>>/binary, "."/utf8>>/binary, Ct@1/binary>>, Sr@1 ); none -> [] end end ); _ -> [] end end ) end, lists:append(Rb_errors, Resp_errors). -file("src/oaspec/openapi/capability_check.gleam", 56). -spec check_path_item_schemas( binary(), oaspec@openapi@spec:path_item(oaspec@openapi@spec:resolved()) ) -> list(oaspec@openapi@diagnostic:diagnostic()). check_path_item_schemas(Base_path, Pi) -> Method_ops = [{<<"get"/utf8>>, erlang:element(4, Pi)}, {<<"post"/utf8>>, erlang:element(5, Pi)}, {<<"put"/utf8>>, erlang:element(6, Pi)}, {<<"delete"/utf8>>, erlang:element(7, Pi)}, {<<"patch"/utf8>>, erlang:element(8, Pi)}, {<<"head"/utf8>>, erlang:element(9, Pi)}, {<<"options"/utf8>>, erlang:element(10, Pi)}, {<<"trace"/utf8>>, erlang:element(11, Pi)}], gleam@list:flat_map( Method_ops, fun(Entry) -> {Method, Maybe_op} = Entry, case Maybe_op of {some, Op} -> check_operation_schemas( <<<>/binary, Method/binary>>, Op ); none -> [] end end ). -file("src/oaspec/openapi/capability_check.gleam", 45). ?DOC(" Check inline schemas within operations (request bodies, responses, parameters).\n"). -spec check_inline_schemas( oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:resolved()) ) -> list(oaspec@openapi@diagnostic:diagnostic()). check_inline_schemas(Spec) -> _pipe = maps:to_list(erlang:element(4, Spec)), gleam@list:flat_map( _pipe, fun(Path_entry) -> {Path, Ref_or} = Path_entry, case Ref_or of {value, Path_item} -> check_path_item_schemas( <<"paths."/utf8, Path/binary>>, Path_item ); _ -> [] end end ). -file("src/oaspec/openapi/capability_check.gleam", 30). ?DOC( " Check all schemas for unsupported keywords stored during lossless parse.\n" " Covers both component schemas and inline schemas in operations.\n" ). -spec check_schemas( oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:resolved()) ) -> list(oaspec@openapi@diagnostic:diagnostic()). check_schemas(Spec) -> Component_errors = case erlang:element(5, Spec) of none -> []; {some, Components} -> _pipe = maps:to_list(erlang:element(2, Components)), gleam@list:flat_map( _pipe, fun(Entry) -> {Name, Schema_ref} = Entry, check_schema_ref( <<"components.schemas."/utf8, Name/binary>>, Schema_ref ) end ) end, Inline_errors = check_inline_schemas(Spec), lists:append(Component_errors, Inline_errors). -file("src/oaspec/openapi/capability_check.gleam", 22). ?DOC( " Run capability checks on a resolved spec.\n" " Returns errors for unsupported features and warnings for parsed-but-unused features.\n" ). -spec check(oaspec@openapi@spec:open_api_spec(oaspec@openapi@spec:resolved())) -> list(oaspec@openapi@diagnostic:diagnostic()). check(Spec) -> Schema_errors = check_schemas(Spec), Security_errors = check_security_schemes(Spec), lists:append([Schema_errors, Security_errors]).