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