Packages
oaspec
0.24.0
0.68.0
0.67.0
0.66.0
0.65.0
0.64.0
0.63.0
0.62.0
0.61.0
0.60.0
0.59.0
0.58.1
0.58.0
0.57.0
0.56.0
0.55.0
0.54.0
0.53.0
0.52.0
0.51.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.3
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.1.3
Generate Gleam code from OpenAPI 3.x specifications
Current section
Files
Jump to
Current section
Files
src/oaspec@codegen@validate.erl
-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", 55).
?DOC(
" Detect `Foo` + `FooList` schema name pairs that would generate two\n"
" `decode_foo_list` functions in `decode.gleam` and trip the gleam\n"
" compiler's `Duplicate definition` check. The synthetic list decoder\n"
" (`decode_<schema>_list` returning `List(<Schema>)`) is emitted for\n"
" every component schema, so any user-named `<Schema>List` schema\n"
" collides on the same identifier. Detected at validation time so the\n"
" user gets a one-line spec-level diagnostic instead of a confusing\n"
" post-codegen build failure. (#267)\n"
).
-spec validate_decode_list_collisions(oaspec@codegen@context:context()) -> list(oaspec@openapi@diagnostic:diagnostic()).
validate_decode_list_collisions(Ctx) ->
Schema_names = case erlang:element(5, oaspec@codegen@context:spec(Ctx)) of
{some, Components} ->
_pipe = maps:to_list(erlang:element(2, Components)),
gleam@list:map(_pipe, fun(Entry) -> erlang:element(1, Entry) end);
none ->
[]
end,
gleam@list:filter_map(
Schema_names,
fun(Base_name) ->
Collider_name = <<Base_name/binary, "List"/utf8>>,
case gleam@list:contains(Schema_names, Collider_name) of
false ->
{error, nil};
true ->
Snake = oaspec@util@naming:to_snake_case(Base_name),
{ok,
oaspec@openapi@diagnostic:validation(
<<"components.schemas."/utf8, Collider_name/binary>>,
<<<<<<<<<<<<<<<<<<<<"Schema name '"/utf8,
Collider_name/binary>>/binary,
"' would generate decode_"/utf8>>/binary,
Snake/binary>>/binary,
"_list, which collides with the synthetic list decoder for '"/utf8>>/binary,
Base_name/binary>>/binary,
"' (a JSON array of "/utf8>>/binary,
Base_name/binary>>/binary,
"). gleam build will fail with `Duplicate definition: decode_"/utf8>>/binary,
Snake/binary>>/binary,
"_list`."/utf8>>,
severity_error,
target_both,
{some,
<<<<<<<<<<<<<<<<"Rename one of the schemas. Example: rename '"/utf8,
Collider_name/binary>>/binary,
"' to '"/utf8>>/binary,
Base_name/binary>>/binary,
"Collection', '"/utf8>>/binary,
Base_name/binary>>/binary,
"Page', or '"/utf8>>/binary,
Base_name/binary>>/binary,
"Items'."/utf8>>}
)}
end
end
).
-file("src/oaspec/codegen/validate.gleam", 143).
-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", 159).
-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", 175).
-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", 103).
?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", 192).
?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", 197).
?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", 202).
?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", 210).
?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", 308).
?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 => <<?FILEPATH/utf8>>,
module => <<"oaspec/codegen/validate"/utf8>>,
function => <<"extract_path_template_names"/utf8>>,
line => 310,
value => _assert_fail,
start => 10881,
'end' => 10936,
pattern_start => 10892,
pattern_end => 10898})
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", 266).
?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(
<<Op_id/binary, ".path"/utf8>>,
<<<<<<<<"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", 543).
-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", 636).
-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", 384).
?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", 514).
-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", 478).
-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(
<<<<Path/binary, "."/utf8>>/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", 427).
-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", 604).
?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(
<<<<Path/binary, "."/utf8>>/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(
<<<<Path/binary, "."/utf8>>/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(
<<<<Path/binary, "."/utf8>>/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(
<<<<Path/binary, "."/utf8>>/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", 553).
?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", 324).
?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 = <<<<Op_id/binary, ".parameters."/utf8>>/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", 781).
?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(
<<Op_id/binary, ".requestBody"/utf8>>,
<<"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", 872).
-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 (Key /= <<"application/octet-stream"/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(
<<Op_id/binary, ".requestBody"/utf8>>,
<<<<"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, application/x-www-form-urlencoded, or application/octet-stream for non-JSON payloads."/utf8>>}
)
end
)
end.
-file("src/oaspec/codegen/validate.gleam", 984).
-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", 962).
-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", 813).
?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(
<<Op_id/binary, ".requestBody"/utf8>>,
<<"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(
<<<<Op_id/binary,
".requestBody.form."/utf8>>/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", 1009).
-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", 997).
-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", 905).
-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(
<<Op_id/binary, ".requestBody"/utf8>>,
<<"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(
<<<<Op_id/binary,
".requestBody.multipart."/utf8>>/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", 1022).
-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", 734).
-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(
<<<<Op_id/binary,
".requestBody.multipart."/utf8>>/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(
<<Op_id/binary, ".requestBody"/utf8>>,
<<"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", 1078).
?DOC(
" Validate component schemas recursively.\n"
" Detect schema names that differ only in case and would collide when\n"
" mapped to the same Gleam type name via `schema_to_type_name` (#293).\n"
).
-spec validate_unique_schema_names(oaspec@codegen@context:context()) -> list(oaspec@openapi@diagnostic:diagnostic()).
validate_unique_schema_names(Ctx) ->
Schemas = case erlang:element(5, oaspec@codegen@context:spec(Ctx)) of
{some, Components} ->
maps:keys(erlang:element(2, Components));
none ->
[]
end,
By_type_name = gleam@list:fold(
Schemas,
maps:new(),
fun(Acc, Name) ->
Key = oaspec@util@naming:schema_to_type_name(Name),
case gleam_stdlib:map_get(Acc, Key) of
{ok, Existing} ->
gleam@dict:insert(Acc, Key, [Name | Existing]);
{error, _} ->
gleam@dict:insert(Acc, Key, [Name])
end
end
),
_pipe = maps:to_list(By_type_name),
gleam@list:filter_map(
_pipe,
fun(Entry) ->
{Type_name, Names} = Entry,
case Names of
[_, _ | _] ->
{ok,
oaspec@openapi@diagnostic:invalid_value(
<<"components.schemas"/utf8>>,
<<<<<<<<"Schema names "/utf8,
(gleam@string:join(
gleam@list:map(
Names,
fun(N) ->
<<<<"\""/utf8,
N/binary>>/binary,
"\""/utf8>>
end
),
<<", "/utf8>>
))/binary>>/binary,
" all map to Gleam type `"/utf8>>/binary,
Type_name/binary>>/binary,
"` — rename one to avoid the collision"/utf8>>,
no_source_loc
)};
_ ->
{error, nil}
end
end
).
-file("src/oaspec/codegen/validate.gleam", 1132).
?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", 1256).
?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(
<<<<Op_id/binary,
".security."/utf8>>/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", 1198).
?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(
<<Path/binary, ".additionalProperties"/utf8>>,
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 = <<<<Path/binary, "."/utf8>>/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(
<<Path/binary, ".items"/utf8>>,
Items,
Ctx
);
{one_of_schema, _, Schemas, _} ->
gleam@list:flat_map(
Schemas,
fun(S_ref) ->
validate_schema_ref_recursive(
<<Path/binary, ".oneOf"/utf8>>,
S_ref,
Ctx
)
end
);
{any_of_schema, _, Schemas@1, _} ->
gleam@list:flat_map(
Schemas@1,
fun(S_ref@1) ->
validate_schema_ref_recursive(
<<Path/binary, ".anyOf"/utf8>>,
S_ref@1,
Ctx
)
end
);
{all_of_schema, _, Schemas@2} ->
gleam@list:flat_map(
Schemas@2,
fun(S_ref@2) ->
validate_schema_ref_recursive(
<<Path/binary, ".allOf"/utf8>>,
S_ref@2,
Ctx
)
end
);
_ ->
[]
end.
-file("src/oaspec/codegen/validate.gleam", 1137).
?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", 653).
?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(
<<Op_id/binary, ".requestBody"/utf8>>,
<<<<"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, application/octet-stream."/utf8>>,
severity_error,
target_both,
{some,
<<"Use application/json (or a +json suffix type like application/problem+json), multipart/form-data, application/x-www-form-urlencoded, or application/octet-stream."/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(
<<Op_id/binary, ".requestBody"/utf8>>,
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", 1033).
?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 = <<<<Op_id/binary, ".responses."/utf8>>/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/x-ndjson, 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/x-ndjson, 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", 215).
?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", 1112).
-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),
Schema_collision_errors = validate_unique_schema_names(Ctx),
Security_errors = validate_security_schemes(Ctx, Operations),
List_decoder_errors = validate_decode_list_collisions(Ctx),
lists:append(
[Op_errors,
Opid_errors,
Schema_errors,
Schema_collision_errors,
Security_errors,
List_decoder_errors]
).