fixed: rewrote schema loading (#2)

fixed: rewrote schema loading
This commit is contained in:
yuri-bukhalenkov 2021-03-24 14:41:59 +03:00 committed by GitHub
parent 5b9e7db104
commit 69f37cea0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -10,108 +10,81 @@
-spec get() -> {{packageName}}:object(). -spec get() -> {{packageName}}:object().
get() -> get() ->
ct_expand:term(enumerate_components(maps:with([?COMPONENTS], load_raw()))). ct_expand:term(enumerate_components(load_raw())).
-spec enumerate_components(Schema :: map()) -> -spec enumerate_components(Schema :: map()) ->
Schema :: map() | no_return(). Schema :: map() | no_return().
enumerate_components(Schema = #{?COMPONENTS := Components}) -> enumerate_components(Schema = #{?COMPONENTS := Components}) ->
%@NOTICE only parents within the same component type are supported Schema#{?COMPONENTS => enumerate_discriminator_children(Components)};
Schema#{?COMPONENTS => maps:map(fun enumerate_discriminator_children/2, Components)};
enumerate_components(Schema) -> enumerate_components(Schema) ->
Schema. Schema.
-spec enumerate_discriminator_children(ComponentType :: binary(), Schema :: map()) -> %% When using the discriminator, inline schemas will not be considered.
-spec enumerate_discriminator_children(Schema :: map()) ->
Schema :: map() | no_return(). Schema :: map() | no_return().
enumerate_discriminator_children(_ComponentType, Defs) -> enumerate_discriminator_children(#{<<"schemas">> := Defs}) ->
try try
{Parents, _} = maps:fold( maps:fold(fun correct_schema/3, Defs, build_hier(Defs))
fun(Name, Schema, Acc) ->
check_definition(Name, Schema, Acc)
end,
{#{}, #{}},
Defs
),
maps:fold(
fun(Parent, Children, Schema) ->
correct_schema(Parent, Children, Schema)
end,
Defs,
Parents
)
catch catch
_:Error -> _:Error ->
handle_error(Error) handle_error(Error)
end. end;
enumerate_discriminator_children(Schema) ->
Schema.
-spec handle_error(_) -> -spec handle_error(_) ->
no_return(). no_return().
handle_error(Error) -> handle_error(Error) ->
erlang:error({schema_invalid, Error}). erlang:error({schema_invalid, Error}).
check_definition(Name, Schema, Acc) -> build_hier(Defs) ->
Acc1 = check_discriminator(Name, Schema, Acc), F = fun
check_backrefs(Name, Schema, Acc1). (Name, #{<<"discriminator">> := _Type}, Acc) ->
insert_parent(Name, Acc);
check_discriminator(Name, Schema, {Parents, Candidates}) -> (Name, #{<<"allOf">> := Props}, Acc) ->
case maps:get(<<"discriminator">>, Schema, undefined) of lists:foldl(
undefined -> fun
{Parents, Candidates}; (#{<<"$ref">> := <<"#/components/schemas/", Type/binary>>}, Hier) ->
_ -> %% check: whether type itself exists
{ %% TODO: Implement according to OAS3:
Parents#{Name => maps:get(Name, Candidates, [])}, %% In scenarios where the value of the discriminator field does not match
maps:without([Name], Candidates) %% the schema name or implicit mapping is not possible, an optional mapping
} %% definition MAY be used.
end. Schema = maps:get(Type, Defs),
Discriminator = maps:get(<<"discriminator">>, Schema, undefined),
insert_child(Discriminator, Type, Name, Hier);
check_backrefs(Name, Schema, Acc) -> (_, Hier) ->
case maps:get(<<"allOf">>, Schema, undefined) of Hier
undefined -> end,
Acc; Acc,
AllOf -> Props
lists:foldl(fun(E, A) -> check_allOf(E, Name, A) end, Acc, AllOf) )
end. end,
maps:fold(F, #{}, Defs).
check_allOf(#{<<"$ref">> := RefPath}, Child, {Parents, Candidates}) -> insert_parent(Parent, Hier) when not is_map_key(Parent, Hier) ->
Parent = get_parent_from_ref(RefPath), Hier#{Parent => []};
case maps:get(Parent, Parents, undefined) of insert_parent(_, Hier) ->
undefined -> Hier.
{Parents, update_candidates(Parent, Child, Candidates)};
Children ->
{Parents#{Parent => [Child | Children]}, Candidates}
end;
check_allOf(_, _, Acc) ->
Acc.
get_parent_from_ref(RefPath) -> insert_child(undefined, _Parent, _Child, Hier) ->
Split = binary:split(RefPath, [<<"/">>], [global]), Hier;
lists:last(Split). insert_child(_Discriminator, Parent, Child, Hier) ->
maps:put(Parent, [Child | maps:get(Parent, Hier, [])], Hier).
update_candidates(Parent, Child, Candidates) ->
case maps:get(Parent, Candidates, undefined) of
undefined ->
Candidates#{Parent => [Child]};
Children ->
Candidates#{Parent => [Child | Children]}
end.
correct_schema(Parent, Children, Schema) -> correct_schema(Parent, Children, Schema) ->
BasePath = [Parent], PropertyName = get_sub_schema([Parent, <<"discriminator">>, <<"propertyName">>], Schema),
Discr = maps:get(<<"discriminator">>, get_sub_schema(BasePath, Schema)), update_schema([Parent, <<"properties">>, PropertyName, <<"enum">>], Children, Schema).
PropertyName = maps:get(<<"propertyName">>, Discr),
update_schema(Children, [<<"enum">>, PropertyName, <<"properties">> | BasePath], Schema).
update_schema(Value, [], _Schema) -> update_schema([Key], Value, Schema) ->
Value; Schema#{Key => Value};
update_schema(Value, [Key | Path], Schema) -> update_schema([Key | Path], Value, Schema) ->
SubSchema0 = get_sub_schema(Path, Schema), maps:put(Key, update_schema(Path, Value, maps:get(Key, Schema)), Schema).
SubSchema1 = update_sub_schema(Key, Value, SubSchema0),
update_schema(SubSchema1, Path, Schema).
get_sub_schema(ReversedPath, Schema) -> get_sub_schema(Path, Schema) ->
lists:foldr(fun(K, S) -> maps:get(K, S) end, Schema, ReversedPath). lists:foldl(fun maps:get/2, Schema, Path).
update_sub_schema(Key, Value, Schema) ->
Schema#{Key => Value}.
-spec get_raw() -> map(). -spec get_raw() -> map().
get_raw() -> get_raw() ->
@ -215,7 +188,7 @@ get_openapi_path() ->
}}">>). }}">>).
get_enum(Parent, Discr, ComponentType, Schema) -> get_enum(Parent, Discr, ComponentType, Schema) ->
lists:sort(get_sub_schema([<<"enum">>, Discr, <<"properties">>, Parent, ComponentType, ?COMPONENTS], Schema)). lists:sort(get_sub_schema([?COMPONENTS, ComponentType, Parent, <<"properties">>, Discr, <<"enum">>], Schema)).
-spec test() -> _. -spec test() -> _.
-spec enumerate_discriminator_children_test() -> _. -spec enumerate_discriminator_children_test() -> _.
@ -223,16 +196,16 @@ enumerate_discriminator_children_test() ->
Schema = jsx:decode(?SCHEMA, [return_maps]), Schema = jsx:decode(?SCHEMA, [return_maps]),
FixedSchema = enumerate_components(Schema), FixedSchema = enumerate_components(Schema),
?assertEqual( ?assertEqual(
lists:sort([<<"Dog">>, <<"Cat">>, <<"WildMix">>]), [<<"Cat">>, <<"Dog">>, <<"WildMix">>],
get_enum(<<"Pet">>, <<"petType">>, <<"schemas">>, FixedSchema) get_enum(<<"Pet">>, <<"petType">>, <<"schemas">>, FixedSchema)
), ),
?assertEqual([<<"WildMix">>], get_enum(<<"Person">>, <<"personType">>, <<"schemas">>, FixedSchema)), ?assertEqual([<<"WildMix">>], get_enum(<<"Person">>, <<"personType">>, <<"schemas">>, FixedSchema)),
?assertEqual([], get_enum(<<"Dummy">>, <<"dummyType">>, <<"schemas">>, FixedSchema)). ?assertEqual([], get_enum(<<"Dummy">>, <<"dummyType">>, <<"schemas">>, FixedSchema)).
-spec get_test() -> _. -spec get_test() -> _.
get_test() -> get_test() ->
?assertEqual( ?assertEqual(
enumerate_components(maps:with([?COMPONENTS], get_raw())), enumerate_components(get_raw()),
?MODULE:get() ?MODULE:get()
). ).
-endif. -endif.