Packages
oaspec
0.3.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", 29).
?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", 49).
?DOC(" Validate parameters for unsupported patterns.\n").
-spec validate_parameters(binary(), list(oaspec@openapi@spec:parameter())) -> list(validation_error()).
validate_parameters(Op_id, Params) ->
gleam@list:flat_map(
Params,
fun(Param) ->
Path = <<<<Op_id/binary, ".parameters."/utf8>>/binary,
(erlang:element(2, Param))/binary>>,
Style_errors = case erlang:element(7, Param) of
{some, <<"deepObject"/utf8>>} ->
[{unsupported_feature,
Path,
<<"style: deepObject is not supported. Gleam cannot express deep object query serialization."/utf8>>}];
_ ->
[]
end,
Schema_errors = case erlang:element(6, Param) of
{some, {inline, {array_schema, _, _, _, _, _}}} ->
[{unsupported_feature,
Path,
<<"Array parameters are not yet supported. Use a single-value parameter instead."/utf8>>}];
{some, {inline, {object_schema, _, _, _, _, _, _}}} ->
[{unsupported_feature,
Path,
<<"Complex schema parameters (object/allOf/oneOf/anyOf) are not supported."/utf8>>}];
{some, {inline, {all_of_schema, _, _}}} ->
[{unsupported_feature,
Path,
<<"Complex schema parameters (object/allOf/oneOf/anyOf) are not supported."/utf8>>}];
{some, {inline, {one_of_schema, _, _, _}}} ->
[{unsupported_feature,
Path,
<<"Complex schema parameters (object/allOf/oneOf/anyOf) are not supported."/utf8>>}];
{some, {inline, {any_of_schema, _, _}}} ->
[{unsupported_feature,
Path,
<<"Complex schema parameters (object/allOf/oneOf/anyOf) are not supported."/utf8>>}];
_ ->
[]
end,
lists:append(Style_errors, Schema_errors)
end
).
-file("src/oaspec/codegen/validate.gleam", 332).
?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", 410).
?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(IAC),
fun((IAC) -> 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", 342).
?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_name_errors = find_duplicates(
Operations,
fun(Op@1) ->
{Op_id@1, _, _, _} = Op@1,
oaspec@util@naming:operation_to_function_name(Op_id@1)
end,
fun(Dup@1) ->
{unsupported_feature,
<<"paths"/utf8>>,
<<<<"Function name collision after snake_case conversion: '"/utf8,
Dup@1/binary>>/binary,
"'"/utf8>>}
end
),
Type_name_errors = find_duplicates(
Operations,
fun(Op@2) ->
{Op_id@2, _, _, _} = Op@2,
oaspec@util@naming:schema_to_type_name(Op_id@2)
end,
fun(Dup@2) ->
{unsupported_feature,
<<"paths"/utf8>>,
<<<<"Type name collision after PascalCase conversion: '"/utf8,
Dup@2/binary>>/binary,
"'"/utf8>>}
end
),
lists:append([Op_id_errors, Fn_name_errors, Type_name_errors]).
-file("src/oaspec/codegen/validate.gleam", 401).
?DOC(" Find duplicates in a list of strings.\n").
-spec find_string_duplicates(
list(binary()),
fun((binary()) -> validation_error())
) -> list(validation_error()).
find_string_duplicates(Items, Error_fn) ->
find_duplicates(Items, fun(S) -> S end, Error_fn).
-file("src/oaspec/codegen/validate.gleam", 181).
?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 = case Additional_properties_untyped of
true ->
[{unsupported_feature,
Path,
<<"additionalProperties: true is not supported. Gleam has no untyped map type."/utf8>>}];
false ->
[]
end,
Typed_ap_errors = case Additional_properties of
{some, _} ->
[{unsupported_feature,
Path,
<<"Typed additionalProperties is not yet supported."/utf8>>}];
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_string_duplicates(
Prop_names,
fun(Dup) ->
{unsupported_feature,
Path,
<<<<"Property name collision after snake_case: '"/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
);
_ ->
case Schema_obj of
{string_schema, _, _, Enum_values, _, _, _, _} when Enum_values =/= [] ->
Variant_names = gleam@list:map(
Enum_values,
fun(V) -> oaspec@util@naming:schema_to_type_name(V) end
),
find_string_duplicates(
Variant_names,
fun(Dup@1) ->
{unsupported_feature,
Path,
<<<<"Enum variant collision after PascalCase: '"/utf8,
Dup@1/binary>>/binary,
"'"/utf8>>}
end
);
_ ->
[]
end
end.
-file("src/oaspec/codegen/validate.gleam", 310).
?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", 170).
?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", 88).
?DOC(" Validate request body for unsupported patterns.\n").
-spec validate_request_body(
binary(),
gleam@option:option(oaspec@openapi@spec:request_body())
) -> list(validation_error()).
validate_request_body(Op_id, Request_body) ->
case Request_body of
none ->
[];
{some, Rb} ->
Content_keys = maps:keys(erlang:element(3, Rb)),
Non_json = gleam@list:filter(
Content_keys,
fun(Key) -> Key /= <<"application/json"/utf8>> end
),
Content_type_errors = case Non_json of
[] ->
[];
[Media_type | _] ->
[{unsupported_feature,
<<Op_id/binary, ".requestBody"/utf8>>,
<<<<"Content type '"/utf8, Media_type/binary>>/binary,
"' is not supported. Only 'application/json' is supported."/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,
lists:append(Content_type_errors, Schema_errors)
end.
-file("src/oaspec/codegen/validate.gleam", 126).
?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 Media_type_name of
<<"application/json"/utf8>> ->
[];
_ ->
[{unsupported_feature,
Path,
<<<<"Response content type '"/utf8,
Media_type_name/binary>>/binary,
"' is not supported. Only 'application/json' is 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", 37).
?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)
),
Body_errors = validate_request_body(
Op_id,
erlang:element(7, Operation)
),
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", 158).
?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", 21).
?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),
lists:append([Op_errors, Schema_errors, Collision_errors]).