Current section

Files

Jump to
oaspec src oaspec@codegen@validate.erl
Raw

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]).
-export_type([severity/0, target/0, validation_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type severity() :: severity_error | severity_warning.
-type target() :: target_both | target_client | target_server.
-type validation_error() :: {validation_error,
binary(),
binary(),
severity(),
target()}.
-file("src/oaspec/codegen/validate.gleam", 54).
?DOC(" Filter to only errors (not warnings).\n").
-spec errors_only(list(validation_error())) -> list(validation_error()).
errors_only(Issues) ->
gleam@list:filter(
Issues,
fun(E) -> erlang:element(4, E) =:= severity_error end
).
-file("src/oaspec/codegen/validate.gleam", 59).
?DOC(" Filter to only warnings (not errors).\n").
-spec warnings_only(list(validation_error())) -> list(validation_error()).
warnings_only(Issues) ->
gleam@list:filter(
Issues,
fun(E) -> erlang:element(4, E) =:= severity_warning end
).
-file("src/oaspec/codegen/validate.gleam", 64).
?DOC(" Filter validation issues to those relevant for the selected generation mode.\n").
-spec filter_by_mode(list(validation_error()), oaspec@config:generate_mode()) -> list(validation_error()).
filter_by_mode(Issues, Mode) ->
case Mode of
client ->
gleam@list:filter(
Issues,
fun(E) -> erlang:element(5, E) /= target_server end
);
server ->
gleam@list:filter(
Issues,
fun(E@1) -> erlang:element(5, E@1) /= target_client end
);
both ->
Issues
end.
-file("src/oaspec/codegen/validate.gleam", 76).
?DOC(" Convert a validation error to a human-readable string.\n").
-spec error_to_string(validation_error()) -> binary().
error_to_string(Error) ->
Prefix = case erlang:element(4, Error) of
severity_error ->
<<"Error"/utf8>>;
severity_warning ->
<<"Warning"/utf8>>
end,
<<<<<<<<Prefix/binary, " at "/utf8>>/binary,
(erlang:element(2, Error))/binary>>/binary,
": "/utf8>>/binary,
(erlang:element(3, Error))/binary>>.
-file("src/oaspec/codegen/validate.gleam", 150).
?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 => 151,
value => _assert_fail,
start => 4868,
'end' => 4923,
pattern_start => 4879,
pattern_end => 4885})
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", 118).
?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())
) -> list(validation_error()).
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 ->
{ok,
{validation_error,
<<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}}
end end
).
-file("src/oaspec/codegen/validate.gleam", 318).
-spec validate_server_cookie_param(
binary(),
oaspec@openapi@spec:parameter(),
oaspec@codegen@context:context()
) -> list(validation_error()).
validate_server_cookie_param(Path, Param, Ctx) ->
_ = Path,
_ = Param,
_ = Ctx,
[].
-file("src/oaspec/codegen/validate.gleam", 405).
-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,
erlang:element(2, Ctx)
) of
{ok, Schema_obj@1} ->
{some, Schema_obj@1};
{error, _} ->
none
end;
none ->
none
end.
-file("src/oaspec/codegen/validate.gleam", 289).
-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", 260).
-spec validate_server_deep_object_param(
binary(),
oaspec@openapi@spec:parameter(),
oaspec@codegen@context:context()
) -> list(validation_error()).
validate_server_deep_object_param(Path, Param, Ctx) ->
case {erlang:element(3, Param),
erlang:element(7, Param),
resolve_schema_object(erlang:element(6, 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 ->
[{validation_error,
<<<<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}]
end
end
);
{_, _, _} ->
[]
end.
-file("src/oaspec/codegen/validate.gleam", 215).
-spec validate_server_structured_param(
binary(),
oaspec@openapi@spec:parameter(),
oaspec@codegen@context:context()
) -> list(validation_error()).
validate_server_structured_param(Path, Param, Ctx) ->
case erlang:element(6, erlang:element(3, Ctx)) of
client ->
[];
_ ->
Schema_obj = resolve_schema_object(erlang:element(6, 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, _, _, _, _, _}}} ->
[{validation_error,
Path,
<<"Query array parameters are only supported for inline primitive items in server code generation."/utf8>>,
severity_error,
target_server}];
{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, _, _, _, _, _}}} ->
[{validation_error,
Path,
<<"Header array parameters are only supported for inline primitive items in server code generation."/utf8>>,
severity_error,
target_server}];
{_, _} ->
[]
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", 376).
?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@codegen@context:context()
) -> list(validation_error()).
validate_deep_object_no_nested_objects(Path, Param, Ctx) ->
case resolve_schema_object(erlang:element(6, 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, _, _, _, _, _, _, _}} ->
[{validation_error,
<<<<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, {all_of_schema, _, _}} ->
[{validation_error,
<<<<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, {one_of_schema, _, _, _}} ->
[{validation_error,
<<<<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, {any_of_schema, _, _, _}} ->
[{validation_error,
<<<<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}];
_ ->
[]
end
end
);
_ ->
[]
end.
-file("src/oaspec/codegen/validate.gleam", 331).
?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@codegen@context:context()
) -> list(validation_error()).
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(erlang:element(6, Param), Ctx) of
{some, {object_schema, _, _, _, _, _, _, _}} ->
case erlang:element(3, Param) of
in_path ->
case erlang:element(6, erlang:element(3, Ctx)) of
client ->
[];
_ ->
[{validation_error,
Path,
<<"Complex path parameters are not supported for server code generation."/utf8>>,
severity_error,
target_server}]
end;
_ ->
[{validation_error,
Path,
<<"Complex schema (object/oneOf/allOf/anyOf) parameters require style: deepObject. Without it, the parameter cannot be serialized."/utf8>>,
severity_error,
target_both}]
end;
{some, {all_of_schema, _, _}} ->
case erlang:element(3, Param) of
in_path ->
case erlang:element(6, erlang:element(3, Ctx)) of
client ->
[];
_ ->
[{validation_error,
Path,
<<"Complex path parameters are not supported for server code generation."/utf8>>,
severity_error,
target_server}]
end;
_ ->
[{validation_error,
Path,
<<"Complex schema (object/oneOf/allOf/anyOf) parameters require style: deepObject. Without it, the parameter cannot be serialized."/utf8>>,
severity_error,
target_both}]
end;
{some, {one_of_schema, _, _, _}} ->
case erlang:element(3, Param) of
in_path ->
case erlang:element(6, erlang:element(3, Ctx)) of
client ->
[];
_ ->
[{validation_error,
Path,
<<"Complex path parameters are not supported for server code generation."/utf8>>,
severity_error,
target_server}]
end;
_ ->
[{validation_error,
Path,
<<"Complex schema (object/oneOf/allOf/anyOf) parameters require style: deepObject. Without it, the parameter cannot be serialized."/utf8>>,
severity_error,
target_both}]
end;
{some, {any_of_schema, _, _, _}} ->
case erlang:element(3, Param) of
in_path ->
case erlang:element(6, erlang:element(3, Ctx)) of
client ->
[];
_ ->
[{validation_error,
Path,
<<"Complex path parameters are not supported for server code generation."/utf8>>,
severity_error,
target_server}]
end;
_ ->
[{validation_error,
Path,
<<"Complex schema (object/oneOf/allOf/anyOf) parameters require style: deepObject. Without it, the parameter cannot be serialized."/utf8>>,
severity_error,
target_both}]
end;
_ ->
[]
end
end.
-file("src/oaspec/codegen/validate.gleam", 164).
?DOC(
" Validate parameters for unsupported serialization styles.\n"
" Supported: form (default), deepObject (query+object), exploded array.\n"
" Unsupported: matrix, label, simple, spaceDelimited, pipeDelimited.\n"
).
-spec validate_parameters(
binary(),
list(oaspec@openapi@spec:parameter()),
oaspec@codegen@context:context()
) -> list(validation_error()).
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} ->
[{validation_error,
Path,
<<"Parameter style is not supported. Supported styles: form, deepObject, simple."/utf8>>,
severity_error,
target_both}];
{some, label_style} ->
[{validation_error,
Path,
<<"Parameter style is not supported. Supported styles: form, deepObject, simple."/utf8>>,
severity_error,
target_both}];
{some, space_delimited_style} ->
[{validation_error,
Path,
<<"Parameter style is not supported. Supported styles: form, deepObject, simple."/utf8>>,
severity_error,
target_both}];
{some, pipe_delimited_style} ->
[{validation_error,
Path,
<<"Parameter style is not supported. Supported styles: form, deepObject, simple."/utf8>>,
severity_error,
target_both}];
_ ->
[]
end,
Content_errors = case erlang:element(6, P) of
none ->
[{validation_error,
Path,
<<"Parameters using 'content' instead of 'schema' are not supported."/utf8>>,
severity_error,
target_both}];
_ ->
[]
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", 539).
?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(validation_error()).
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, _} ->
[{validation_error,
<<Op_id/binary, ".requestBody"/utf8>>,
<<"application/x-www-form-urlencoded request bodies must use an object schema."/utf8>>,
severity_error,
target_both}];
none ->
[]
end;
{error, _} ->
[]
end.
-file("src/oaspec/codegen/validate.gleam", 619).
-spec validate_server_request_body_content_types(
binary(),
list(binary()),
oaspec@codegen@context:context()
) -> list(validation_error()).
validate_server_request_body_content_types(Op_id, Content_keys, Ctx) ->
case erlang:element(6, erlang:element(3, 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) ->
{validation_error,
<<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}
end
)
end.
-file("src/oaspec/codegen/validate.gleam", 720).
-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", 698).
-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", 567).
?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(validation_error()).
validate_server_form_urlencoded_request_body(Op_id, Content, Content_keys, Ctx) ->
case erlang:element(6, erlang:element(3, 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 ->
[{validation_error,
<<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}];
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 ->
[{validation_error,
<<<<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}]
end
end
);
_ ->
[]
end,
lists:append(Content_type_errors, Field_errors);
{error, _} ->
[]
end
end.
-file("src/oaspec/codegen/validate.gleam", 745).
-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", 733).
-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", 648).
-spec validate_server_multipart_request_body(
binary(),
gleam@dict:dict(binary(), oaspec@openapi@spec:media_type()),
list(binary()),
oaspec@codegen@context:context()
) -> list(validation_error()).
validate_server_multipart_request_body(Op_id, Content, Content_keys, Ctx) ->
case erlang:element(6, erlang:element(3, 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 ->
[{validation_error,
<<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}];
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 ->
[{validation_error,
<<<<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}]
end
end
);
_ ->
[]
end,
lists:append(Content_type_errors, Field_errors);
{error, _} ->
[]
end
end.
-file("src/oaspec/codegen/validate.gleam", 758).
-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", 499).
-spec validate_multipart_request_body_fields(
binary(),
gleam@dict:dict(binary(), oaspec@openapi@spec:media_type()),
oaspec@codegen@context:context()
) -> list(validation_error()).
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 ->
[{validation_error,
<<<<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}]
end
end
);
{some, _} ->
[{validation_error,
<<Op_id/binary, ".requestBody"/utf8>>,
<<"multipart/form-data request bodies must use an object schema."/utf8>>,
severity_error,
target_both}];
none ->
[]
end;
{error, _} ->
[]
end.
-file("src/oaspec/codegen/validate.gleam", 928).
?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(validation_error()).
validate_security_schemes(Ctx) ->
Scheme_names = case erlang:element(5, erlang:element(2, Ctx)) of
{some, Components} ->
maps:keys(erlang:element(6, Components));
none ->
[]
end,
Global_errors = gleam@list:flat_map(
erlang:element(7, erlang:element(2, 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,
{validation_error,
<<"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}}
end
end
)
end
),
Operations = oaspec@codegen@types:collect_operations(Ctx),
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,
{validation_error,
<<<<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}}
end
end
)
end
);
none ->
[]
end
end
),
lists:append(Global_errors, Operation_errors).
-file("src/oaspec/codegen/validate.gleam", 984).
?DOC(
" Check for AST fields that are parsed but not used by codegen, emitting\n"
" warnings so users are aware their spec contains features we preserve but\n"
" do not generate code for.\n"
).
-spec validate_preserved_but_unused(oaspec@codegen@context:context()) -> list(validation_error()).
validate_preserved_but_unused(Ctx) ->
Webhook_warnings = case gleam@dict:is_empty(
erlang:element(8, erlang:element(2, Ctx))
) of
true ->
[];
false ->
[{validation_error,
<<"webhooks"/utf8>>,
<<"Webhooks are parsed but not used by code generation."/utf8>>,
severity_warning,
target_both}]
end,
Operations = oaspec@codegen@types:collect_operations(Ctx),
Response_warnings = gleam@list:flat_map(
Operations,
fun(Op) ->
{Op_id, Operation, _, _} = Op,
Entries = maps:to_list(erlang:element(8, Operation)),
gleam@list:flat_map(
Entries,
fun(Entry) ->
{Status_code, Response} = Entry,
Base_path = <<<<Op_id/binary, ".responses."/utf8>>/binary,
Status_code/binary>>,
Multi_content_warnings = case {erlang:element(
6,
erlang:element(3, Ctx)
),
erlang:length(maps:to_list(erlang:element(3, Response)))} of
{client, _} ->
[];
{_, N} when N > 1 ->
[{validation_error,
<<Base_path/binary, ".content"/utf8>>,
<<"Multiple response content types are not fully supported for server code generation. Generated server responses lose the content-type header."/utf8>>,
severity_warning,
target_server}];
{_, _} ->
[]
end,
Header_warnings = case gleam@dict:is_empty(
erlang:element(4, Response)
) of
true ->
[];
false ->
[{validation_error,
<<Base_path/binary, ".headers"/utf8>>,
<<"Response headers are parsed but not used by code generation."/utf8>>,
severity_warning,
target_both}]
end,
Link_warnings = case gleam@dict:is_empty(
erlang:element(5, Response)
) of
true ->
[];
false ->
[{validation_error,
<<Base_path/binary, ".links"/utf8>>,
<<"Response links are parsed but not used by code generation."/utf8>>,
severity_warning,
target_both}]
end,
Content_entries = maps:to_list(erlang:element(3, Response)),
Encoding_warnings = gleam@list:flat_map(
Content_entries,
fun(Ce) ->
{Media_type_name, Media_type} = Ce,
case gleam@dict:is_empty(
erlang:element(5, Media_type)
) of
true ->
[];
false ->
[{validation_error,
<<<<<<Base_path/binary, "."/utf8>>/binary,
Media_type_name/binary>>/binary,
".encoding"/utf8>>,
<<"MediaType encoding is parsed but not used by code generation."/utf8>>,
severity_warning,
target_both}]
end
end
),
lists:append(
[Multi_content_warnings,
Header_warnings,
Link_warnings,
Encoding_warnings]
)
end
)
end
),
External_docs_warnings = case erlang:element(10, erlang:element(2, Ctx)) of
{some, _} ->
[{validation_error,
<<"externalDocs"/utf8>>,
<<"External docs are parsed but not used by code generation."/utf8>>,
severity_warning,
target_both}];
none ->
[]
end,
Tag_warnings = case gleam@list:is_empty(
erlang:element(9, erlang:element(2, Ctx))
) of
true ->
[];
false ->
[{validation_error,
<<"tags"/utf8>>,
<<"Top-level tags are parsed but not used by code generation."/utf8>>,
severity_warning,
target_both}]
end,
Operation_server_warnings = gleam@list:flat_map(
Operations,
fun(Op@1) ->
{Op_id@1, Operation@1, _, _} = Op@1,
case gleam@list:is_empty(erlang:element(12, Operation@1)) of
true ->
[];
false ->
[{validation_error,
<<Op_id@1/binary, ".servers"/utf8>>,
<<"Operation-level servers are parsed but client code generation uses only the top-level server URL."/utf8>>,
severity_warning,
target_client}]
end
end
),
Path_server_warnings = begin
_pipe = maps:to_list(erlang:element(4, erlang:element(2, Ctx))),
gleam@list:flat_map(
_pipe,
fun(Entry@1) ->
{Path, Path_item} = Entry@1,
case gleam@list:is_empty(erlang:element(13, Path_item)) of
true ->
[];
false ->
[{validation_error,
<<<<"paths."/utf8, Path/binary>>/binary,
".servers"/utf8>>,
<<"Path-level servers are parsed but client code generation uses only the top-level server URL."/utf8>>,
severity_warning,
target_client}]
end
end
)
end,
Component_warnings = case erlang:element(5, erlang:element(2, Ctx)) of
{some, Components} ->
Header_w = case gleam@dict:is_empty(erlang:element(8, Components)) of
true ->
[];
false ->
[{validation_error,
<<"components.headers"/utf8>>,
<<"Component headers are parsed but not used by code generation."/utf8>>,
severity_warning,
target_both}]
end,
Example_w = case gleam@dict:is_empty(erlang:element(9, Components)) of
true ->
[];
false ->
[{validation_error,
<<"components.examples"/utf8>>,
<<"Component examples are parsed but not used by code generation."/utf8>>,
severity_warning,
target_both}]
end,
Link_w = case gleam@dict:is_empty(erlang:element(10, Components)) of
true ->
[];
false ->
[{validation_error,
<<"components.links"/utf8>>,
<<"Component links are parsed but not used by code generation."/utf8>>,
severity_warning,
target_both}]
end,
lists:append([Header_w, Example_w, Link_w]);
none ->
[]
end,
lists:append(
[Webhook_warnings,
Response_warnings,
External_docs_warnings,
Tag_warnings,
Operation_server_warnings,
Path_server_warnings,
Component_warnings]
).
-file("src/oaspec/codegen/validate.gleam", 864).
?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(validation_error()).
validate_schema_recursive(Path, Schema_obj, Ctx) ->
case Schema_obj of
{object_schema,
_,
Properties,
_,
Additional_properties,
Additional_properties_untyped,
_,
_} ->
Ap_errors = [],
_ = Additional_properties_untyped,
Typed_ap_errors = case Additional_properties of
{some, Ap_ref} ->
validate_schema_ref_recursive(
<<Path/binary, ".additionalProperties"/utf8>>,
Ap_ref,
Ctx
);
none ->
[]
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", 825).
?DOC(
" Recursively validate a SchemaRef at any depth.\n"
" 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(validation_error()).
validate_schema_ref_recursive(Path, Schema_ref, Ctx) ->
case Schema_ref of
{reference, Ref, _} ->
case gleam_stdlib:string_starts_with(Ref, <<"#/"/utf8>>) of
false ->
[{validation_error,
Path,
<<<<"External $ref '"/utf8, Ref/binary>>/binary,
"' is not supported. Only local references (#/components/...) are supported."/utf8>>,
severity_error,
target_both}];
true ->
case oaspec@openapi@resolver:resolve_schema_ref(
Schema_ref,
erlang:element(2, Ctx)
) of
{ok, _} ->
[];
{error, _} ->
[{validation_error,
Path,
<<<<"Unresolved schema reference: '"/utf8,
Ref/binary>>/binary,
"'. The referenced schema does not exist in components."/utf8>>,
severity_error,
target_both}]
end
end;
{inline, Schema_obj} ->
validate_schema_recursive(Path, Schema_obj, Ctx)
end.
-file("src/oaspec/codegen/validate.gleam", 421).
?DOC(" Validate request body for unsupported patterns.\n").
-spec validate_request_body(
binary(),
gleam@option:option(oaspec@openapi@spec:request_body()),
oaspec@codegen@context:context()
) -> list(validation_error()).
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 | _] ->
[{validation_error,
<<Op_id/binary, ".requestBody"/utf8>>,
<<<<"Content type '"/utf8, Media_type/binary>>/binary,
"' is not supported. Supported request content types: application/json, multipart/form-data, application/x-www-form-urlencoded."/utf8>>,
severity_error,
target_both}]
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", 769).
?DOC(" Validate response schemas and content types.\n").
-spec validate_responses(
binary(),
gleam@dict:dict(binary(), oaspec@openapi@spec:response()),
oaspec@codegen@context:context()
) -> list(validation_error()).
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,
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 ->
[{validation_error,
Path,
<<<<"Response content type '"/utf8,
Media_type_name/binary>>/binary,
"' is not supported. Supported response content types: application/json, text/plain, application/octet-stream, application/xml, text/xml."/utf8>>,
severity_error,
target_both}]
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", 85).
?DOC(" Validate all operations for unsupported patterns.\n").
-spec validate_operations(oaspec@codegen@context:context()) -> list(validation_error()).
validate_operations(Ctx) ->
Operations = oaspec@codegen@types:collect_operations(Ctx),
gleam@list:flat_map(
Operations,
fun(Op) ->
{Op_id, Operation, Path, _} = Op,
Path_errors = validate_path_template_params(
Op_id,
Path,
erlang:element(6, Operation)
),
Param_errors = validate_parameters(
Op_id,
erlang:element(6, Operation),
Ctx
),
Body_errors = validate_request_body(
Op_id,
erlang:element(7, Operation),
Ctx
),
Response_errors = validate_responses(
Op_id,
erlang:element(8, Operation),
Ctx
),
Missing_responses_errors = case gleam@dict:is_empty(
erlang:element(8, Operation)
) of
true ->
[{validation_error,
Op_id,
<<"Operation has no responses defined. OpenAPI 3.x requires at least one response."/utf8>>,
severity_error,
target_both}];
false ->
[]
end,
lists:append(
[Path_errors,
Param_errors,
Body_errors,
Response_errors,
Missing_responses_errors]
)
end
).
-file("src/oaspec/codegen/validate.gleam", 808).
?DOC(" Validate component schemas recursively.\n").
-spec validate_component_schemas(oaspec@codegen@context:context()) -> list(validation_error()).
validate_component_schemas(Ctx) ->
Schemas = case erlang:element(5, erlang:element(2, 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", 45).
?DOC(
" Validate the parsed spec for unsupported patterns.\n"
" Returns a list of errors; empty list means validation passed.\n"
" Name collisions and duplicate operationIds are handled by the dedup pass\n"
" before validation, so they are no longer checked here.\n"
).
-spec validate(oaspec@codegen@context:context()) -> list(validation_error()).
validate(Ctx) ->
Op_errors = validate_operations(Ctx),
Schema_errors = validate_component_schemas(Ctx),
Security_errors = validate_security_schemes(Ctx),
Preserved_warnings = validate_preserved_but_unused(Ctx),
lists:append(
[Op_errors, Schema_errors, Security_errors, Preserved_warnings]
).