Skip to content

Commit

Permalink
fix: handle oas 3.0 optionality
Browse files Browse the repository at this point in the history
  • Loading branch information
javiergarea committed Dec 13, 2023
1 parent 213e2f0 commit 6304d93
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 171 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: erf ci
name: CI

on:
push:
Expand Down
116 changes: 66 additions & 50 deletions src/erf_oas_3_0.erl
Original file line number Diff line number Diff line change
Expand Up @@ -259,12 +259,18 @@ parse_operation(
maps:get(<<"parameters">>, RawOperation, [])
),

RequestBodyRef = erf_util:to_snake_case(<<(NewCTX#ctx.namespace)/binary, "_request_body">>),
RawRequestBody = maps:get(<<"requestBody">>, RawOperation, undefined),
{RequestBodySchema, RequestBodyExtraSchemas, RequestBodyCTX} = parse_request_body(
{ParsedRequestBody, RawRequestBodySchemas, RequestBodyCTX} = parse_request_body(
RawRequestBody, ParametersCTX
),
RequestBodySchemas = [{RequestBodyRef, RequestBodySchema} | RequestBodyExtraSchemas],
RequestBodyRef = erf_util:to_snake_case(<<(NewCTX#ctx.namespace)/binary, "_request_body">>),
RequestBodyRequired = maps:get(required, ParsedRequestBody),
RequestBodySchema = maps:get(schema, ParsedRequestBody),
RequestBodySchemas = [{RequestBodyRef, RequestBodySchema} | RawRequestBodySchemas],
RequestBody = #{
ref => RequestBodyRef,
required => RequestBodyRequired
},

{Responses, ResponsesSchemas, ResponsesCTX} =
lists:foldl(
Expand All @@ -279,22 +285,22 @@ parse_operation(
_RawStatusCode ->
erlang:binary_to_integer(RawStatusCode)
end,
{ResponseBody, ResponseExtraSchemas, ResponseCTX} = parse_response_body(
{ParsedResponse, RawResponseExtraSchemas, ResponseCTX} = parse_response(
RawResponse, ResponsesCTXAcc
),
RawRef =
case StatusCode of
'*' ->
<<"default">>;
_StatusCode ->
erlang:integer_to_binary(StatusCode)
end,
Ref = erf_util:to_snake_case(<<
(NewCTX#ctx.namespace)/binary, "_response_body_", RawRef/binary
ResponseRef = erf_util:to_snake_case(<<
(NewCTX#ctx.namespace)/binary, "_response_body_", RawStatusCode/binary
>>),
ResponseRequired = maps:get(required, ParsedResponse),
ResponseSchema = maps:get(schema, ParsedResponse),
ResponseExtraSchemas = [{ResponseRef, ResponseSchema} | RawResponseExtraSchemas],
Response = #{
ref => ResponseRef,
required => ResponseRequired
},
{
ResponsesAcc#{StatusCode => Ref},
[{Ref, ResponseBody} | ResponseExtraSchemas] ++ ResponsesExtraSchemasAcc,
ResponsesAcc#{StatusCode => Response},
ResponseExtraSchemas ++ ResponsesExtraSchemasAcc,
ResponseCTX
}
end,
Expand All @@ -306,7 +312,7 @@ parse_operation(
id => OperationId,
method => Method,
parameters => Parameters,
request_body => RequestBodyRef,
request_body => RequestBody,
responses => Responses
},
Schemas = ParametersSchemas ++ RequestBodySchemas ++ ResponsesSchemas,
Expand Down Expand Up @@ -347,17 +353,14 @@ parse_parameter(#{<<"content">> := Content} = RawParameter, #ctx{namespace = Nam
Parameter = #{
ref => ParameterRef,
name => ParameterName,
type => ParameterType
type => ParameterType,
required => Required
},
{AnyOf, ExtraSchemas, NewCTX} =
lists:foldl(
fun({_MediaType, #{<<"schema">> := RawSchema}}, {AnyOfAcc, ExtraSchemasAcc, CTXAcc}) ->
{Schema, ExtraSchemas, SchemaCTX} = parse_schema(RawSchema, CTXAcc),
{
[Schema#{<<"nullable">> => not Required} | AnyOfAcc],
ExtraSchemas ++ ExtraSchemasAcc,
SchemaCTX
}
{[Schema | AnyOfAcc], ExtraSchemas ++ ExtraSchemasAcc, SchemaCTX}
end,
{[], [], CTX},
maps:to_list(Content)
Expand Down Expand Up @@ -389,64 +392,76 @@ parse_parameter(#{<<"schema">> := RawSchema} = RawParameter, #ctx{namespace = Na
Parameter = #{
ref => ParameterRef,
name => ParameterName,
type => ParameterType
type => ParameterType,
required => Required
},
{RawParameterSchema, NewExtraSchemas, NewCTX} = parse_schema(RawSchema, CTX),
ParameterSchema = maps:put(<<"nullable">>, not Required, RawParameterSchema),
{ParameterSchema, NewExtraSchemas, NewCTX} = parse_schema(RawSchema, CTX),
{Parameter, [{ParameterRef, ParameterSchema} | NewExtraSchemas], NewCTX}.

-spec parse_request_body(OAS, CTX) -> Result when
OAS :: oas(),
CTX :: ctx(),
Result :: {RequestBody, ExtraSchemas, NewCTX},
RequestBody :: erf_parser:schema(),
RequestBody :: #{schema := erf_parser:schema(), required := boolean()},
ExtraSchemas :: [{erf_parser:ref(), erf_parser:schema()}],
NewCTX :: ctx().
parse_request_body(#{<<"$ref">> := Ref}, CTX) ->
{RefResolved, RefOAS, RefCTX} = resolve_ref(Ref, CTX),
{NewSchema, NewExtraSchemas, NewCTX} = parse_request_body(RefOAS, RefCTX),
{#{<<"$ref">> => RefResolved}, [{RefResolved, NewSchema} | NewExtraSchemas], NewCTX};
{_RefResolved, RefOAS, RefCTX} = resolve_ref(Ref, CTX),
parse_request_body(RefOAS, RefCTX);
parse_request_body(#{<<"content">> := Content} = ReqBody, CTX) ->
Required = maps:get(<<"required">>, ReqBody, false),
{AnyOf, NewExtraSchemas, NewCTX} = lists:foldl(
{AnyOf, ExtraSchemas, NewCTX} = lists:foldl(
fun({_MediaType, #{<<"schema">> := RawSchema}}, {AnyOfAcc, ExtraSchemasAcc, CTXAcc}) ->
{Schema, ExtraSchemas, SchemaCTX} = parse_schema(RawSchema, CTXAcc),
{
[Schema#{<<"nullable">> => not Required} | AnyOfAcc],
ExtraSchemas ++ ExtraSchemasAcc,
SchemaCTX
}
{Schema, NewExtraSchemas, SchemaCTX} = parse_schema(RawSchema, CTXAcc),
{[Schema | AnyOfAcc], NewExtraSchemas ++ ExtraSchemasAcc, SchemaCTX}
end,
{[], [], CTX},
maps:to_list(Content)
),
{#{<<"anyOf">> => AnyOf}, NewExtraSchemas, NewCTX};
RequestBodySchema = #{<<"anyOf">> => AnyOf},
RequestBody = #{
schema => RequestBodySchema,
required => Required
},
{RequestBody, ExtraSchemas, NewCTX};
parse_request_body(_ReqBody, CTX) ->
{undefined, [], CTX}.
RequestBody = #{
schema => true,
required => false
},
{RequestBody, [], CTX}.

-spec parse_response_body(OAS, CTX) -> Result when
-spec parse_response(OAS, CTX) -> Result when
OAS :: oas(),
CTX :: ctx(),
Result :: {ResponseBody, ExtraSchemas, NewCTX},
ResponseBody :: erf_parser:schema(),
Result :: {Response, ExtraSchemas, NewCTX},
Response :: #{schema := erf_parser:schema(), required := boolean()},
ExtraSchemas :: [{erf_parser:ref(), erf_parser:schema()}],
NewCTX :: ctx().
parse_response_body(#{<<"$ref">> := Ref}, CTX) ->
{RefResolved, RefOAS, RefCTX} = resolve_ref(Ref, CTX),
{NewSchema, NewExtraSchemas, NewCTX} = parse_response_body(RefOAS, RefCTX),
{#{<<"$ref">> => RefResolved}, [{RefResolved, NewSchema} | NewExtraSchemas], NewCTX};
parse_response_body(#{<<"content">> := Content}, CTX) ->
{AnyOf, NewExtraSchemas, NewCTX} = lists:foldl(
parse_response(#{<<"$ref">> := Ref}, CTX) ->
{_RefResolved, RefOAS, RefCTX} = resolve_ref(Ref, CTX),
parse_response(RefOAS, RefCTX);
parse_response(#{<<"content">> := Content}, CTX) ->
{AnyOf, ExtraSchemas, NewCTX} = lists:foldl(
fun({_MediaType, #{<<"schema">> := RawSchema}}, {AnyOfAcc, ExtraSchemasAcc, CTXAcc}) ->
{Schema, ExtraSchemas, SchemaCTX} = parse_schema(RawSchema, CTXAcc),
{[Schema | AnyOfAcc], ExtraSchemas ++ ExtraSchemasAcc, SchemaCTX}
end,
{[], [], CTX},
maps:to_list(Content)
),
{#{<<"anyOf">> => AnyOf}, NewExtraSchemas, NewCTX};
parse_response_body(_Response, CTX) ->
{undefined, [], CTX}.
ResponseSchema = #{<<"anyOf">> => AnyOf},
Response = #{
schema => ResponseSchema,
required => false
},
{Response, ExtraSchemas, NewCTX};
parse_response(_Response, CTX) ->
Response = #{
schema => true,
required => false
},
{Response, [], CTX}.

-spec parse_schema(OAS, CTX) -> Result when
OAS :: oas(),
Expand All @@ -455,6 +470,7 @@ parse_response_body(_Response, CTX) ->
Schema :: erf_parser:schema(),
ExtraSchemas :: [{erf_parser:ref(), erf_parser:schema()}],
NewCTX :: ctx().
%% TODO: parse to new ndto:schema() type
parse_schema(#{<<"$ref">> := Ref}, CTX) ->
{RefResolved, RefOAS, RefCTX} = resolve_ref(Ref, CTX),
{NewSchema, NewExtraSchemas, NewCTX} = parse_schema(RefOAS, RefCTX),
Expand Down
23 changes: 19 additions & 4 deletions src/erf_parser.erl
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,29 @@
id := binary(),
method := method(),
parameters := [parameter()],
request_body := ref(),
request_body := request_body(),
responses := #{
'*' | status_code() := ref()
'*' | status_code() := response()
}
}.
-type parameter() :: #{
ref := ref(),
name := parameter_name(),
type := parameter_type()
type := parameter_type(),
required := boolean()
}.
-type parameter_name() :: binary().
-type parameter_type() :: header | cookie | path | query.
-type path() :: binary().
-type ref() :: binary().
-type request_body() :: #{
ref := ref(),
required := boolean()
}.
-type response() :: #{
ref := ref(),
required := boolean()
}.
-type schema() :: ndto:schema().
-type status_code() :: 100..599.

Expand All @@ -61,8 +70,14 @@
method/0,
operation/0,
parameter/0,
parameter_name/0,
parameter_type/0,
path/0,
ref/0,
schema/0
request_body/0,
response/0,
schema/0,
status_code/0
]).

%%%-----------------------------------------------------------------------------
Expand Down
Loading

0 comments on commit 6304d93

Please sign in to comment.