-module(oaspec@codegen@validate). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/oaspec/codegen/validate.gleam"). -export([errors_only/1, warnings_only/1, filter_by_mode/2, error_to_string/1, validate/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/codegen/validate.gleam", 82). -spec group_operations_by_id( list({binary(), oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()), binary(), oaspec@openapi@spec:http_method()}), fun((binary()) -> binary()) ) -> gleam@dict:dict(binary(), list(binary())). group_operations_by_id(Operations, Key_fn) -> gleam@list:fold( Operations, maps:new(), fun(Acc, Entry) -> {Op_id, _, Path, Method} = Entry, Key = Key_fn(Op_id), Site = <<<<(string:uppercase( oaspec@openapi@spec:method_to_string(Method) ))/binary, " "/utf8>>/binary, Path/binary>>, case gleam_stdlib:map_get(Acc, Key) of {ok, Existing} -> gleam@dict:insert(Acc, Key, lists:append(Existing, [Site])); {error, _} -> gleam@dict:insert(Acc, Key, [Site]) end end ). -file("src/oaspec/codegen/validate.gleam", 98). -spec duplicate_operation_id_diagnostic(binary(), list(binary())) -> oaspec@openapi@diagnostic:diagnostic(). duplicate_operation_id_diagnostic(Op_id, Sites) -> oaspec@openapi@diagnostic:invalid_value( <<"paths.*.operationId"/utf8>>, <<<<<<<<<<"Duplicate operationId '"/utf8, Op_id/binary>>/binary, "' found on: "/utf8>>/binary, (gleam@string:join(Sites, <<", "/utf8>>))/binary>>/binary, ". operationId must be unique across the entire spec; "/utf8>>/binary, "rename one of the operations to keep the generated API stable."/utf8>>, no_source_loc ). -file("src/oaspec/codegen/validate.gleam", 114). -spec duplicate_function_name_diagnostic(binary(), list(binary())) -> oaspec@openapi@diagnostic:diagnostic(). duplicate_function_name_diagnostic(Fn_name, Sites) -> oaspec@openapi@diagnostic:invalid_value( <<"paths.*.operationId"/utf8>>, <<<<<<<<<<"operationIds that normalize to the same generated function name '"/utf8, Fn_name/binary>>/binary, "' found on: "/utf8>>/binary, (gleam@string:join(Sites, <<", "/utf8>>))/binary>>/binary, ". oaspec converts operationIds to snake_case, so values like "/utf8>>/binary, "'listItems' and 'list_items' collide; rename one of them."/utf8>>, no_source_loc ). -file("src/oaspec/codegen/validate.gleam", 42). ?DOC( " Fail the spec if two operations end up sharing an operationId, either\n" " literally or after snake_case conversion to the generated function\n" " name. Returns one diagnostic per distinct colliding name, listing all\n" " `METHOD /path` sites that claimed it.\n" ). -spec validate_unique_operation_ids( list({binary(), oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()), binary(), oaspec@openapi@spec:http_method()}) ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_unique_operation_ids(Operations) -> Literal = group_operations_by_id(Operations, fun(Op_id) -> Op_id end), By_function = group_operations_by_id( Operations, fun oaspec@util@naming:operation_to_function_name/1 ), Literal_errors = begin _pipe = maps:to_list(Literal), gleam@list:filter_map( _pipe, fun(Entry) -> {Op_id@1, Sites} = Entry, case Sites of [_, _ | _] -> {ok, duplicate_operation_id_diagnostic(Op_id@1, Sites)}; _ -> {error, nil} end end ) end, Function_errors = begin _pipe@1 = maps:to_list(By_function), gleam@list:filter_map( _pipe@1, fun(Entry@1) -> {Fn_name, Sites@1} = Entry@1, case Sites@1 of [_, _ | _] -> case gleam_stdlib:map_get(Literal, Fn_name) of {ok, Literal_sites} when Literal_sites =:= Sites@1 -> {error, nil}; _ -> {ok, duplicate_function_name_diagnostic( Fn_name, Sites@1 )} end; _ -> {error, nil} end end ) end, lists:append(Literal_errors, Function_errors). -file("src/oaspec/codegen/validate.gleam", 131). ?DOC(" Filter to only errors (not warnings).\n"). -spec errors_only(list(oaspec@openapi@diagnostic:diagnostic())) -> list(oaspec@openapi@diagnostic:diagnostic()). errors_only(Issues) -> oaspec@openapi@diagnostic:errors_only(Issues). -file("src/oaspec/codegen/validate.gleam", 136). ?DOC(" Filter to only warnings (not errors).\n"). -spec warnings_only(list(oaspec@openapi@diagnostic:diagnostic())) -> list(oaspec@openapi@diagnostic:diagnostic()). warnings_only(Issues) -> oaspec@openapi@diagnostic:warnings_only(Issues). -file("src/oaspec/codegen/validate.gleam", 141). ?DOC(" Filter validation issues to those relevant for the selected generation mode.\n"). -spec filter_by_mode( list(oaspec@openapi@diagnostic:diagnostic()), oaspec@config:generate_mode() ) -> list(oaspec@openapi@diagnostic:diagnostic()). filter_by_mode(Issues, Mode) -> oaspec@openapi@diagnostic:filter_by_mode(Issues, Mode). -file("src/oaspec/codegen/validate.gleam", 149). ?DOC(" Convert a validation error to a human-readable string.\n"). -spec error_to_string(oaspec@openapi@diagnostic:diagnostic()) -> binary(). error_to_string(Error) -> oaspec@openapi@diagnostic:to_string(Error). -file("src/oaspec/codegen/validate.gleam", 247). ?DOC(" Extract parameter names from path template, e.g. \"/items/{id}\" -> [\"id\"].\n"). -spec extract_path_template_names(binary()) -> list(binary()). extract_path_template_names(Path) -> Re@1 = case gleam@regexp:from_string(<<"\\{([^}]+)\\}"/utf8>>) of {ok, Re} -> Re; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"oaspec/codegen/validate"/utf8>>, function => <<"extract_path_template_names"/utf8>>, line => 249, value => _assert_fail, start => 8746, 'end' => 8801, pattern_start => 8757, pattern_end => 8763}) end, _pipe = gleam@regexp:scan(Re@1, Path), gleam@list:filter_map(_pipe, fun(Match) -> case erlang:element(3, Match) of [{some, Name}] -> {ok, Name}; _ -> {error, nil} end end). -file("src/oaspec/codegen/validate.gleam", 205). ?DOC( " Validate that all {param} templates in the path have a corresponding\n" " path parameter definition. Reports unbound templates that would produce\n" " invalid generated code with literal {param} in URLs.\n" ). -spec validate_path_template_params( binary(), binary(), list(oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved())) ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_path_template_params(Op_id, Path, Params) -> Template_names = extract_path_template_names(Path), Path_param_names = gleam@list:filter_map( Params, fun(P) -> case erlang:element(3, P) of in_path -> {ok, erlang:element(2, P)}; _ -> {error, nil} end end ), gleam@list:filter_map( Template_names, fun(Name) -> case gleam@list:contains(Path_param_names, Name) of true -> {error, nil}; false -> Defined = case Path_param_names of [] -> <<""/utf8>>; Names -> <<<<" Defined path parameters: "/utf8, (gleam@string:join(Names, <<", "/utf8>>))/binary>>/binary, "."/utf8>> end, {ok, oaspec@openapi@diagnostic:validation( <>, <<<<<<<<"Path template parameter '{"/utf8, Name/binary>>/binary, "}' in '"/utf8>>/binary, Path/binary>>/binary, "' has no corresponding parameter definition."/utf8>>, severity_error, target_both, {some, <<"Add a parameter definition with 'in: path' for this variable, or remove it from the path template."/utf8, Defined/binary>>} )} end end ). -file("src/oaspec/codegen/validate.gleam", 482). -spec validate_server_cookie_param( binary(), oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved()), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_server_cookie_param(_, _, _) -> []. -file("src/oaspec/codegen/validate.gleam", 575). -spec resolve_schema_object( gleam@option:option(oaspec@openapi@schema:schema_ref()), oaspec@codegen@context:context() ) -> gleam@option:option(oaspec@openapi@schema:schema_object()). resolve_schema_object(Schema_ref, Ctx) -> case Schema_ref of {some, {inline, Schema_obj}} -> {some, Schema_obj}; {some, Schema_ref@1} -> case oaspec@openapi@resolver:resolve_schema_ref( Schema_ref@1, oaspec@codegen@context:spec(Ctx) ) of {ok, Schema_obj@1} -> {some, Schema_obj@1}; {error, _} -> none end; none -> none end. -file("src/oaspec/codegen/validate.gleam", 323). ?DOC( " Validate pipeDelimited / spaceDelimited parameter styles.\n" " Both are only meaningful for array-typed query parameters; reject elsewhere.\n" ). -spec validate_delimited_style( binary(), oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved()), binary(), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_delimited_style(Path, Param, Style_name, Ctx) -> Location_errors = case erlang:element(3, Param) of in_query -> []; _ -> [oaspec@openapi@diagnostic:validation( Path, <<<<"Parameter style '"/utf8, Style_name/binary>>/binary, "' is only supported for 'in: query'."/utf8>>, severity_error, target_both, {some, <<"Move this parameter to 'in: query' or switch to a style valid for its location."/utf8>>} )] end, Schema_errors = case resolve_schema_object( oaspec@openapi@spec:parameter_schema(Param), Ctx ) of {some, {array_schema, _, _, _, _, _}} -> []; _ -> [oaspec@openapi@diagnostic:validation( Path, <<<<"Parameter style '"/utf8, Style_name/binary>>/binary, "' requires an array schema."/utf8>>, severity_error, target_both, {some, <<"Change the schema to 'type: array' or switch to style 'form'."/utf8>>} )] end, lists:append([Location_errors, Schema_errors]). -file("src/oaspec/codegen/validate.gleam", 453). -spec deep_object_server_leaf_supported( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). deep_object_server_leaf_supported(Schema_ref, Ctx) -> case Schema_ref of {inline, {string_schema, _, _, _, _, _, _}} -> true; {inline, {integer_schema, _, _, _, _, _, _, _}} -> true; {inline, {number_schema, _, _, _, _, _, _, _}} -> true; {inline, {boolean_schema, _}} -> true; {inline, {array_schema, _, {inline, {string_schema, _, _, _, _, _, _}}, _, _, _}} -> true; {inline, {array_schema, _, {inline, {integer_schema, _, _, _, _, _, _, _}}, _, _, _}} -> true; {inline, {array_schema, _, {inline, {number_schema, _, _, _, _, _, _, _}}, _, _, _}} -> true; {inline, {array_schema, _, {inline, {boolean_schema, _}}, _, _, _}} -> true; {reference, _, _} -> case resolve_schema_object({some, Schema_ref}, Ctx) of {some, {string_schema, _, _, _, _, _, _}} -> true; {some, {integer_schema, _, _, _, _, _, _, _}} -> true; {some, {number_schema, _, _, _, _, _, _, _}} -> true; {some, {boolean_schema, _}} -> true; {some, {array_schema, _, {inline, {string_schema, _, _, _, _, _, _}}, _, _, _}} -> true; {some, {array_schema, _, {inline, {integer_schema, _, _, _, _, _, _, _}}, _, _, _}} -> true; {some, {array_schema, _, {inline, {number_schema, _, _, _, _, _, _, _}}, _, _, _}} -> true; {some, {array_schema, _, {inline, {boolean_schema, _}}, _, _, _}} -> true; _ -> false end; _ -> false end. -file("src/oaspec/codegen/validate.gleam", 417). -spec validate_server_deep_object_param( binary(), oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved()), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_server_deep_object_param(Path, Param, Ctx) -> case {erlang:element(3, Param), erlang:element(7, Param), resolve_schema_object(oaspec@openapi@spec:parameter_schema(Param), Ctx)} of {in_query, {some, deep_object_style}, {some, {object_schema, _, Properties, _, _, _, _}}} -> _pipe = maps:to_list(Properties), gleam@list:flat_map( _pipe, fun(Entry) -> {Prop_name, Prop_ref} = Entry, case deep_object_server_leaf_supported(Prop_ref, Ctx) of true -> []; false -> [oaspec@openapi@diagnostic:validation( <<<>/binary, Prop_name/binary>>, <<"deepObject properties are only supported for inline primitive scalars and inline primitive array leaves in server code generation."/utf8>>, severity_error, target_server, {some, <<"Simplify deepObject properties to primitive scalars or primitive arrays."/utf8>>} )] end end ); {_, _, _} -> [] end. -file("src/oaspec/codegen/validate.gleam", 366). -spec validate_server_structured_param( binary(), oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved()), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_server_structured_param(Path, Param, Ctx) -> case oaspec@config:mode(oaspec@codegen@context:config(Ctx)) of client -> []; _ -> Schema_obj = resolve_schema_object( oaspec@openapi@spec:parameter_schema(Param), Ctx ), Array_errors = case {erlang:element(3, Param), Schema_obj} of {in_query, {some, {array_schema, _, {inline, {string_schema, _, _, _, _, _, _}}, _, _, _}}} -> []; {in_query, {some, {array_schema, _, {inline, {integer_schema, _, _, _, _, _, _, _}}, _, _, _}}} -> []; {in_query, {some, {array_schema, _, {inline, {number_schema, _, _, _, _, _, _, _}}, _, _, _}}} -> []; {in_query, {some, {array_schema, _, {inline, {boolean_schema, _}}, _, _, _}}} -> []; {in_query, {some, {array_schema, _, _, _, _, _}}} -> [oaspec@openapi@diagnostic:validation( Path, <<"Query array parameters are only supported for inline primitive items in server code generation."/utf8>>, severity_error, target_server, {some, <<"Use inline primitive items (string, integer, number, boolean) for array query parameters."/utf8>>} )]; {in_header, {some, {array_schema, _, {inline, {string_schema, _, _, _, _, _, _}}, _, _, _}}} -> []; {in_header, {some, {array_schema, _, {inline, {integer_schema, _, _, _, _, _, _, _}}, _, _, _}}} -> []; {in_header, {some, {array_schema, _, {inline, {number_schema, _, _, _, _, _, _, _}}, _, _, _}}} -> []; {in_header, {some, {array_schema, _, {inline, {boolean_schema, _}}, _, _, _}}} -> []; {in_header, {some, {array_schema, _, _, _, _, _}}} -> [oaspec@openapi@diagnostic:validation( Path, <<"Header array parameters are only supported for inline primitive items in server code generation."/utf8>>, severity_error, target_server, {some, <<"Use inline primitive items (string, integer, number, boolean) for array header parameters."/utf8>>} )]; {_, _} -> [] end, Deep_object_errors = validate_server_deep_object_param( Path, Param, Ctx ), lists:append([Array_errors, Deep_object_errors]) end. -file("src/oaspec/codegen/validate.gleam", 543). ?DOC(" Validate that a deepObject parameter has no nested object properties.\n"). -spec validate_deep_object_no_nested_objects( binary(), oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved()), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_deep_object_no_nested_objects(Path, Param, Ctx) -> case resolve_schema_object(oaspec@openapi@spec:parameter_schema(Param), Ctx) of {some, {object_schema, _, Properties, _, _, _, _}} -> _pipe = maps:to_list(Properties), gleam@list:flat_map( _pipe, fun(Entry) -> {Prop_name, Prop_ref} = Entry, case resolve_schema_object({some, Prop_ref}, Ctx) of {some, {object_schema, _, _, _, _, _, _}} -> [oaspec@openapi@diagnostic:validation( <<<>/binary, Prop_name/binary>>, <<"Nested object properties in deepObject parameters are not supported. Only one level of object nesting is supported (e.g., filter[name]=value)."/utf8>>, severity_error, target_both, {some, <<"Flatten the property structure to a single level of nesting."/utf8>>} )]; {some, {all_of_schema, _, _}} -> [oaspec@openapi@diagnostic:validation( <<<>/binary, Prop_name/binary>>, <<"Nested object properties in deepObject parameters are not supported. Only one level of object nesting is supported (e.g., filter[name]=value)."/utf8>>, severity_error, target_both, {some, <<"Flatten the property structure to a single level of nesting."/utf8>>} )]; {some, {one_of_schema, _, _, _}} -> [oaspec@openapi@diagnostic:validation( <<<>/binary, Prop_name/binary>>, <<"Nested object properties in deepObject parameters are not supported. Only one level of object nesting is supported (e.g., filter[name]=value)."/utf8>>, severity_error, target_both, {some, <<"Flatten the property structure to a single level of nesting."/utf8>>} )]; {some, {any_of_schema, _, _, _}} -> [oaspec@openapi@diagnostic:validation( <<<>/binary, Prop_name/binary>>, <<"Nested object properties in deepObject parameters are not supported. Only one level of object nesting is supported (e.g., filter[name]=value)."/utf8>>, severity_error, target_both, {some, <<"Flatten the property structure to a single level of nesting."/utf8>>} )]; _ -> [] end end ); _ -> [] end. -file("src/oaspec/codegen/validate.gleam", 492). ?DOC( " Check if a parameter has a complex schema (object, oneOf, allOf, anyOf)\n" " that is not handled by deepObject style.\n" ). -spec validate_complex_param_schema( binary(), oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved()), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_complex_param_schema(Path, Param, Ctx) -> case erlang:element(7, Param) of {some, deep_object_style} -> validate_deep_object_no_nested_objects(Path, Param, Ctx); _ -> case resolve_schema_object( oaspec@openapi@spec:parameter_schema(Param), Ctx ) of {some, {object_schema, _, _, _, _, _, _}} -> case erlang:element(3, Param) of in_path -> case oaspec@config:mode( oaspec@codegen@context:config(Ctx) ) of client -> []; _ -> [oaspec@openapi@diagnostic:validation( Path, <<"Complex path parameters are not supported for server code generation."/utf8>>, severity_error, target_server, {some, <<"Use a simple scalar type (string, integer, number, boolean) for path parameters."/utf8>>} )] end; _ -> [oaspec@openapi@diagnostic:validation( Path, <<"Complex schema (object/oneOf/allOf/anyOf) parameters require style: deepObject. Without it, the parameter cannot be serialized."/utf8>>, severity_error, target_both, {some, <<"Add 'style: deepObject' to the parameter definition."/utf8>>} )] end; {some, {all_of_schema, _, _}} -> case erlang:element(3, Param) of in_path -> case oaspec@config:mode( oaspec@codegen@context:config(Ctx) ) of client -> []; _ -> [oaspec@openapi@diagnostic:validation( Path, <<"Complex path parameters are not supported for server code generation."/utf8>>, severity_error, target_server, {some, <<"Use a simple scalar type (string, integer, number, boolean) for path parameters."/utf8>>} )] end; _ -> [oaspec@openapi@diagnostic:validation( Path, <<"Complex schema (object/oneOf/allOf/anyOf) parameters require style: deepObject. Without it, the parameter cannot be serialized."/utf8>>, severity_error, target_both, {some, <<"Add 'style: deepObject' to the parameter definition."/utf8>>} )] end; {some, {one_of_schema, _, _, _}} -> case erlang:element(3, Param) of in_path -> case oaspec@config:mode( oaspec@codegen@context:config(Ctx) ) of client -> []; _ -> [oaspec@openapi@diagnostic:validation( Path, <<"Complex path parameters are not supported for server code generation."/utf8>>, severity_error, target_server, {some, <<"Use a simple scalar type (string, integer, number, boolean) for path parameters."/utf8>>} )] end; _ -> [oaspec@openapi@diagnostic:validation( Path, <<"Complex schema (object/oneOf/allOf/anyOf) parameters require style: deepObject. Without it, the parameter cannot be serialized."/utf8>>, severity_error, target_both, {some, <<"Add 'style: deepObject' to the parameter definition."/utf8>>} )] end; {some, {any_of_schema, _, _, _}} -> case erlang:element(3, Param) of in_path -> case oaspec@config:mode( oaspec@codegen@context:config(Ctx) ) of client -> []; _ -> [oaspec@openapi@diagnostic:validation( Path, <<"Complex path parameters are not supported for server code generation."/utf8>>, severity_error, target_server, {some, <<"Use a simple scalar type (string, integer, number, boolean) for path parameters."/utf8>>} )] end; _ -> [oaspec@openapi@diagnostic:validation( Path, <<"Complex schema (object/oneOf/allOf/anyOf) parameters require style: deepObject. Without it, the parameter cannot be serialized."/utf8>>, severity_error, target_both, {some, <<"Add 'style: deepObject' to the parameter definition."/utf8>>} )] end; _ -> [] end end. -file("src/oaspec/codegen/validate.gleam", 263). ?DOC( " Validate parameters for unsupported serialization styles.\n" " Supported: form (default), deepObject (query+object), exploded array,\n" " pipeDelimited / spaceDelimited (query+array only).\n" " Unsupported: matrix, label.\n" ). -spec validate_parameters( binary(), list(oaspec@openapi@spec:parameter(oaspec@openapi@spec:resolved())), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_parameters(Op_id, Params, Ctx) -> gleam@list:flat_map( Params, fun(P) -> Path = <<<>/binary, (erlang:element(2, P))/binary>>, Style_errors = case erlang:element(7, P) of {some, matrix_style} -> [oaspec@openapi@diagnostic:validation( Path, <<"Parameter style is not supported. Supported styles: form, simple, deepObject, pipeDelimited, spaceDelimited."/utf8>>, severity_error, target_both, {some, <<"Use style 'form', 'simple', 'deepObject', 'pipeDelimited', or 'spaceDelimited' instead."/utf8>>} )]; {some, label_style} -> [oaspec@openapi@diagnostic:validation( Path, <<"Parameter style is not supported. Supported styles: form, simple, deepObject, pipeDelimited, spaceDelimited."/utf8>>, severity_error, target_both, {some, <<"Use style 'form', 'simple', 'deepObject', 'pipeDelimited', or 'spaceDelimited' instead."/utf8>>} )]; {some, pipe_delimited_style} -> validate_delimited_style( Path, P, <<"pipeDelimited"/utf8>>, Ctx ); {some, space_delimited_style} -> validate_delimited_style( Path, P, <<"spaceDelimited"/utf8>>, Ctx ); _ -> [] end, Content_errors = case erlang:element(6, P) of {parameter_content, _} -> [oaspec@openapi@diagnostic:validation( Path, <<"Parameters using 'content' instead of 'schema' are not supported."/utf8>>, severity_error, target_both, {some, <<"Replace the 'content' field with a 'schema' field in the parameter definition."/utf8>>} )]; {parameter_schema, _} -> [] end, Complex_schema_errors = validate_complex_param_schema(Path, P, Ctx), Server_structured_param_errors = validate_server_structured_param( Path, P, Ctx ), Cookie_errors = validate_server_cookie_param(Path, P, Ctx), lists:append( [Style_errors, Content_errors, Complex_schema_errors, Server_structured_param_errors, Cookie_errors] ) end ). -file("src/oaspec/codegen/validate.gleam", 720). ?DOC( " Validate that application/x-www-form-urlencoded uses an object schema.\n" " Non-object schemas produce empty form bodies in the generated code.\n" ). -spec validate_form_urlencoded_schema( binary(), gleam@dict:dict(binary(), oaspec@openapi@spec:media_type()), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_form_urlencoded_schema(Op_id, Content, Ctx) -> case gleam_stdlib:map_get( Content, <<"application/x-www-form-urlencoded"/utf8>> ) of {ok, Media_type} -> case resolve_schema_object(erlang:element(2, Media_type), Ctx) of {some, {object_schema, _, _, _, _, _, _}} -> []; {some, _} -> [oaspec@openapi@diagnostic:validation( <>, <<"application/x-www-form-urlencoded request bodies must use an object schema."/utf8>>, severity_error, target_both, {some, <<"Wrap fields in an object schema with properties for each form field."/utf8>>} )]; none -> [] end; {error, _} -> [] end. -file("src/oaspec/codegen/validate.gleam", 811). -spec validate_server_request_body_content_types( binary(), list(binary()), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_server_request_body_content_types(Op_id, Content_keys, Ctx) -> case oaspec@config:mode(oaspec@codegen@context:config(Ctx)) of client -> []; _ -> Non_json_but_supported = gleam@list:filter( Content_keys, fun(Key) -> (((Key /= <<"application/json"/utf8>>) andalso (Key /= <<"application/x-www-form-urlencoded"/utf8>>)) andalso (Key /= <<"multipart/form-data"/utf8>>)) andalso oaspec@util@content_type:is_supported_request( oaspec@util@content_type:from_string(Key) ) end ), gleam@list:map( Non_json_but_supported, fun(Media_type) -> oaspec@openapi@diagnostic:validation( <>, <<<<"Content type '"/utf8, Media_type/binary>>/binary, "' is not supported for server code generation. Server router only supports application/json request bodies with typed decoding."/utf8>>, severity_error, target_server, {some, <<"Use application/json for typed server request bodies, or multipart/form-data and application/x-www-form-urlencoded for form data."/utf8>>} ) end ) end. -file("src/oaspec/codegen/validate.gleam", 922). -spec form_urlencoded_server_array_item_supported( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). form_urlencoded_server_array_item_supported(Schema_ref, Ctx) -> case resolve_schema_object({some, Schema_ref}, Ctx) of {some, {string_schema, _, _, _, _, _, _}} -> true; {some, {integer_schema, _, _, _, _, _, _, _}} -> true; {some, {number_schema, _, _, _, _, _, _, _}} -> true; {some, {boolean_schema, _}} -> true; _ -> false end. -file("src/oaspec/codegen/validate.gleam", 900). -spec form_urlencoded_server_field_supported( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context(), integer() ) -> boolean(). form_urlencoded_server_field_supported(Schema_ref, Ctx, Depth) -> case resolve_schema_object({some, Schema_ref}, Ctx) of {some, {string_schema, _, _, _, _, _, _}} -> true; {some, {integer_schema, _, _, _, _, _, _, _}} -> true; {some, {number_schema, _, _, _, _, _, _, _}} -> true; {some, {boolean_schema, _}} -> true; {some, {array_schema, _, Items, _, _, _}} -> form_urlencoded_server_array_item_supported(Items, Ctx); {some, {object_schema, _, Properties, _, _, _, _}} when Depth < 5 -> _pipe = maps:to_list(Properties), gleam@list:all( _pipe, fun(Entry) -> {_, Child_schema} = Entry, form_urlencoded_server_field_supported( Child_schema, Ctx, Depth + 1 ) end ); _ -> false end. -file("src/oaspec/codegen/validate.gleam", 752). ?DOC( " Validate that request body content types are supported for server codegen.\n" " Server router only handles application/json with typed decode; other types\n" " that pass the general is_supported_request check (multipart/form-data,\n" " application/x-www-form-urlencoded) are passed as raw String which breaks\n" " the typed body contract.\n" ). -spec validate_server_form_urlencoded_request_body( binary(), gleam@dict:dict(binary(), oaspec@openapi@spec:media_type()), list(binary()), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_server_form_urlencoded_request_body(Op_id, Content, Content_keys, Ctx) -> case oaspec@config:mode(oaspec@codegen@context:config(Ctx)) of client -> []; _ -> case gleam_stdlib:map_get( Content, <<"application/x-www-form-urlencoded"/utf8>> ) of {ok, Media_type} -> Content_type_errors = case erlang:length(Content_keys) > 1 of true -> [oaspec@openapi@diagnostic:validation( <>, <<"application/x-www-form-urlencoded request bodies are only supported as the sole request content type for server code generation."/utf8>>, severity_error, target_server, {some, <<"Remove other content type definitions from this operation's request body."/utf8>>} )]; false -> [] end, Field_errors = case resolve_schema_object( erlang:element(2, Media_type), Ctx ) of {some, {object_schema, _, Properties, _, _, _, _}} -> _pipe = maps:to_list(Properties), gleam@list:flat_map( _pipe, fun(Entry) -> {Field_name, Field_schema} = Entry, case form_urlencoded_server_field_supported( Field_schema, Ctx, 0 ) of true -> []; false -> [oaspec@openapi@diagnostic:validation( <<<>/binary, Field_name/binary>>, <<"application/x-www-form-urlencoded server request bodies only support primitive scalars, primitive arrays, and nested objects with primitive leaves (max 5 levels)."/utf8>>, severity_error, target_server, {some, <<"Simplify to primitive scalars, primitive arrays, or shallow nested objects."/utf8>>} )] end end ); _ -> [] end, lists:append(Content_type_errors, Field_errors); {error, _} -> [] end end. -file("src/oaspec/codegen/validate.gleam", 947). -spec multipart_server_array_item_supported( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). multipart_server_array_item_supported(Schema_ref, Ctx) -> case resolve_schema_object({some, Schema_ref}, Ctx) of {some, {string_schema, _, _, _, _, _, _}} -> true; {some, {integer_schema, _, _, _, _, _, _, _}} -> true; {some, {number_schema, _, _, _, _, _, _, _}} -> true; {some, {boolean_schema, _}} -> true; _ -> false end. -file("src/oaspec/codegen/validate.gleam", 935). -spec multipart_server_field_supported( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). multipart_server_field_supported(Schema_ref, Ctx) -> case resolve_schema_object({some, Schema_ref}, Ctx) of {some, {string_schema, _, _, _, _, _, _}} -> true; {some, {integer_schema, _, _, _, _, _, _, _}} -> true; {some, {number_schema, _, _, _, _, _, _, _}} -> true; {some, {boolean_schema, _}} -> true; {some, {array_schema, _, Items, _, _, _}} -> multipart_server_array_item_supported(Items, Ctx); _ -> false end. -file("src/oaspec/codegen/validate.gleam", 843). -spec validate_server_multipart_request_body( binary(), gleam@dict:dict(binary(), oaspec@openapi@spec:media_type()), list(binary()), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_server_multipart_request_body(Op_id, Content, Content_keys, Ctx) -> case oaspec@config:mode(oaspec@codegen@context:config(Ctx)) of client -> []; _ -> case gleam_stdlib:map_get(Content, <<"multipart/form-data"/utf8>>) of {ok, Media_type} -> Content_type_errors = case erlang:length(Content_keys) > 1 of true -> [oaspec@openapi@diagnostic:validation( <>, <<"multipart/form-data request bodies are only supported as the sole request content type for server code generation."/utf8>>, severity_error, target_server, {some, <<"Remove other content type definitions from this operation's request body."/utf8>>} )]; false -> [] end, Field_errors = case resolve_schema_object( erlang:element(2, Media_type), Ctx ) of {some, {object_schema, _, Properties, _, _, _, _}} -> _pipe = maps:to_list(Properties), gleam@list:flat_map( _pipe, fun(Entry) -> {Field_name, Field_schema} = Entry, case multipart_server_field_supported( Field_schema, Ctx ) of true -> []; false -> [oaspec@openapi@diagnostic:validation( <<<>/binary, Field_name/binary>>, <<"multipart/form-data server request bodies only support primitive scalar fields."/utf8>>, severity_error, target_server, {some, <<"Use primitive scalar types or arrays of primitive scalars (string, integer, number, boolean) for multipart form fields."/utf8>>} )] end end ); _ -> [] end, lists:append(Content_type_errors, Field_errors); {error, _} -> [] end end. -file("src/oaspec/codegen/validate.gleam", 960). -spec multipart_field_is_stringifiable( oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> boolean(). multipart_field_is_stringifiable(Schema_ref, Ctx) -> case resolve_schema_object({some, Schema_ref}, Ctx) of {some, {string_schema, _, _, _, _, _, _}} -> true; {some, {integer_schema, _, _, _, _, _, _, _}} -> true; {some, {number_schema, _, _, _, _, _, _, _}} -> true; {some, {boolean_schema, _}} -> true; _ -> false end. -file("src/oaspec/codegen/validate.gleam", 673). -spec validate_multipart_request_body_fields( binary(), gleam@dict:dict(binary(), oaspec@openapi@spec:media_type()), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_multipart_request_body_fields(Op_id, Content, Ctx) -> case gleam_stdlib:map_get(Content, <<"multipart/form-data"/utf8>>) of {ok, Media_type} -> case resolve_schema_object(erlang:element(2, Media_type), Ctx) of {some, {object_schema, _, Properties, _, _, _, _}} -> _pipe = maps:to_list(Properties), gleam@list:flat_map( _pipe, fun(Entry) -> {Field_name, Field_schema} = Entry, case multipart_field_is_stringifiable( Field_schema, Ctx ) of true -> []; false -> [oaspec@openapi@diagnostic:validation( <<<>/binary, Field_name/binary>>, <<"multipart/form-data fields must be string, integer, number, boolean, binary, or string enums."/utf8>>, severity_error, target_both, {some, <<"Use a primitive scalar type, binary, or string enum for multipart fields."/utf8>>} )] end end ); {some, _} -> [oaspec@openapi@diagnostic:validation( <>, <<"multipart/form-data request bodies must use an object schema."/utf8>>, severity_error, target_both, {some, <<"Wrap fields in an object schema with properties for each form field."/utf8>>} )]; none -> [] end; {error, _} -> [] end. -file("src/oaspec/codegen/validate.gleam", 1034). ?DOC( " Recursively validate a SchemaRef at any depth.\n" " Does this `$ref` value look like an absolute URL (http/https)? These\n" " are the shape that OpenAPI 3.1 `$id`-backed same-document refs take,\n" " and we surface them as a dedicated diagnostic separate from generic\n" " external `$ref` errors.\n" ). -spec is_url_style_ref(binary()) -> boolean(). is_url_style_ref(Ref) -> gleam_stdlib:string_starts_with(Ref, <<"http://"/utf8>>) orelse gleam_stdlib:string_starts_with( Ref, <<"https://"/utf8>> ). -file("src/oaspec/codegen/validate.gleam", 1158). ?DOC( " Validate that all security scheme references in global and operation-level\n" " security requirements point to schemes defined in components.securitySchemes.\n" ). -spec validate_security_schemes( oaspec@codegen@context:context(), list({binary(), oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()), binary(), oaspec@openapi@spec:http_method()}) ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_security_schemes(Ctx, Operations) -> Scheme_names = case erlang:element(5, oaspec@codegen@context:spec(Ctx)) of {some, Components} -> maps:keys(erlang:element(6, Components)); none -> [] end, Global_errors = gleam@list:flat_map( erlang:element(7, oaspec@codegen@context:spec(Ctx)), fun(Req) -> gleam@list:filter_map( erlang:element(2, Req), fun(Scheme_ref) -> case gleam@list:contains( Scheme_names, erlang:element(2, Scheme_ref) ) of true -> {error, nil}; false -> {ok, oaspec@openapi@diagnostic:validation( <<"security."/utf8, (erlang:element(2, Scheme_ref))/binary>>, <<<<"Security requirement references scheme '"/utf8, (erlang:element(2, Scheme_ref))/binary>>/binary, "' which is not defined in components.securitySchemes."/utf8>>, severity_error, target_both, {some, <<"Add the security scheme definition to components.securitySchemes or fix the scheme name."/utf8>>} )} end end ) end ), Operation_errors = gleam@list:flat_map( Operations, fun(Op) -> {Op_id, Operation, _, _} = Op, case erlang:element(10, Operation) of {some, Reqs} -> gleam@list:flat_map( Reqs, fun(Req@1) -> gleam@list:filter_map( erlang:element(2, Req@1), fun(Scheme_ref@1) -> case gleam@list:contains( Scheme_names, erlang:element(2, Scheme_ref@1) ) of true -> {error, nil}; false -> {ok, oaspec@openapi@diagnostic:validation( <<<>/binary, (erlang:element( 2, Scheme_ref@1 ))/binary>>, <<<<"Security requirement references scheme '"/utf8, (erlang:element( 2, Scheme_ref@1 ))/binary>>/binary, "' which is not defined in components.securitySchemes."/utf8>>, severity_error, target_both, {some, <<"Add the security scheme definition to components.securitySchemes or fix the scheme name."/utf8>>} )} end end ) end ); none -> [] end end ), lists:append(Global_errors, Operation_errors). -file("src/oaspec/codegen/validate.gleam", 1100). ?DOC(" Recursively validate a SchemaObject, descending into all sub-schemas.\n"). -spec validate_schema_recursive( binary(), oaspec@openapi@schema:schema_object(), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_schema_recursive(Path, Schema_obj, Ctx) -> case Schema_obj of {object_schema, _, Properties, _, Additional_properties, _, _} -> Ap_errors = [], Typed_ap_errors = case Additional_properties of {typed, Ap_ref} -> validate_schema_ref_recursive( <>, Ap_ref, Ctx ); forbidden -> []; untyped -> []; unspecified -> [] end, Prop_errors = begin _pipe = maps:to_list(Properties), gleam@list:flat_map( _pipe, fun(Entry) -> {Prop_name, Prop_ref} = Entry, Prop_path = <<<>/binary, Prop_name/binary>>, validate_schema_ref_recursive(Prop_path, Prop_ref, Ctx) end ) end, lists:append([Ap_errors, Typed_ap_errors, Prop_errors]); {array_schema, _, Items, _, _, _} -> validate_schema_ref_recursive( <>, Items, Ctx ); {one_of_schema, _, Schemas, _} -> gleam@list:flat_map( Schemas, fun(S_ref) -> validate_schema_ref_recursive( <>, S_ref, Ctx ) end ); {any_of_schema, _, Schemas@1, _} -> gleam@list:flat_map( Schemas@1, fun(S_ref@1) -> validate_schema_ref_recursive( <>, S_ref@1, Ctx ) end ); {all_of_schema, _, Schemas@2} -> gleam@list:flat_map( Schemas@2, fun(S_ref@2) -> validate_schema_ref_recursive( <>, S_ref@2, Ctx ) end ); _ -> [] end. -file("src/oaspec/codegen/validate.gleam", 1039). ?DOC(" References are checked for resolvability against the spec's components.\n"). -spec validate_schema_ref_recursive( binary(), oaspec@openapi@schema:schema_ref(), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_schema_ref_recursive(Path, Schema_ref, Ctx) -> case Schema_ref of {reference, Ref, _} -> case gleam_stdlib:string_starts_with(Ref, <<"#/"/utf8>>) of false -> [case is_url_style_ref(Ref) of true -> oaspec@openapi@diagnostic:validation( Path, <<<<"URL-style $ref '"/utf8, Ref/binary>>/binary, "' is not supported. oaspec does not resolve OpenAPI 3.1 / JSON Schema `$id`-backed identifiers — those refs are an explicit boundary."/utf8>>, severity_error, target_both, {some, <<"Rewrite the schema to a local $ref (`#/components/schemas/...`) and drop the `$id` URL, or inline the schema at the use site."/utf8>>} ); false -> oaspec@openapi@diagnostic:validation( Path, <<<<"External $ref '"/utf8, Ref/binary>>/binary, "' is not supported. Only local references (#/components/...) are supported."/utf8>>, severity_error, target_both, {some, <<"Inline the external schema or copy it into #/components/schemas/ and use a local $ref."/utf8>>} ) end]; true -> case oaspec@openapi@resolver:resolve_schema_ref( Schema_ref, oaspec@codegen@context:spec(Ctx) ) of {ok, _} -> []; {error, _} -> [oaspec@openapi@diagnostic:validation( Path, <<<<"Unresolved schema reference: '"/utf8, Ref/binary>>/binary, "'. The referenced schema does not exist in components."/utf8>>, severity_error, target_both, {some, <<"Verify the schema is defined in components.schemas and the $ref path is spelled correctly."/utf8>>} )] end end; {inline, Schema_obj} -> validate_schema_recursive(Path, Schema_obj, Ctx) end. -file("src/oaspec/codegen/validate.gleam", 592). ?DOC(" Validate request body for unsupported patterns.\n"). -spec validate_request_body( binary(), gleam@option:option(oaspec@openapi@spec:request_body(oaspec@openapi@spec:resolved())), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_request_body(Op_id, Request_body, Ctx) -> case Request_body of none -> []; {some, Rb} -> Content_keys = maps:keys(erlang:element(3, Rb)), Unsupported = gleam@list:filter( Content_keys, fun(Key) -> not oaspec@util@content_type:is_supported_request( oaspec@util@content_type:from_string(Key) ) end ), Content_type_errors = case Unsupported of [] -> []; [Media_type | _] -> [oaspec@openapi@diagnostic:validation( <>, <<<<"Content type '"/utf8, Media_type/binary>>/binary, "' is not supported. Supported request content types: application/json (and +json suffix types), multipart/form-data, application/x-www-form-urlencoded."/utf8>>, severity_error, target_both, {some, <<"Use application/json (or a +json suffix type like application/problem+json), multipart/form-data, or application/x-www-form-urlencoded."/utf8>>} )] end, Schema_errors = begin _pipe = maps:to_list(erlang:element(3, Rb)), gleam@list:flat_map( _pipe, fun(Entry) -> {_, Media_type@1} = Entry, case erlang:element(2, Media_type@1) of {some, Schema_ref} -> validate_schema_ref_recursive( <>, Schema_ref, Ctx ); none -> [] end end ) end, Multipart_field_errors = validate_multipart_request_body_fields( Op_id, erlang:element(3, Rb), Ctx ), Form_urlencoded_errors = validate_form_urlencoded_schema( Op_id, erlang:element(3, Rb), Ctx ), Server_form_urlencoded_errors = validate_server_form_urlencoded_request_body( Op_id, erlang:element(3, Rb), Content_keys, Ctx ), Server_multipart_errors = validate_server_multipart_request_body( Op_id, erlang:element(3, Rb), Content_keys, Ctx ), Server_body_errors = validate_server_request_body_content_types( Op_id, Content_keys, Ctx ), lists:append( [Content_type_errors, Schema_errors, Multipart_field_errors, Form_urlencoded_errors, Server_form_urlencoded_errors, Server_multipart_errors, Server_body_errors] ) end. -file("src/oaspec/codegen/validate.gleam", 971). ?DOC(" Validate response schemas and content types.\n"). -spec validate_responses( binary(), gleam@dict:dict(oaspec@util@http:http_status_code(), oaspec@openapi@spec:response(oaspec@openapi@spec:resolved())), oaspec@codegen@context:context() ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_responses(Op_id, Responses, Ctx) -> Entries = maps:to_list(Responses), gleam@list:flat_map( Entries, fun(Entry) -> {Status_code, Response} = Entry, Content_entries = maps:to_list(erlang:element(3, Response)), gleam@list:flat_map( Content_entries, fun(Ce) -> {Media_type_name, Media_type} = Ce, Path = <<<>/binary, (oaspec@util@http:status_code_to_string(Status_code))/binary>>, Content_type_errors = case oaspec@util@content_type:is_supported_response( oaspec@util@content_type:from_string(Media_type_name) ) of true -> []; false -> [oaspec@openapi@diagnostic:validation( Path, <<<<"Response content type '"/utf8, Media_type_name/binary>>/binary, "' is not supported. Supported response content types: application/json (and +json suffix types), text/plain, application/octet-stream, application/xml (and +xml suffix types), text/xml."/utf8>>, severity_error, target_both, {some, <<"Use application/json (or a +json suffix type), text/plain, application/octet-stream, application/xml (or a +xml suffix type), or text/xml."/utf8>>} )] end, Schema_errors = case erlang:element(2, Media_type) of {some, Schema_ref} -> validate_schema_ref_recursive(Path, Schema_ref, Ctx); none -> [] end, lists:append(Content_type_errors, Schema_errors) end ) end ). -file("src/oaspec/codegen/validate.gleam", 154). ?DOC(" Validate all operations for unsupported patterns.\n"). -spec validate_operations( oaspec@codegen@context:context(), list({binary(), oaspec@openapi@spec:operation(oaspec@openapi@spec:resolved()), binary(), oaspec@openapi@spec:http_method()}) ) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_operations(Ctx, Operations) -> gleam@list:flat_map( Operations, fun(Op) -> {Op_id, Operation, Path, _} = Op, Resolved_params = gleam@list:map( erlang:element(6, Operation), fun oaspec@openapi@spec:unwrap_ref/1 ), Resolved_request_body = case erlang:element(7, Operation) of {some, Ref_or} -> {some, oaspec@openapi@spec:unwrap_ref(Ref_or)}; none -> none end, Resolved_responses = begin _pipe = maps:to_list(erlang:element(8, Operation)), _pipe@1 = gleam@list:map( _pipe, fun(Entry) -> {Status_code, Ref_or@1} = Entry, {Status_code, oaspec@openapi@spec:unwrap_ref(Ref_or@1)} end ), maps:from_list(_pipe@1) end, Path_errors = validate_path_template_params( Op_id, Path, Resolved_params ), Param_errors = validate_parameters(Op_id, Resolved_params, Ctx), Body_errors = validate_request_body( Op_id, Resolved_request_body, Ctx ), Response_errors = validate_responses(Op_id, Resolved_responses, Ctx), Missing_responses_errors = case gleam@dict:is_empty( Resolved_responses ) of true -> [oaspec@openapi@diagnostic:validation( Op_id, <<"Operation has no responses defined. OpenAPI 3.x requires at least one response."/utf8>>, severity_error, target_both, {some, <<"Add at least one response (e.g., '200': { description: ok }) to this operation."/utf8>>} )]; false -> [] end, lists:append( [Path_errors, Param_errors, Body_errors, Response_errors, Missing_responses_errors] ) end ). -file("src/oaspec/codegen/validate.gleam", 1014). ?DOC(" Validate component schemas recursively.\n"). -spec validate_component_schemas(oaspec@codegen@context:context()) -> list(oaspec@openapi@diagnostic:diagnostic()). validate_component_schemas(Ctx) -> Schemas = case erlang:element(5, oaspec@codegen@context:spec(Ctx)) of {some, Components} -> maps:to_list(erlang:element(2, Components)); none -> [] end, gleam@list:flat_map( Schemas, fun(Entry) -> {Name, Schema_ref} = Entry, validate_schema_ref_recursive( <<"components.schemas."/utf8, Name/binary>>, Schema_ref, Ctx ) end ). -file("src/oaspec/codegen/validate.gleam", 29). ?DOC( " Validate the parsed spec for unsupported patterns.\n" " Returns a list of errors; empty list means validation passed.\n" "\n" " operationId uniqueness is enforced here with a hard error (issue #237):\n" " silently renaming duplicates would mutate the generated public API\n" " surface without telling the user, which is worse than failing the spec.\n" ). -spec validate(oaspec@codegen@context:context()) -> list(oaspec@openapi@diagnostic:diagnostic()). validate(Ctx) -> Operations = oaspec@openapi@operations:collect_operations(Ctx), Op_errors = validate_operations(Ctx, Operations), Opid_errors = validate_unique_operation_ids(Operations), Schema_errors = validate_component_schemas(Ctx), Security_errors = validate_security_schemes(Ctx, Operations), lists:append([Op_errors, Opid_errors, Schema_errors, Security_errors]).