Packages
oaspec
0.6.1
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]).
-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", 133).
?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 => 134,
value => _assert_fail,
start => 4455,
'end' => 4510,
pattern_start => 4466,
pattern_end => 4472})
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", 101).
?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", 301).
-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", 388).
-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", 272).
-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", 243).
-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", 198).
-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", 359).
?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", 314).
?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", 147).
?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", 522).
?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", 602).
-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", 703).
-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", 681).
-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", 550).
?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", 728).
-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", 716).
-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", 631).
-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", 741).
-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", 482).
-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", 912).
?DOC(
" Validate security schemes for unsupported types.\n"
" All scheme types are now supported: apiKey, HTTP (any scheme), OAuth2,\n"
" and OpenID Connect.\n"
).
-spec validate_security_schemes(oaspec@codegen@context:context()) -> list(validation_error()).
validate_security_schemes(_) ->
[].
-file("src/oaspec/codegen/validate.gleam", 919).
?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
),
lists:append([Webhook_warnings, Response_warnings]).
-file("src/oaspec/codegen/validate.gleam", 847).
?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", 808).
?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", 404).
?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", 752).
?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
),
lists:append(
[Path_errors, Param_errors, Body_errors, Response_errors]
)
end
).
-file("src/oaspec/codegen/validate.gleam", 791).
?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]
).