Packages

OpenAPI code generation for Gleam — parse specs, generate types, routes, clients, and React Query/SWR hooks

Current section

Files

Jump to
nori src nori@validator.erl
Raw

src/nori@validator.erl

-module(nori@validator).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/nori/validator.gleam").
-export([validate/1, is_valid/1]).
-export_type([validation_result/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.
?MODULEDOC(
" Validator for OpenAPI documents.\n"
"\n"
" Validates OpenAPI documents against the specification.\n"
).
-type validation_result() :: valid |
{invalid, list(nori@validator@errors:validation_error())}.
-file("src/nori/validator.gleam", 48).
?DOC(" Validates info object.\n").
-spec validate_info(
nori@document:document(),
list(nori@validator@errors:validation_error())
) -> list(nori@validator@errors:validation_error()).
validate_info(Doc, Errors) ->
Errors@1 = case gleam@string:is_empty(
erlang:element(2, erlang:element(3, Doc))
) of
true ->
[{empty_required_field, <<"info.title"/utf8>>} | Errors];
false ->
Errors
end,
Errors@2 = case gleam@string:is_empty(
erlang:element(7, erlang:element(3, Doc))
) of
true ->
[{empty_required_field, <<"info.version"/utf8>>} | Errors@1];
false ->
Errors@1
end,
Errors@2.
-file("src/nori/validator.gleam", 125).
?DOC(" Validates an operation.\n").
-spec validate_operation(
binary(),
binary(),
nori@operation:operation(),
list(nori@validator@errors:validation_error())
) -> list(nori@validator@errors:validation_error()).
validate_operation(Path, Method, Op, Errors) ->
Errors@1 = case gleam@dict:is_empty(erlang:element(9, Op)) of
true ->
[{missing_response,
<<<<Path/binary, "."/utf8>>/binary, Method/binary>>,
<<"Operation must have at least one response"/utf8>>} |
Errors];
false ->
Errors
end,
Errors@2 = gleam@list:fold(
erlang:element(7, Op),
Errors@1,
fun(Errs, Param_ref) -> case Param_ref of
{reference, _} ->
Errs;
{inline, Param} ->
case erlang:element(3, Param) of
in_path ->
case erlang:element(5, Param) of
{some, true} ->
Errs;
_ ->
[{invalid_parameter,
<<<<<<<<Path/binary, "."/utf8>>/binary,
Method/binary>>/binary,
".parameters."/utf8>>/binary,
(erlang:element(2, Param))/binary>>,
<<"Path parameters must be required"/utf8>>} |
Errs]
end;
_ ->
Errs
end
end end
),
Errors@2.
-file("src/nori/validator.gleam", 111).
?DOC(" Validates an optional operation.\n").
-spec validate_operation_opt(
binary(),
binary(),
gleam@option:option(nori@operation:operation()),
list(nori@validator@errors:validation_error())
) -> list(nori@validator@errors:validation_error()).
validate_operation_opt(Path, Method, Op, Errors) ->
case Op of
none ->
Errors;
{some, Operation} ->
validate_operation(Path, Method, Operation, Errors)
end.
-file("src/nori/validator.gleam", 94).
?DOC(" Validates a path item.\n").
-spec validate_path_item(
binary(),
nori@operation:path_item(),
list(nori@validator@errors:validation_error())
) -> list(nori@validator@errors:validation_error()).
validate_path_item(Path, Item, Errors) ->
Errors@1 = validate_operation_opt(
Path,
<<"get"/utf8>>,
erlang:element(5, Item),
Errors
),
Errors@2 = validate_operation_opt(
Path,
<<"put"/utf8>>,
erlang:element(6, Item),
Errors@1
),
Errors@3 = validate_operation_opt(
Path,
<<"post"/utf8>>,
erlang:element(7, Item),
Errors@2
),
Errors@4 = validate_operation_opt(
Path,
<<"delete"/utf8>>,
erlang:element(8, Item),
Errors@3
),
Errors@5 = validate_operation_opt(
Path,
<<"options"/utf8>>,
erlang:element(9, Item),
Errors@4
),
Errors@6 = validate_operation_opt(
Path,
<<"head"/utf8>>,
erlang:element(10, Item),
Errors@5
),
Errors@7 = validate_operation_opt(
Path,
<<"patch"/utf8>>,
erlang:element(11, Item),
Errors@6
),
Errors@8 = validate_operation_opt(
Path,
<<"trace"/utf8>>,
erlang:element(12, Item),
Errors@7
),
Errors@8.
-file("src/nori/validator.gleam", 66).
?DOC(" Validates paths object.\n").
-spec validate_paths(
nori@document:document(),
list(nori@validator@errors:validation_error())
) -> list(nori@validator@errors:validation_error()).
validate_paths(Doc, Errors) ->
case erlang:element(6, Doc) of
none ->
Errors;
{some, Paths} ->
gleam@dict:fold(
Paths,
Errors,
fun(Errs, Path, Item_ref) ->
Errs@1 = case gleam_stdlib:string_starts_with(
Path,
<<"/"/utf8>>
) of
true ->
Errs;
false ->
[{invalid_path,
Path,
<<"Path must start with /"/utf8>>} |
Errs]
end,
case Item_ref of
{reference, _} ->
Errs@1;
{inline, Item} ->
validate_path_item(Path, Item, Errs@1)
end
end
)
end.
-file("src/nori/validator.gleam", 237).
-spec is_valid_component_char(binary()) -> boolean().
is_valid_component_char(Char) ->
case Char of
<<"a"/utf8>> ->
true;
<<"b"/utf8>> ->
true;
<<"c"/utf8>> ->
true;
<<"d"/utf8>> ->
true;
<<"e"/utf8>> ->
true;
<<"f"/utf8>> ->
true;
<<"g"/utf8>> ->
true;
<<"h"/utf8>> ->
true;
<<"i"/utf8>> ->
true;
<<"j"/utf8>> ->
true;
<<"k"/utf8>> ->
true;
<<"l"/utf8>> ->
true;
<<"m"/utf8>> ->
true;
<<"n"/utf8>> ->
true;
<<"o"/utf8>> ->
true;
<<"p"/utf8>> ->
true;
<<"q"/utf8>> ->
true;
<<"r"/utf8>> ->
true;
<<"s"/utf8>> ->
true;
<<"t"/utf8>> ->
true;
<<"u"/utf8>> ->
true;
<<"v"/utf8>> ->
true;
<<"w"/utf8>> ->
true;
<<"x"/utf8>> ->
true;
<<"y"/utf8>> ->
true;
<<"z"/utf8>> ->
true;
<<"A"/utf8>> ->
true;
<<"B"/utf8>> ->
true;
<<"C"/utf8>> ->
true;
<<"D"/utf8>> ->
true;
<<"E"/utf8>> ->
true;
<<"F"/utf8>> ->
true;
<<"G"/utf8>> ->
true;
<<"H"/utf8>> ->
true;
<<"I"/utf8>> ->
true;
<<"J"/utf8>> ->
true;
<<"K"/utf8>> ->
true;
<<"L"/utf8>> ->
true;
<<"M"/utf8>> ->
true;
<<"N"/utf8>> ->
true;
<<"O"/utf8>> ->
true;
<<"P"/utf8>> ->
true;
<<"Q"/utf8>> ->
true;
<<"R"/utf8>> ->
true;
<<"S"/utf8>> ->
true;
<<"T"/utf8>> ->
true;
<<"U"/utf8>> ->
true;
<<"V"/utf8>> ->
true;
<<"W"/utf8>> ->
true;
<<"X"/utf8>> ->
true;
<<"Y"/utf8>> ->
true;
<<"Z"/utf8>> ->
true;
<<"0"/utf8>> ->
true;
<<"1"/utf8>> ->
true;
<<"2"/utf8>> ->
true;
<<"3"/utf8>> ->
true;
<<"4"/utf8>> ->
true;
<<"5"/utf8>> ->
true;
<<"6"/utf8>> ->
true;
<<"7"/utf8>> ->
true;
<<"8"/utf8>> ->
true;
<<"9"/utf8>> ->
true;
<<"."/utf8>> ->
true;
<<"-"/utf8>> ->
true;
<<"_"/utf8>> ->
true;
_ ->
false
end.
-file("src/nori/validator.gleam", 228).
?DOC(" Checks if a component name is valid.\n").
-spec is_valid_component_name(binary()) -> boolean().
is_valid_component_name(Name) ->
case gleam@string:is_empty(Name) of
true ->
false;
false ->
_pipe = gleam@string:to_graphemes(Name),
gleam@list:all(_pipe, fun is_valid_component_char/1)
end.
-file("src/nori/validator.gleam", 208).
?DOC(" Validates a component name matches the required pattern.\n").
-spec validate_component_name(
binary(),
binary(),
list(nori@validator@errors:validation_error())
) -> list(nori@validator@errors:validation_error()).
validate_component_name(Path, Name, Errors) ->
Valid = is_valid_component_name(Name),
case Valid of
true ->
Errors;
false ->
[{invalid_component_name,
<<<<Path/binary, "."/utf8>>/binary, Name/binary>>,
Name,
<<"Component names must match ^[a-zA-Z0-9._-]+$"/utf8>>} |
Errors]
end.
-file("src/nori/validator.gleam", 174).
?DOC(" Validates components.\n").
-spec validate_components(
nori@document:document(),
list(nori@validator@errors:validation_error())
) -> list(nori@validator@errors:validation_error()).
validate_components(Doc, Errors) ->
case erlang:element(8, Doc) of
none ->
Errors;
{some, Components} ->
Errors@1 = gleam@dict:fold(
erlang:element(2, Components),
Errors,
fun(Errs, Name, _) ->
validate_component_name(
<<"components.schemas"/utf8>>,
Name,
Errs
)
end
),
Errors@2 = gleam@dict:fold(
erlang:element(3, Components),
Errors@1,
fun(Errs@1, Name@1, _) ->
validate_component_name(
<<"components.responses"/utf8>>,
Name@1,
Errs@1
)
end
),
Errors@3 = gleam@dict:fold(
erlang:element(4, Components),
Errors@2,
fun(Errs@2, Name@2, _) ->
validate_component_name(
<<"components.parameters"/utf8>>,
Name@2,
Errs@2
)
end
),
Errors@4 = gleam@dict:fold(
erlang:element(8, Components),
Errors@3,
fun(Errs@3, Name@3, _) ->
validate_component_name(
<<"components.securitySchemes"/utf8>>,
Name@3,
Errs@3
)
end
),
Errors@4
end.
-file("src/nori/validator.gleam", 284).
-spec collect_refs_from_path_item_ref(
nori@reference:ref(nori@operation:path_item()),
list(binary())
) -> list(binary()).
collect_refs_from_path_item_ref(Item_ref, Refs) ->
case Item_ref of
{reference, R} ->
[R | Refs];
{inline, _} ->
Refs
end.
-file("src/nori/validator.gleam", 269).
?DOC(" Collects all $ref strings from the document.\n").
-spec collect_references(nori@document:document()) -> list(binary()).
collect_references(Doc) ->
Refs = [],
Refs@1 = case erlang:element(6, Doc) of
none ->
Refs;
{some, Paths} ->
gleam@dict:fold(
Paths,
Refs,
fun(R, _, Item_ref) ->
collect_refs_from_path_item_ref(Item_ref, R)
end
)
end,
Refs@1.
-file("src/nori/validator.gleam", 295).
?DOC(" Checks if a reference is resolvable within the document.\n").
-spec is_reference_resolvable(nori@document:document(), binary()) -> boolean().
is_reference_resolvable(Doc, Ref) ->
case Ref of
<<"#/components/schemas/"/utf8, Name/binary>> ->
case erlang:element(8, Doc) of
none ->
false;
{some, C} ->
gleam@dict:has_key(erlang:element(2, C), Name)
end;
<<"#/components/responses/"/utf8, Name@1/binary>> ->
case erlang:element(8, Doc) of
none ->
false;
{some, C@1} ->
gleam@dict:has_key(erlang:element(3, C@1), Name@1)
end;
<<"#/components/parameters/"/utf8, Name@2/binary>> ->
case erlang:element(8, Doc) of
none ->
false;
{some, C@2} ->
gleam@dict:has_key(erlang:element(4, C@2), Name@2)
end;
<<"#/components/examples/"/utf8, Name@3/binary>> ->
case erlang:element(8, Doc) of
none ->
false;
{some, C@3} ->
gleam@dict:has_key(erlang:element(5, C@3), Name@3)
end;
<<"#/components/requestBodies/"/utf8, Name@4/binary>> ->
case erlang:element(8, Doc) of
none ->
false;
{some, C@4} ->
gleam@dict:has_key(erlang:element(6, C@4), Name@4)
end;
<<"#/components/headers/"/utf8, Name@5/binary>> ->
case erlang:element(8, Doc) of
none ->
false;
{some, C@5} ->
gleam@dict:has_key(erlang:element(7, C@5), Name@5)
end;
<<"#/components/securitySchemes/"/utf8, Name@6/binary>> ->
case erlang:element(8, Doc) of
none ->
false;
{some, C@6} ->
gleam@dict:has_key(erlang:element(8, C@6), Name@6)
end;
<<"#/components/links/"/utf8, Name@7/binary>> ->
case erlang:element(8, Doc) of
none ->
false;
{some, C@7} ->
gleam@dict:has_key(erlang:element(9, C@7), Name@7)
end;
<<"#/components/callbacks/"/utf8, Name@8/binary>> ->
case erlang:element(8, Doc) of
none ->
false;
{some, C@8} ->
gleam@dict:has_key(erlang:element(10, C@8), Name@8)
end;
<<"#/components/pathItems/"/utf8, Name@9/binary>> ->
case erlang:element(8, Doc) of
none ->
false;
{some, C@9} ->
gleam@dict:has_key(erlang:element(11, C@9), Name@9)
end;
_ ->
true
end.
-file("src/nori/validator.gleam", 254).
?DOC(" Validates that all $ref references are resolvable.\n").
-spec validate_references(
nori@document:document(),
list(nori@validator@errors:validation_error())
) -> list(nori@validator@errors:validation_error()).
validate_references(Doc, Errors) ->
Refs = collect_references(Doc),
gleam@list:fold(
Refs,
Errors,
fun(Errs, Ref) -> case is_reference_resolvable(Doc, Ref) of
true ->
Errs;
false ->
[{unresolvable_reference, Ref} | Errs]
end end
).
-file("src/nori/validator.gleam", 379).
-spec find_duplicates_helper(list(binary()), list(binary()), list(binary())) -> list(binary()).
find_duplicates_helper(Remaining, Seen, Duplicates) ->
case Remaining of
[] ->
Duplicates;
[Item | Rest] ->
case gleam@list:contains(Seen, Item) of
true ->
case gleam@list:contains(Duplicates, Item) of
true ->
find_duplicates_helper(Rest, Seen, Duplicates);
false ->
find_duplicates_helper(
Rest,
Seen,
[Item | Duplicates]
)
end;
false ->
find_duplicates_helper(Rest, [Item | Seen], Duplicates)
end
end.
-file("src/nori/validator.gleam", 375).
?DOC(" Finds duplicate items in a list.\n").
-spec find_duplicates(list(binary())) -> list(binary()).
find_duplicates(Items) ->
find_duplicates_helper(Items, [], []).
-file("src/nori/validator.gleam", 363).
?DOC(" Validates that operation IDs are unique.\n").
-spec validate_unique_operation_ids(
nori@document:document(),
list(nori@validator@errors:validation_error())
) -> list(nori@validator@errors:validation_error()).
validate_unique_operation_ids(Doc, Errors) ->
Op_ids = nori@document:get_operation_ids(Doc),
Duplicates = find_duplicates(Op_ids),
gleam@list:fold(
Duplicates,
Errors,
fun(Errs, Dup) -> [{duplicate_operation_id, Dup} | Errs] end
).
-file("src/nori/validator.gleam", 23).
?DOC(" Validates an OpenAPI document.\n").
-spec validate(nori@document:document()) -> validation_result().
validate(Doc) ->
Errors = [],
Errors@1 = validate_info(Doc, Errors),
Errors@2 = validate_paths(Doc, Errors@1),
Errors@3 = validate_components(Doc, Errors@2),
Errors@4 = validate_references(Doc, Errors@3),
Errors@5 = validate_unique_operation_ids(Doc, Errors@4),
case Errors@5 of
[] ->
valid;
Errs ->
{invalid, lists:reverse(Errs)}
end.
-file("src/nori/validator.gleam", 401).
?DOC(" Checks if a document is valid (convenience function).\n").
-spec is_valid(nori@document:document()) -> boolean().
is_valid(Doc) ->
case validate(Doc) of
valid ->
true;
{invalid, _} ->
false
end.