Packages
oaspec
0.4.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([error_to_string/1, validate/1]).
-export_type([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 validation_error() :: {unsupported_feature, binary(), binary()}.
-file("src/oaspec/codegen/validate.gleam", 33).
?DOC(" Convert a validation error to a human-readable string.\n").
-spec error_to_string(validation_error()) -> binary().
error_to_string(Error) ->
case Error of
{unsupported_feature, Path, Detail} ->
<<<<<<"Unsupported feature at "/utf8, Path/binary>>/binary,
": "/utf8>>/binary,
Detail/binary>>
end.
-file("src/oaspec/codegen/validate.gleam", 110).
-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", 53).
?DOC(" Validate parameters for unsupported patterns.\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(Param) ->
Path = <<<<Op_id/binary, ".parameters."/utf8>>/binary,
(erlang:element(2, Param))/binary>>,
Resolved_schema = resolve_schema_object(
erlang:element(6, Param),
Ctx
),
Deep_object_errors = case erlang:element(7, Param) of
{some, <<"deepObject"/utf8>>} ->
[{unsupported_feature,
Path,
<<"Parameter style 'deepObject' is not supported."/utf8>>}];
_ ->
[]
end,
Complex_schema_errors = case Resolved_schema of
{some, {object_schema, _, _, _, _, _, _}} ->
[{unsupported_feature,
Path,
<<"Complex schema parameters (object/allOf/oneOf/anyOf) are not supported."/utf8>>}];
{some, {all_of_schema, _, _}} ->
[{unsupported_feature,
Path,
<<"Complex schema parameters (object/allOf/oneOf/anyOf) are not supported."/utf8>>}];
{some, {one_of_schema, _, _, _}} ->
[{unsupported_feature,
Path,
<<"Complex schema parameters (object/allOf/oneOf/anyOf) are not supported."/utf8>>}];
{some, {any_of_schema, _, _}} ->
[{unsupported_feature,
Path,
<<"Complex schema parameters (object/allOf/oneOf/anyOf) are not supported."/utf8>>}];
_ ->
[]
end,
Array_errors = case {erlang:element(3, Param), Resolved_schema} of
{in_path, _} ->
[];
{_, {some, {array_schema, _, _, _, _, _}}} ->
[{unsupported_feature,
Path,
<<"Array parameters in query/header/cookie are not supported."/utf8>>}];
{_, _} ->
[]
end,
Required_errors = case {erlang:element(3, Param),
erlang:element(5, Param)} of
{in_path, false} ->
[{unsupported_feature,
Path,
<<"Path parameters with required: false are not supported."/utf8>>}];
{_, _} ->
[]
end,
lists:append(
[Deep_object_errors,
Complex_schema_errors,
Array_errors,
Required_errors]
)
end
).
-file("src/oaspec/codegen/validate.gleam", 206).
-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", 172).
-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 ->
[{unsupported_feature,
<<<<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>>}]
end
end
);
{some, _} ->
[{unsupported_feature,
<<Op_id/binary, ".requestBody"/utf8>>,
<<"multipart/form-data request bodies must use an object schema."/utf8>>}];
none ->
[]
end;
{error, _} ->
[]
end.
-file("src/oaspec/codegen/validate.gleam", 403).
?DOC(" Check if any schema in a list is inline (not a $ref).\n").
-spec has_any_inline_schemas(list(oaspec@openapi@schema:schema_ref())) -> boolean().
has_any_inline_schemas(Schemas) ->
gleam@list:any(Schemas, fun(S) -> case S of
{inline, _} ->
true;
{reference, _} ->
false
end end).
-file("src/oaspec/codegen/validate.gleam", 465).
?DOC(
" Find duplicates in a list using a key function, producing errors via an\n"
" error function. Only reports the first occurrence of each duplicate.\n"
).
-spec find_duplicates(
list(IBQ),
fun((IBQ) -> binary()),
fun((binary()) -> validation_error())
) -> list(validation_error()).
find_duplicates(Items, Key_fn, Error_fn) ->
{_, Errors} = gleam@list:fold(
Items,
{gleam@set:new(), []},
fun(Acc, Item) ->
{Seen, Errs} = Acc,
Key = Key_fn(Item),
case gleam@set:contains(Seen, Key) of
true ->
{Seen, [Error_fn(Key) | Errs]};
false ->
{gleam@set:insert(Seen, Key), Errs}
end
end
),
lists:reverse(Errors).
-file("src/oaspec/codegen/validate.gleam", 483).
?DOC(" Find duplicate names in a simple list of strings.\n").
-spec find_name_collisions(
list(binary()),
fun((binary()) -> validation_error())
) -> list(validation_error()).
find_name_collisions(Names, Error_fn) ->
find_duplicates(Names, fun(Name) -> Name end, Error_fn).
-file("src/oaspec/codegen/validate.gleam", 413).
?DOC(" Validate for operationId duplicates and naming collisions.\n").
-spec validate_name_collisions(oaspec@codegen@context:context()) -> list(validation_error()).
validate_name_collisions(Ctx) ->
Operations = oaspec@codegen@types:collect_operations(Ctx),
Op_id_errors = find_duplicates(
Operations,
fun(Op) ->
{Op_id, _, _, _} = Op,
Op_id
end,
fun(Dup) ->
{unsupported_feature,
<<"paths"/utf8>>,
<<<<"Duplicate operationId: '"/utf8, Dup/binary>>/binary,
"'"/utf8>>}
end
),
Fn_names = gleam@list:map(
Operations,
fun(Op@1) ->
{Op_id@1, _, _, _} = Op@1,
oaspec@util@naming:operation_to_function_name(Op_id@1)
end
),
Fn_collision_errors = find_name_collisions(
Fn_names,
fun(Dup@1) ->
{unsupported_feature,
<<"paths"/utf8>>,
<<<<"Function name collision after case conversion: '"/utf8,
Dup@1/binary>>/binary,
"'"/utf8>>}
end
),
Type_names = gleam@list:map(
Operations,
fun(Op@2) ->
{Op_id@2, _, _, _} = Op@2,
oaspec@util@naming:schema_to_type_name(Op_id@2)
end
),
Type_collision_errors = find_name_collisions(
Type_names,
fun(Dup@2) ->
{unsupported_feature,
<<"paths"/utf8>>,
<<<<"Type name collision after case conversion: '"/utf8,
Dup@2/binary>>/binary,
"'"/utf8>>}
end
),
lists:append([Op_id_errors, Fn_collision_errors, Type_collision_errors]).
-file("src/oaspec/codegen/validate.gleam", 491).
?DOC(" Validate security schemes for unsupported types.\n").
-spec validate_security_schemes(oaspec@codegen@context:context()) -> list(validation_error()).
validate_security_schemes(Ctx) ->
Schemes = case erlang:element(5, erlang:element(2, Ctx)) of
{some, Components} ->
maps:to_list(erlang:element(6, Components));
none ->
[]
end,
gleam@list:flat_map(
Schemes,
fun(Entry) ->
{Name, Scheme} = Entry,
case Scheme of
{api_key_scheme, _, _} ->
[];
{http_scheme, <<"bearer"/utf8>>, _} ->
[];
{http_scheme, <<"basic"/utf8>>, _} ->
[];
{http_scheme, <<"digest"/utf8>>, _} ->
[];
{http_scheme, Scheme_name, _} ->
[{unsupported_feature,
<<"components.securitySchemes."/utf8, Name/binary>>,
<<<<"HTTP security scheme '"/utf8,
Scheme_name/binary>>/binary,
"' is not supported. Supported schemes: bearer, basic, digest."/utf8>>}];
{o_auth2_scheme, _} ->
[{unsupported_feature,
<<"components.securitySchemes."/utf8, Name/binary>>,
<<"OAuth2 security scheme is not supported."/utf8>>}]
end
end
).
-file("src/oaspec/codegen/validate.gleam", 276).
?DOC(" Recursively validate a SchemaObject, descending into all sub-schemas.\n").
-spec validate_schema_recursive(binary(), oaspec@openapi@schema:schema_object()) -> list(validation_error()).
validate_schema_recursive(Path, Schema_obj) ->
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
);
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>>,
Inline_obj_errors = case Prop_ref of
{inline, {object_schema, _, _, _, _, _, _}} ->
[{unsupported_feature,
Prop_path,
<<"Nested inline object properties are not supported. Extract to a named schema in components.schemas and use $ref."/utf8>>}];
{inline, {all_of_schema, _, _}} ->
[{unsupported_feature,
Prop_path,
<<"Nested inline allOf properties are not supported. Extract to a named schema in components.schemas and use $ref."/utf8>>}];
_ ->
[]
end,
lists:append(
Inline_obj_errors,
validate_schema_ref_recursive(Prop_path, Prop_ref)
)
end
)
end,
Prop_names = begin
_pipe@1 = maps:to_list(Properties),
gleam@list:map(
_pipe@1,
fun(Entry@1) ->
{Prop_name@1, _} = Entry@1,
oaspec@util@naming:to_snake_case(Prop_name@1)
end
)
end,
Prop_collision_errors = find_name_collisions(
Prop_names,
fun(Dup) ->
{unsupported_feature,
Path,
<<<<"Property name collision after snake_case conversion: '"/utf8,
Dup/binary>>/binary,
"'"/utf8>>}
end
),
lists:append(
[Ap_errors, Typed_ap_errors, Prop_errors, Prop_collision_errors]
);
{array_schema, _, Items, _, _, _} ->
Item_errors = case Items of
{inline, {object_schema, _, _, _, _, _, _}} ->
[{unsupported_feature,
<<Path/binary, ".items"/utf8>>,
<<"Inline complex array items (object/allOf/oneOf/anyOf) are not supported. Extract to components.schemas and use $ref."/utf8>>}];
{inline, {all_of_schema, _, _}} ->
[{unsupported_feature,
<<Path/binary, ".items"/utf8>>,
<<"Inline complex array items (object/allOf/oneOf/anyOf) are not supported. Extract to components.schemas and use $ref."/utf8>>}];
{inline, {one_of_schema, _, _, _}} ->
[{unsupported_feature,
<<Path/binary, ".items"/utf8>>,
<<"Inline complex array items (object/allOf/oneOf/anyOf) are not supported. Extract to components.schemas and use $ref."/utf8>>}];
{inline, {any_of_schema, _, _}} ->
[{unsupported_feature,
<<Path/binary, ".items"/utf8>>,
<<"Inline complex array items (object/allOf/oneOf/anyOf) are not supported. Extract to components.schemas and use $ref."/utf8>>}];
_ ->
[]
end,
lists:append(
Item_errors,
validate_schema_ref_recursive(
<<Path/binary, ".items"/utf8>>,
Items
)
);
{one_of_schema, _, Schemas, _} ->
validate_compound_schemas(Path, Schemas);
{any_of_schema, _, Schemas@1} ->
validate_compound_schemas(Path, Schemas@1);
{all_of_schema, _, Schemas@2} ->
gleam@list:flat_map(
Schemas@2,
fun(S_ref) ->
validate_schema_ref_recursive(
<<Path/binary, ".allOf"/utf8>>,
S_ref
)
end
);
_ ->
[]
end.
-file("src/oaspec/codegen/validate.gleam", 381).
?DOC(
" Validate oneOf/anyOf schemas: only $ref variants are supported.\n"
" Inline schemas (primitives, objects, arrays) cannot be generated as\n"
" union variants.\n"
).
-spec validate_compound_schemas(
binary(),
list(oaspec@openapi@schema:schema_ref())
) -> list(validation_error()).
validate_compound_schemas(Path, Schemas) ->
Inline_errors = case has_any_inline_schemas(Schemas) of
true ->
[{unsupported_feature,
Path,
<<"oneOf/anyOf with inline schemas is not supported. All variants must be $ref to named schemas."/utf8>>}];
false ->
[]
end,
Child_errors = gleam@list:flat_map(
Schemas,
fun(S_ref) -> validate_schema_ref_recursive(Path, S_ref) end
),
lists:append(Inline_errors, Child_errors).
-file("src/oaspec/codegen/validate.gleam", 265).
?DOC(" Recursively validate a SchemaRef at any depth.\n").
-spec validate_schema_ref_recursive(
binary(),
oaspec@openapi@schema:schema_ref()
) -> list(validation_error()).
validate_schema_ref_recursive(Path, Schema_ref) ->
case Schema_ref of
{reference, _} ->
[];
{inline, Schema_obj} ->
validate_schema_recursive(Path, Schema_obj)
end.
-file("src/oaspec/codegen/validate.gleam", 126).
?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 | _] ->
[{unsupported_feature,
<<Op_id/binary, ".requestBody"/utf8>>,
<<<<"Content type '"/utf8, Media_type/binary>>/binary,
"' is not supported. Only 'application/json' and 'multipart/form-data' are supported for request bodies."/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
);
none ->
[]
end
end
)
end,
Multipart_field_errors = validate_multipart_request_body_fields(
Op_id,
erlang:element(3, Rb),
Ctx
),
lists:append(
[Content_type_errors, Schema_errors, Multipart_field_errors]
)
end.
-file("src/oaspec/codegen/validate.gleam", 217).
?DOC(" Validate response schemas and content types.\n").
-spec validate_responses(
binary(),
gleam@dict:dict(binary(), oaspec@openapi@spec:response())
) -> list(validation_error()).
validate_responses(Op_id, Responses) ->
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 ->
[{unsupported_feature,
Path,
<<<<"Response content type '"/utf8,
Media_type_name/binary>>/binary,
"' is not supported. Only 'application/json' and 'text/plain' are supported."/utf8>>}]
end,
Schema_errors = case erlang:element(2, Media_type) of
{some, Schema_ref} ->
validate_schema_ref_recursive(Path, Schema_ref);
none ->
[]
end,
lists:append(Content_type_errors, Schema_errors)
end
)
end
).
-file("src/oaspec/codegen/validate.gleam", 41).
?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, _, _} = Op,
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)
),
lists:append([Param_errors, Body_errors, Response_errors])
end
).
-file("src/oaspec/codegen/validate.gleam", 253).
?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
)
end
).
-file("src/oaspec/codegen/validate.gleam", 24).
?DOC(
" Validate the parsed spec for unsupported patterns.\n"
" Returns a list of errors; empty list means validation passed.\n"
).
-spec validate(oaspec@codegen@context:context()) -> list(validation_error()).
validate(Ctx) ->
Op_errors = validate_operations(Ctx),
Schema_errors = validate_component_schemas(Ctx),
Collision_errors = validate_name_collisions(Ctx),
Security_errors = validate_security_schemes(Ctx),
lists:append([Op_errors, Schema_errors, Collision_errors, Security_errors]).