diff --git a/modules/openapi-generator/src/main/resources/erlang-server/api.mustache b/modules/openapi-generator/src/main/resources/erlang-server/api.mustache index ba8f13854f61..bc6d271dcc92 100644 --- a/modules/openapi-generator/src/main/resources/erlang-server/api.mustache +++ b/modules/openapi-generator/src/main/resources/erlang-server/api.mustache @@ -46,7 +46,7 @@ accept_callback(Class, OperationID, Req, Context) -> -export_type([operation_id/0]). --dialyzer({nowarn_function, [to_binary/1, to_list/1, validate_response_body/4]}). +-dialyzer({nowarn_function, [to_binary/1, validate_response_body/4]}). -type rule() :: {type, binary} | @@ -158,24 +158,31 @@ for the `OperationID` operation. {{/allParams}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}request_param_info(OperationID, Name) -> error({unknown_param, OperationID, Name}). +-spec populate_request_params( + operation_id(), [request_param()], cowboy_req:req(), jesse_state:state(), map()) -> + {ok, map(), cowboy_req:req()} | {error, _, cowboy_req:req()}. populate_request_params(_, [], Req, _, Model) -> {ok, Model, Req}; -populate_request_params(OperationID, [FieldParams | T], Req0, ValidatorState, Model) -> - case populate_request_param(OperationID, FieldParams, Req0, ValidatorState) of - {ok, K, V, Req} -> - populate_request_params(OperationID, T, Req, ValidatorState, maps:put(K, V, Model)); +populate_request_params(OperationID, [ReqParamName | T], Req0, ValidatorState, Model0) -> + case populate_request_param(OperationID, ReqParamName, Req0, ValidatorState) of + {ok, V, Req} -> + Model = maps:put(ReqParamName, V, Model0), + populate_request_params(OperationID, T, Req, ValidatorState, Model); Error -> Error end. -populate_request_param(OperationID, Name, Req0, ValidatorState) -> - #{rules := Rules, source := Source} = request_param_info(OperationID, Name), - case get_value(Source, Name, Req0) of +-spec populate_request_param( + operation_id(), request_param(), cowboy_req:req(), jesse_state:state()) -> + {ok, term(), cowboy_req:req()} | {error, term(), cowboy_req:req()}. +populate_request_param(OperationID, ReqParamName, Req0, ValidatorState) -> + #{rules := Rules, source := Source} = request_param_info(OperationID, ReqParamName), + case get_value(Source, ReqParamName, Req0) of {error, Reason, Req} -> {error, Reason, Req}; {Value, Req} -> - case prepare_param(Rules, Name, Value, ValidatorState) of - {ok, Result} -> {ok, Name, Result, Req}; + case prepare_param(Rules, ReqParamName, Value, ValidatorState) of + {ok, Result} -> {ok, Result, Req}; {error, Reason} -> {error, Reason, Req} end @@ -185,112 +192,111 @@ populate_request_param(OperationID, Name, Req0, ValidatorState) -> validate_response_body(list, ReturnBaseType, Body, ValidatorState) -> [ - validate(schema, ReturnBaseType, Item, ValidatorState) + validate(schema, Item, ReturnBaseType, ValidatorState) || Item <- Body]; validate_response_body(_, ReturnBaseType, Body, ValidatorState) -> - validate(schema, ReturnBaseType, Body, ValidatorState). + validate(schema, Body, ReturnBaseType, ValidatorState). -validate(Rule = required, Name, Value, _ValidatorState) -> - case Value of - undefined -> validation_error(Rule, Name); - _ -> ok - end; -validate(not_required, _Name, _Value, _ValidatorState) -> +-spec validate(rule(), term(), request_param(), jesse_state:state()) -> + ok | {ok, term()}. +validate(required, undefined, ReqParamName, _) -> + validation_error(required, ReqParamName, undefined); +validate(required, _Value, _ReqParamName, _) -> + ok; +validate(not_required, _Value, _ReqParamName, _) -> + ok; +validate(_, undefined, _ReqParamName, _) -> + ok; +validate({type, boolean}, Value, _ReqParamName, _) when is_boolean(Value) -> + {ok, Value}; +validate({type, integer}, Value, _ReqParamName, _) when is_integer(Value) -> + ok; +validate({type, float}, Value, _ReqParamName, _) when is_float(Value) -> ok; -validate(_, _Name, undefined, _ValidatorState) -> +validate({type, binary}, Value, _ReqParamName, _) when is_binary(Value) -> ok; -validate(Rule = {type, integer}, Name, Value, _ValidatorState) -> +validate(Rule = {type, binary}, Value, ReqParamName, _) -> + validation_error(Rule, ReqParamName, Value); +validate(Rule = {type, boolean}, Value, ReqParamName, _) -> + case binary_to_lower(Value) of + <<"true">> -> {ok, true}; + <<"false">> -> {ok, false}; + _ -> validation_error(Rule, ReqParamName, Value) + end; +validate(Rule = {type, integer}, Value, ReqParamName, _) -> try {ok, to_int(Value)} catch error:badarg -> - validation_error(Rule, Name) + validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {type, float}, Name, Value, _ValidatorState) -> +validate(Rule = {type, float}, Value, ReqParamName, _) -> try {ok, to_float(Value)} catch error:badarg -> - validation_error(Rule, Name) - end; -validate(Rule = {type, binary}, Name, Value, _ValidatorState) -> - case is_binary(Value) of - true -> ok; - false -> validation_error(Rule, Name) - end; -validate(_Rule = {type, boolean}, _Name, Value, _ValidatorState) when is_boolean(Value) -> - {ok, Value}; -validate(Rule = {type, boolean}, Name, Value, _ValidatorState) -> - V = binary_to_lower(Value), - try - case binary_to_existing_atom(V, utf8) of - B when is_boolean(B) -> {ok, B}; - _ -> validation_error(Rule, Name) - end - catch - error:badarg -> - validation_error(Rule, Name) + validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {type, date}, Name, Value, _ValidatorState) -> +validate(Rule = {type, date}, Value, ReqParamName, _) -> case is_binary(Value) of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {type, datetime}, Name, Value, _ValidatorState) -> +validate(Rule = {type, datetime}, Value, ReqParamName, _) -> case is_binary(Value) of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {enum, Values}, Name, Value, _ValidatorState) -> +validate(Rule = {enum, Values}, Value, ReqParamName, _) -> try FormattedValue = erlang:binary_to_existing_atom(Value, utf8), case lists:member(FormattedValue, Values) of true -> {ok, FormattedValue}; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end catch error:badarg -> - validation_error(Rule, Name) + validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {max, Max}, Name, Value, _ValidatorState) -> +validate(Rule = {max, Max}, Value, ReqParamName, _) -> case Value =< Max of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {exclusive_max, ExclusiveMax}, Name, Value, _ValidatorState) -> +validate(Rule = {exclusive_max, ExclusiveMax}, Value, ReqParamName, _) -> case Value > ExclusiveMax of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {min, Min}, Name, Value, _ValidatorState) -> +validate(Rule = {min, Min}, Value, ReqParamName, _) -> case Value >= Min of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {exclusive_min, ExclusiveMin}, Name, Value, _ValidatorState) -> +validate(Rule = {exclusive_min, ExclusiveMin}, Value, ReqParamName, _) -> case Value =< ExclusiveMin of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {max_length, MaxLength}, Name, Value, _ValidatorState) -> +validate(Rule = {max_length, MaxLength}, Value, ReqParamName, _) -> case size(Value) =< MaxLength of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {min_length, MinLength}, Name, Value, _ValidatorState) -> +validate(Rule = {min_length, MinLength}, Value, ReqParamName, _) -> case size(Value) >= MinLength of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {pattern, Pattern}, Name, Value, _ValidatorState) -> +validate(Rule = {pattern, Pattern}, Value, ReqParamName, _) -> {ok, MP} = re:compile(Pattern), case re:run(Value, MP) of {match, _} -> ok; - _ -> validation_error(Rule, Name) + _ -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = schema, Name, Value, ValidatorState) -> - Definition = list_to_binary("#/components/schemas/" ++ to_list(Name)), +validate(Rule = schema, Value, ReqParamName, ValidatorState) -> + Definition = iolist_to_binary(["#/components/schemas/", atom_to_binary(ReqParamName)]), try _ = validate_with_schema(Value, Definition, ValidatorState), ok @@ -300,7 +306,7 @@ validate(Rule = schema, Name, Value, ValidatorState) -> type => schema_invalid, error => Error }, - validation_error(Rule, Name, Info); + validation_error(Rule, ReqParamName, Value, Info); throw:[{data_invalid, Schema, Error, _, Path} | _] -> Info = #{ type => data_invalid, @@ -308,25 +314,25 @@ validate(Rule = schema, Name, Value, ValidatorState) -> schema => Schema, path => Path }, - validation_error(Rule, Name, Info) + validation_error(Rule, ReqParamName, Value, Info) end; -validate(Rule, Name, _Value, _ValidatorState) -> - ?LOG_INFO(#{what => "Cannot validate rule", name => Name, rule => Rule}), +validate(Rule, _Value, ReqParamName, _) -> + ?LOG_INFO(#{what => "Cannot validate rule", name => ReqParamName, rule => Rule}), error({unknown_validation_rule, Rule}). --spec validation_error(Rule :: any(), Name :: any()) -> no_return(). -validation_error(ViolatedRule, Name) -> - validation_error(ViolatedRule, Name, #{}). +-spec validation_error(rule(), request_param(), term()) -> no_return(). +validation_error(ViolatedRule, Name, Value) -> + validation_error(ViolatedRule, Name, Value, #{}). --spec validation_error(Rule :: any(), Name :: any(), Info :: #{_ := _}) -> no_return(). -validation_error(ViolatedRule, Name, Info) -> - throw({wrong_param, Name, ViolatedRule, Info}). +-spec validation_error(rule(), request_param(), term(), Info :: #{_ := _}) -> no_return(). +validation_error(ViolatedRule, Name, Value, Info) -> + throw({wrong_param, Name, Value, ViolatedRule, Info}). --spec get_value(body | qs_val | header | binding, Name :: any(), Req0 :: cowboy_req:req()) -> - {Value :: any(), Req :: cowboy_req:req()} | - {error, Reason :: any(), Req :: cowboy_req:req()}. +-spec get_value(body | qs_val | header | binding, request_param(), cowboy_req:req()) -> + {any(), cowboy_req:req()} | + {error, any(), cowboy_req:req()}. get_value(body, _Name, Req0) -> - {ok, Body, Req} = cowboy_req:read_body(Req0), + {ok, Body, Req} = read_entire_body(Req0), case prepare_body(Body) of {error, Reason} -> {error, Reason, Req}; @@ -342,17 +348,30 @@ get_value(header, Name, Req) -> Value = maps:get(to_header(Name), Headers, undefined), {Value, Req}; get_value(binding, Name, Req) -> - Value = cowboy_req:binding(to_binding(Name), Req), + Value = cowboy_req:binding(Name, Req), {Value, Req}. +-spec read_entire_body(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}. +read_entire_body(Req) -> + read_entire_body(Req, []). + +-spec read_entire_body(cowboy_req:req(), iodata()) -> {ok, binary(), cowboy_req:req()}. +read_entire_body(Request, Acc) -> % { + case cowboy_req:read_body(Request) of + {ok, Data, NewRequest} -> + {ok, iolist_to_binary(lists:reverse([Data | Acc])), NewRequest}; + {more, Data, NewRequest} -> + read_entire_body(NewRequest, [Data | Acc]) + end. + prepare_body(<<>>) -> <<>>; prepare_body(Body) -> try json:decode(Body) catch - error:_ -> - {error, {invalid_body, not_json, Body}} + error:Error -> + {error, {invalid_json, Body, Error}} end. validate_with_schema(Body, Definition, ValidatorState) -> @@ -362,18 +381,17 @@ validate_with_schema(Body, Definition, ValidatorState) -> ValidatorState ). -prepare_param(Rules, Name, Value, ValidatorState) -> +-spec prepare_param([rule()], request_param(), term(), jesse_state:state()) -> + {ok, term()} | {error, Reason :: any()}. +prepare_param(Rules, ReqParamName, Value, ValidatorState) -> + Fun = fun(Rule, Acc) -> + case validate(Rule, Acc, ReqParamName, ValidatorState) of + ok -> Acc; + {ok, Prepared} -> Prepared + end + end, try - Result = lists:foldl( - fun(Rule, Acc) -> - case validate(Rule, Name, Acc, ValidatorState) of - ok -> Acc; - {ok, Prepared} -> Prepared - end - end, - Value, - Rules - ), + Result = lists:foldl(Fun, Value, Rules), {ok, Result} catch throw:Reason -> @@ -387,40 +405,28 @@ to_binary(V) when is_atom(V) -> atom_to_binary(V, utf8); to_binary(V) when is_integer(V) -> integer_to_binary(V); to_binary(V) when is_float(V) -> float_to_binary(V). --spec to_list(iodata() | atom() | number()) -> binary(). -to_list(V) when is_list(V) -> V; -to_list(V) when is_binary(V) -> binary_to_list(V); -to_list(V) when is_atom(V) -> atom_to_list(V); -to_list(V) when is_integer(V) -> integer_to_list(V); -to_list(V) when is_float(V) -> float_to_list(V). +-spec to_float(binary() | list()) -> integer(). +to_float(Data) when is_binary(Data) -> + binary_to_float(Data); +to_float(Data) when is_list(Data) -> + list_to_float(Data). --spec to_float(iodata()) -> float(). -to_float(V) -> - binary_to_float(iolist_to_binary([V])). - --spec to_int(integer() | binary() | list()) -> integer(). -to_int(Data) when is_integer(Data) -> - Data; +-spec to_int(binary() | list()) -> integer(). to_int(Data) when is_binary(Data) -> binary_to_integer(Data); to_int(Data) when is_list(Data) -> list_to_integer(Data). --spec to_header(iodata() | atom() | number()) -> binary(). +-spec to_header(request_param()) -> binary(). to_header(Name) -> - to_binary(string:lowercase(to_binary(Name))). + to_binary(string:lowercase(atom_to_binary(Name, utf8))). binary_to_lower(V) when is_binary(V) -> string:lowercase(V). --spec to_qs(iodata() | atom() | number()) -> binary(). +-spec to_qs(request_param()) -> binary(). to_qs(Name) -> - to_binary(Name). - --spec to_binding(iodata() | atom() | number()) -> atom(). -to_binding(Name) -> - Prepared = to_binary(Name), - binary_to_existing_atom(Prepared, utf8). + atom_to_binary(Name, utf8). -spec get_opt(any(), []) -> any(). get_opt(Key, Opts) -> diff --git a/modules/openapi-generator/src/main/resources/erlang-server/handler.mustache b/modules/openapi-generator/src/main/resources/erlang-server/handler.mustache index c4de6d8e15c9..74234d38286c 100644 --- a/modules/openapi-generator/src/main/resources/erlang-server/handler.mustache +++ b/modules/openapi-generator/src/main/resources/erlang-server/handler.mustache @@ -1,5 +1,11 @@ -%% basic handler -module({{classname}}). +-moduledoc """ +Exposes the following operation IDs: +{{#operations}}{{#operation}} +- `{{httpMethod}}` to `{{path}}`, OperationId: `{{operationId}}`: +{{summary}}. +{{/operation}}{{/operations}} +""". -behaviour(cowboy_rest). diff --git a/modules/openapi-generator/src/main/resources/erlang-server/router.mustache b/modules/openapi-generator/src/main/resources/erlang-server/router.mustache index 2bc70bdac73c..70f54eeba0d6 100644 --- a/modules/openapi-generator/src/main/resources/erlang-server/router.mustache +++ b/modules/openapi-generator/src/main/resources/erlang-server/router.mustache @@ -19,23 +19,38 @@ get_paths(LogicHandler) -> group_paths() -> maps:fold( - fun(OperationID, #{path := Path, method := Method, handler := Handler}, Acc) -> - case maps:find(Path, Acc) of + fun(OperationID, #{servers := Servers, base_path := BasePath, path := Path, + method := Method, handler := Handler}, Acc) -> + FullPaths = build_full_paths(Servers, BasePath, Path), + merge_paths(FullPaths, OperationID, Method, Handler, Acc) + end, #{}, get_operations()). + +build_full_paths([], BasePath, Path) -> + [lists:append([BasePath, Path])]; +build_full_paths(Servers, _BasePath, Path) -> + [lists:append([Server, Path]) || Server <- Servers ]. + +merge_paths(FullPaths, OperationID, Method, Handler, Acc) -> + lists:foldl( + fun(Path, Acc0) -> + case maps:find(Path, Acc0) of {ok, PathInfo0 = #{operations := Operations0}} -> Operations = Operations0#{Method => OperationID}, PathInfo = PathInfo0#{operations => Operations}, - Acc#{Path => PathInfo}; + Acc0#{Path => PathInfo}; error -> Operations = #{Method => OperationID}, PathInfo = #{handler => Handler, operations => Operations}, - Acc#{Path => PathInfo} + Acc0#{Path => PathInfo} end - end, #{}, get_operations()). + end, Acc, FullPaths). get_operations() -> #{ {{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} - '{{operationId}}' => #{ - path => "{{{basePathWithoutHost}}}{{{path}}}", + '{{operationId}}' => #{ + servers => [{{#servers}}"{{{url}}}"{{^-last}},{{/-last}}{{/servers}}], + base_path => "{{{basePathWithoutHost}}}", + path => "{{{path}}}", method => <<"{{httpMethod}}">>, handler => '{{classname}}' }{{^-last}},{{/-last}}{{/operation}}{{^-last}},{{/-last}}{{/operations}}{{/apis}}{{/apiInfo}} diff --git a/modules/openapi-generator/src/main/resources/erlang-server/server.mustache b/modules/openapi-generator/src/main/resources/erlang-server/server.mustache index 43ed54e5d230..86b4654a02a1 100644 --- a/modules/openapi-generator/src/main/resources/erlang-server/server.mustache +++ b/modules/openapi-generator/src/main/resources/erlang-server/server.mustache @@ -1,4 +1,9 @@ -module({{packageName}}_server). +{{#appDescription}} +-moduledoc """ +{{{appDescription}}} +""". +{{/appDescription}} -define(DEFAULT_LOGIC_HANDLER, {{packageName}}_logic_handler). diff --git a/samples/server/echo_api/erlang-server/src/openapi_api.erl b/samples/server/echo_api/erlang-server/src/openapi_api.erl index b796f2df4a36..250db0d65837 100644 --- a/samples/server/echo_api/erlang-server/src/openapi_api.erl +++ b/samples/server/echo_api/erlang-server/src/openapi_api.erl @@ -46,7 +46,7 @@ accept_callback(Class, OperationID, Req, Context) -> -export_type([operation_id/0]). --dialyzer({nowarn_function, [to_binary/1, to_list/1, validate_response_body/4]}). +-dialyzer({nowarn_function, [to_binary/1, validate_response_body/4]}). -type rule() :: {type, binary} | @@ -637,24 +637,31 @@ request_param_info('TestQueryStyleFormExplodeTrueObjectAllOf', 'query_object') - request_param_info(OperationID, Name) -> error({unknown_param, OperationID, Name}). +-spec populate_request_params( + operation_id(), [request_param()], cowboy_req:req(), jesse_state:state(), map()) -> + {ok, map(), cowboy_req:req()} | {error, _, cowboy_req:req()}. populate_request_params(_, [], Req, _, Model) -> {ok, Model, Req}; -populate_request_params(OperationID, [FieldParams | T], Req0, ValidatorState, Model) -> - case populate_request_param(OperationID, FieldParams, Req0, ValidatorState) of - {ok, K, V, Req} -> - populate_request_params(OperationID, T, Req, ValidatorState, maps:put(K, V, Model)); +populate_request_params(OperationID, [ReqParamName | T], Req0, ValidatorState, Model0) -> + case populate_request_param(OperationID, ReqParamName, Req0, ValidatorState) of + {ok, V, Req} -> + Model = maps:put(ReqParamName, V, Model0), + populate_request_params(OperationID, T, Req, ValidatorState, Model); Error -> Error end. -populate_request_param(OperationID, Name, Req0, ValidatorState) -> - #{rules := Rules, source := Source} = request_param_info(OperationID, Name), - case get_value(Source, Name, Req0) of +-spec populate_request_param( + operation_id(), request_param(), cowboy_req:req(), jesse_state:state()) -> + {ok, term(), cowboy_req:req()} | {error, term(), cowboy_req:req()}. +populate_request_param(OperationID, ReqParamName, Req0, ValidatorState) -> + #{rules := Rules, source := Source} = request_param_info(OperationID, ReqParamName), + case get_value(Source, ReqParamName, Req0) of {error, Reason, Req} -> {error, Reason, Req}; {Value, Req} -> - case prepare_param(Rules, Name, Value, ValidatorState) of - {ok, Result} -> {ok, Name, Result, Req}; + case prepare_param(Rules, ReqParamName, Value, ValidatorState) of + {ok, Result} -> {ok, Result, Req}; {error, Reason} -> {error, Reason, Req} end @@ -664,112 +671,111 @@ populate_request_param(OperationID, Name, Req0, ValidatorState) -> validate_response_body(list, ReturnBaseType, Body, ValidatorState) -> [ - validate(schema, ReturnBaseType, Item, ValidatorState) + validate(schema, Item, ReturnBaseType, ValidatorState) || Item <- Body]; validate_response_body(_, ReturnBaseType, Body, ValidatorState) -> - validate(schema, ReturnBaseType, Body, ValidatorState). + validate(schema, Body, ReturnBaseType, ValidatorState). -validate(Rule = required, Name, Value, _ValidatorState) -> - case Value of - undefined -> validation_error(Rule, Name); - _ -> ok - end; -validate(not_required, _Name, _Value, _ValidatorState) -> +-spec validate(rule(), term(), request_param(), jesse_state:state()) -> + ok | {ok, term()}. +validate(required, undefined, ReqParamName, _) -> + validation_error(required, ReqParamName, undefined); +validate(required, _Value, _ReqParamName, _) -> + ok; +validate(not_required, _Value, _ReqParamName, _) -> + ok; +validate(_, undefined, _ReqParamName, _) -> + ok; +validate({type, boolean}, Value, _ReqParamName, _) when is_boolean(Value) -> + {ok, Value}; +validate({type, integer}, Value, _ReqParamName, _) when is_integer(Value) -> + ok; +validate({type, float}, Value, _ReqParamName, _) when is_float(Value) -> ok; -validate(_, _Name, undefined, _ValidatorState) -> +validate({type, binary}, Value, _ReqParamName, _) when is_binary(Value) -> ok; -validate(Rule = {type, integer}, Name, Value, _ValidatorState) -> +validate(Rule = {type, binary}, Value, ReqParamName, _) -> + validation_error(Rule, ReqParamName, Value); +validate(Rule = {type, boolean}, Value, ReqParamName, _) -> + case binary_to_lower(Value) of + <<"true">> -> {ok, true}; + <<"false">> -> {ok, false}; + _ -> validation_error(Rule, ReqParamName, Value) + end; +validate(Rule = {type, integer}, Value, ReqParamName, _) -> try {ok, to_int(Value)} catch error:badarg -> - validation_error(Rule, Name) + validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {type, float}, Name, Value, _ValidatorState) -> +validate(Rule = {type, float}, Value, ReqParamName, _) -> try {ok, to_float(Value)} catch error:badarg -> - validation_error(Rule, Name) - end; -validate(Rule = {type, binary}, Name, Value, _ValidatorState) -> - case is_binary(Value) of - true -> ok; - false -> validation_error(Rule, Name) - end; -validate(_Rule = {type, boolean}, _Name, Value, _ValidatorState) when is_boolean(Value) -> - {ok, Value}; -validate(Rule = {type, boolean}, Name, Value, _ValidatorState) -> - V = binary_to_lower(Value), - try - case binary_to_existing_atom(V, utf8) of - B when is_boolean(B) -> {ok, B}; - _ -> validation_error(Rule, Name) - end - catch - error:badarg -> - validation_error(Rule, Name) + validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {type, date}, Name, Value, _ValidatorState) -> +validate(Rule = {type, date}, Value, ReqParamName, _) -> case is_binary(Value) of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {type, datetime}, Name, Value, _ValidatorState) -> +validate(Rule = {type, datetime}, Value, ReqParamName, _) -> case is_binary(Value) of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {enum, Values}, Name, Value, _ValidatorState) -> +validate(Rule = {enum, Values}, Value, ReqParamName, _) -> try FormattedValue = erlang:binary_to_existing_atom(Value, utf8), case lists:member(FormattedValue, Values) of true -> {ok, FormattedValue}; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end catch error:badarg -> - validation_error(Rule, Name) + validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {max, Max}, Name, Value, _ValidatorState) -> +validate(Rule = {max, Max}, Value, ReqParamName, _) -> case Value =< Max of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {exclusive_max, ExclusiveMax}, Name, Value, _ValidatorState) -> +validate(Rule = {exclusive_max, ExclusiveMax}, Value, ReqParamName, _) -> case Value > ExclusiveMax of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {min, Min}, Name, Value, _ValidatorState) -> +validate(Rule = {min, Min}, Value, ReqParamName, _) -> case Value >= Min of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {exclusive_min, ExclusiveMin}, Name, Value, _ValidatorState) -> +validate(Rule = {exclusive_min, ExclusiveMin}, Value, ReqParamName, _) -> case Value =< ExclusiveMin of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {max_length, MaxLength}, Name, Value, _ValidatorState) -> +validate(Rule = {max_length, MaxLength}, Value, ReqParamName, _) -> case size(Value) =< MaxLength of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {min_length, MinLength}, Name, Value, _ValidatorState) -> +validate(Rule = {min_length, MinLength}, Value, ReqParamName, _) -> case size(Value) >= MinLength of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {pattern, Pattern}, Name, Value, _ValidatorState) -> +validate(Rule = {pattern, Pattern}, Value, ReqParamName, _) -> {ok, MP} = re:compile(Pattern), case re:run(Value, MP) of {match, _} -> ok; - _ -> validation_error(Rule, Name) + _ -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = schema, Name, Value, ValidatorState) -> - Definition = list_to_binary("#/components/schemas/" ++ to_list(Name)), +validate(Rule = schema, Value, ReqParamName, ValidatorState) -> + Definition = iolist_to_binary(["#/components/schemas/", atom_to_binary(ReqParamName)]), try _ = validate_with_schema(Value, Definition, ValidatorState), ok @@ -779,7 +785,7 @@ validate(Rule = schema, Name, Value, ValidatorState) -> type => schema_invalid, error => Error }, - validation_error(Rule, Name, Info); + validation_error(Rule, ReqParamName, Value, Info); throw:[{data_invalid, Schema, Error, _, Path} | _] -> Info = #{ type => data_invalid, @@ -787,25 +793,25 @@ validate(Rule = schema, Name, Value, ValidatorState) -> schema => Schema, path => Path }, - validation_error(Rule, Name, Info) + validation_error(Rule, ReqParamName, Value, Info) end; -validate(Rule, Name, _Value, _ValidatorState) -> - ?LOG_INFO(#{what => "Cannot validate rule", name => Name, rule => Rule}), +validate(Rule, _Value, ReqParamName, _) -> + ?LOG_INFO(#{what => "Cannot validate rule", name => ReqParamName, rule => Rule}), error({unknown_validation_rule, Rule}). --spec validation_error(Rule :: any(), Name :: any()) -> no_return(). -validation_error(ViolatedRule, Name) -> - validation_error(ViolatedRule, Name, #{}). +-spec validation_error(rule(), request_param(), term()) -> no_return(). +validation_error(ViolatedRule, Name, Value) -> + validation_error(ViolatedRule, Name, Value, #{}). --spec validation_error(Rule :: any(), Name :: any(), Info :: #{_ := _}) -> no_return(). -validation_error(ViolatedRule, Name, Info) -> - throw({wrong_param, Name, ViolatedRule, Info}). +-spec validation_error(rule(), request_param(), term(), Info :: #{_ := _}) -> no_return(). +validation_error(ViolatedRule, Name, Value, Info) -> + throw({wrong_param, Name, Value, ViolatedRule, Info}). --spec get_value(body | qs_val | header | binding, Name :: any(), Req0 :: cowboy_req:req()) -> - {Value :: any(), Req :: cowboy_req:req()} | - {error, Reason :: any(), Req :: cowboy_req:req()}. +-spec get_value(body | qs_val | header | binding, request_param(), cowboy_req:req()) -> + {any(), cowboy_req:req()} | + {error, any(), cowboy_req:req()}. get_value(body, _Name, Req0) -> - {ok, Body, Req} = cowboy_req:read_body(Req0), + {ok, Body, Req} = read_entire_body(Req0), case prepare_body(Body) of {error, Reason} -> {error, Reason, Req}; @@ -821,17 +827,30 @@ get_value(header, Name, Req) -> Value = maps:get(to_header(Name), Headers, undefined), {Value, Req}; get_value(binding, Name, Req) -> - Value = cowboy_req:binding(to_binding(Name), Req), + Value = cowboy_req:binding(Name, Req), {Value, Req}. +-spec read_entire_body(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}. +read_entire_body(Req) -> + read_entire_body(Req, []). + +-spec read_entire_body(cowboy_req:req(), iodata()) -> {ok, binary(), cowboy_req:req()}. +read_entire_body(Request, Acc) -> % { + case cowboy_req:read_body(Request) of + {ok, Data, NewRequest} -> + {ok, iolist_to_binary(lists:reverse([Data | Acc])), NewRequest}; + {more, Data, NewRequest} -> + read_entire_body(NewRequest, [Data | Acc]) + end. + prepare_body(<<>>) -> <<>>; prepare_body(Body) -> try json:decode(Body) catch - error:_ -> - {error, {invalid_body, not_json, Body}} + error:Error -> + {error, {invalid_json, Body, Error}} end. validate_with_schema(Body, Definition, ValidatorState) -> @@ -841,18 +860,17 @@ validate_with_schema(Body, Definition, ValidatorState) -> ValidatorState ). -prepare_param(Rules, Name, Value, ValidatorState) -> +-spec prepare_param([rule()], request_param(), term(), jesse_state:state()) -> + {ok, term()} | {error, Reason :: any()}. +prepare_param(Rules, ReqParamName, Value, ValidatorState) -> + Fun = fun(Rule, Acc) -> + case validate(Rule, Acc, ReqParamName, ValidatorState) of + ok -> Acc; + {ok, Prepared} -> Prepared + end + end, try - Result = lists:foldl( - fun(Rule, Acc) -> - case validate(Rule, Name, Acc, ValidatorState) of - ok -> Acc; - {ok, Prepared} -> Prepared - end - end, - Value, - Rules - ), + Result = lists:foldl(Fun, Value, Rules), {ok, Result} catch throw:Reason -> @@ -866,40 +884,28 @@ to_binary(V) when is_atom(V) -> atom_to_binary(V, utf8); to_binary(V) when is_integer(V) -> integer_to_binary(V); to_binary(V) when is_float(V) -> float_to_binary(V). --spec to_list(iodata() | atom() | number()) -> binary(). -to_list(V) when is_list(V) -> V; -to_list(V) when is_binary(V) -> binary_to_list(V); -to_list(V) when is_atom(V) -> atom_to_list(V); -to_list(V) when is_integer(V) -> integer_to_list(V); -to_list(V) when is_float(V) -> float_to_list(V). +-spec to_float(binary() | list()) -> integer(). +to_float(Data) when is_binary(Data) -> + binary_to_float(Data); +to_float(Data) when is_list(Data) -> + list_to_float(Data). --spec to_float(iodata()) -> float(). -to_float(V) -> - binary_to_float(iolist_to_binary([V])). - --spec to_int(integer() | binary() | list()) -> integer(). -to_int(Data) when is_integer(Data) -> - Data; +-spec to_int(binary() | list()) -> integer(). to_int(Data) when is_binary(Data) -> binary_to_integer(Data); to_int(Data) when is_list(Data) -> list_to_integer(Data). --spec to_header(iodata() | atom() | number()) -> binary(). +-spec to_header(request_param()) -> binary(). to_header(Name) -> - to_binary(string:lowercase(to_binary(Name))). + to_binary(string:lowercase(atom_to_binary(Name, utf8))). binary_to_lower(V) when is_binary(V) -> string:lowercase(V). --spec to_qs(iodata() | atom() | number()) -> binary(). +-spec to_qs(request_param()) -> binary(). to_qs(Name) -> - to_binary(Name). - --spec to_binding(iodata() | atom() | number()) -> atom(). -to_binding(Name) -> - Prepared = to_binary(Name), - binary_to_existing_atom(Prepared, utf8). + atom_to_binary(Name, utf8). -spec get_opt(any(), []) -> any(). get_opt(Key, Opts) -> diff --git a/samples/server/echo_api/erlang-server/src/openapi_auth_handler.erl b/samples/server/echo_api/erlang-server/src/openapi_auth_handler.erl index d0d456a7f2bf..5ffd01376f84 100644 --- a/samples/server/echo_api/erlang-server/src/openapi_auth_handler.erl +++ b/samples/server/echo_api/erlang-server/src/openapi_auth_handler.erl @@ -1,5 +1,14 @@ -%% basic handler -module(openapi_auth_handler). +-moduledoc """ +Exposes the following operation IDs: + +- `POST` to `/auth/http/basic`, OperationId: `TestAuthHttpBasic`: +To test HTTP basic authentication. + +- `POST` to `/auth/http/bearer`, OperationId: `TestAuthHttpBearer`: +To test HTTP bearer authentication. + +""". -behaviour(cowboy_rest). diff --git a/samples/server/echo_api/erlang-server/src/openapi_body_handler.erl b/samples/server/echo_api/erlang-server/src/openapi_body_handler.erl index 72e5a3685932..ff3c7391d9c2 100644 --- a/samples/server/echo_api/erlang-server/src/openapi_body_handler.erl +++ b/samples/server/echo_api/erlang-server/src/openapi_body_handler.erl @@ -1,5 +1,38 @@ -%% basic handler -module(openapi_body_handler). +-moduledoc """ +Exposes the following operation IDs: + +- `POST` to `/binary/gif`, OperationId: `TestBinaryGif`: +Test binary (gif) response body. + +- `POST` to `/body/application/octetstream/binary`, OperationId: `TestBodyApplicationOctetstreamBinary`: +Test body parameter(s). + +- `POST` to `/body/application/octetstream/array_of_binary`, OperationId: `TestBodyMultipartFormdataArrayOfBinary`: +Test array of binary in multipart mime. + +- `POST` to `/body/application/octetstream/single_binary`, OperationId: `TestBodyMultipartFormdataSingleBinary`: +Test single binary in multipart mime. + +- `POST` to `/echo/body/allOf/Pet`, OperationId: `TestEchoBodyAllOfPet`: +Test body parameter(s). + +- `POST` to `/echo/body/FreeFormObject/response_string`, OperationId: `TestEchoBodyFreeFormObjectResponseString`: +Test free form object. + +- `POST` to `/echo/body/Pet`, OperationId: `TestEchoBodyPet`: +Test body parameter(s). + +- `POST` to `/echo/body/Pet/response_string`, OperationId: `TestEchoBodyPetResponseString`: +Test empty response body. + +- `POST` to `/echo/body/string_enum`, OperationId: `TestEchoBodyStringEnum`: +Test string enum response body. + +- `POST` to `/echo/body/Tag/response_string`, OperationId: `TestEchoBodyTagResponseString`: +Test empty json (request body). + +""". -behaviour(cowboy_rest). diff --git a/samples/server/echo_api/erlang-server/src/openapi_form_handler.erl b/samples/server/echo_api/erlang-server/src/openapi_form_handler.erl index 721ae35d798c..04b6eb863052 100644 --- a/samples/server/echo_api/erlang-server/src/openapi_form_handler.erl +++ b/samples/server/echo_api/erlang-server/src/openapi_form_handler.erl @@ -1,5 +1,17 @@ -%% basic handler -module(openapi_form_handler). +-moduledoc """ +Exposes the following operation IDs: + +- `POST` to `/form/integer/boolean/string`, OperationId: `TestFormIntegerBooleanString`: +Test form parameter(s). + +- `POST` to `/form/object/multipart`, OperationId: `TestFormObjectMultipart`: +Test form parameter(s) for multipart schema. + +- `POST` to `/form/oneof`, OperationId: `TestFormOneof`: +Test form parameter(s) for oneOf schema. + +""". -behaviour(cowboy_rest). diff --git a/samples/server/echo_api/erlang-server/src/openapi_header_handler.erl b/samples/server/echo_api/erlang-server/src/openapi_header_handler.erl index 0508e765aea8..1e270abf3de2 100644 --- a/samples/server/echo_api/erlang-server/src/openapi_header_handler.erl +++ b/samples/server/echo_api/erlang-server/src/openapi_header_handler.erl @@ -1,5 +1,11 @@ -%% basic handler -module(openapi_header_handler). +-moduledoc """ +Exposes the following operation IDs: + +- `GET` to `/header/integer/boolean/string/enums`, OperationId: `TestHeaderIntegerBooleanStringEnums`: +Test header parameter(s). + +""". -behaviour(cowboy_rest). diff --git a/samples/server/echo_api/erlang-server/src/openapi_path_handler.erl b/samples/server/echo_api/erlang-server/src/openapi_path_handler.erl index c80fbd508ac9..f81131ec7f16 100644 --- a/samples/server/echo_api/erlang-server/src/openapi_path_handler.erl +++ b/samples/server/echo_api/erlang-server/src/openapi_path_handler.erl @@ -1,5 +1,11 @@ -%% basic handler -module(openapi_path_handler). +-moduledoc """ +Exposes the following operation IDs: + +- `GET` to `/path/string/:path_string/integer/:path_integer/:enum_nonref_string_path/:enum_ref_string_path`, OperationId: `TestsPathString{pathString}Integer{pathInteger}{enumNonrefStringPath}{enumRefStringPath}`: +Test path parameter(s). + +""". -behaviour(cowboy_rest). diff --git a/samples/server/echo_api/erlang-server/src/openapi_query_handler.erl b/samples/server/echo_api/erlang-server/src/openapi_query_handler.erl index fd298dc1ee8b..d6beabe02917 100644 --- a/samples/server/echo_api/erlang-server/src/openapi_query_handler.erl +++ b/samples/server/echo_api/erlang-server/src/openapi_query_handler.erl @@ -1,5 +1,38 @@ -%% basic handler -module(openapi_query_handler). +-moduledoc """ +Exposes the following operation IDs: + +- `GET` to `/query/enum_ref_string`, OperationId: `TestEnumRefString`: +Test query parameter(s). + +- `GET` to `/query/datetime/date/string`, OperationId: `TestQueryDatetimeDateString`: +Test query parameter(s). + +- `GET` to `/query/integer/boolean/string`, OperationId: `TestQueryIntegerBooleanString`: +Test query parameter(s). + +- `GET` to `/query/style_deepObject/explode_true/object`, OperationId: `TestQueryStyleDeepObjectExplodeTrueObject`: +Test query parameter(s). + +- `GET` to `/query/style_deepObject/explode_true/object/allOf`, OperationId: `TestQueryStyleDeepObjectExplodeTrueObjectAllOf`: +Test query parameter(s). + +- `GET` to `/query/style_form/explode_false/array_integer`, OperationId: `TestQueryStyleFormExplodeFalseArrayInteger`: +Test query parameter(s). + +- `GET` to `/query/style_form/explode_false/array_string`, OperationId: `TestQueryStyleFormExplodeFalseArrayString`: +Test query parameter(s). + +- `GET` to `/query/style_form/explode_true/array_string`, OperationId: `TestQueryStyleFormExplodeTrueArrayString`: +Test query parameter(s). + +- `GET` to `/query/style_form/explode_true/object`, OperationId: `TestQueryStyleFormExplodeTrueObject`: +Test query parameter(s). + +- `GET` to `/query/style_form/explode_true/object/allOf`, OperationId: `TestQueryStyleFormExplodeTrueObjectAllOf`: +Test query parameter(s). + +""". -behaviour(cowboy_rest). diff --git a/samples/server/echo_api/erlang-server/src/openapi_router.erl b/samples/server/echo_api/erlang-server/src/openapi_router.erl index 84cee5a6256d..1eabc420556f 100644 --- a/samples/server/echo_api/erlang-server/src/openapi_router.erl +++ b/samples/server/echo_api/erlang-server/src/openapi_router.erl @@ -19,152 +19,219 @@ get_paths(LogicHandler) -> group_paths() -> maps:fold( - fun(OperationID, #{path := Path, method := Method, handler := Handler}, Acc) -> - case maps:find(Path, Acc) of + fun(OperationID, #{servers := Servers, base_path := BasePath, path := Path, + method := Method, handler := Handler}, Acc) -> + FullPaths = build_full_paths(Servers, BasePath, Path), + merge_paths(FullPaths, OperationID, Method, Handler, Acc) + end, #{}, get_operations()). + +build_full_paths([], BasePath, Path) -> + [lists:append([BasePath, Path])]; +build_full_paths(Servers, _BasePath, Path) -> + [lists:append([Server, Path]) || Server <- Servers ]. + +merge_paths(FullPaths, OperationID, Method, Handler, Acc) -> + lists:foldl( + fun(Path, Acc0) -> + case maps:find(Path, Acc0) of {ok, PathInfo0 = #{operations := Operations0}} -> Operations = Operations0#{Method => OperationID}, PathInfo = PathInfo0#{operations => Operations}, - Acc#{Path => PathInfo}; + Acc0#{Path => PathInfo}; error -> Operations = #{Method => OperationID}, PathInfo = #{handler => Handler, operations => Operations}, - Acc#{Path => PathInfo} + Acc0#{Path => PathInfo} end - end, #{}, get_operations()). + end, Acc, FullPaths). get_operations() -> #{ - 'TestAuthHttpBasic' => #{ + 'TestAuthHttpBasic' => #{ + servers => [], + base_path => "", path => "/auth/http/basic", method => <<"POST">>, handler => 'openapi_auth_handler' }, - 'TestAuthHttpBearer' => #{ + 'TestAuthHttpBearer' => #{ + servers => [], + base_path => "", path => "/auth/http/bearer", method => <<"POST">>, handler => 'openapi_auth_handler' }, - 'TestBinaryGif' => #{ + 'TestBinaryGif' => #{ + servers => [], + base_path => "", path => "/binary/gif", method => <<"POST">>, handler => 'openapi_body_handler' }, - 'TestBodyApplicationOctetstreamBinary' => #{ + 'TestBodyApplicationOctetstreamBinary' => #{ + servers => [], + base_path => "", path => "/body/application/octetstream/binary", method => <<"POST">>, handler => 'openapi_body_handler' }, - 'TestBodyMultipartFormdataArrayOfBinary' => #{ + 'TestBodyMultipartFormdataArrayOfBinary' => #{ + servers => [], + base_path => "", path => "/body/application/octetstream/array_of_binary", method => <<"POST">>, handler => 'openapi_body_handler' }, - 'TestBodyMultipartFormdataSingleBinary' => #{ + 'TestBodyMultipartFormdataSingleBinary' => #{ + servers => [], + base_path => "", path => "/body/application/octetstream/single_binary", method => <<"POST">>, handler => 'openapi_body_handler' }, - 'TestEchoBodyAllOfPet' => #{ + 'TestEchoBodyAllOfPet' => #{ + servers => [], + base_path => "", path => "/echo/body/allOf/Pet", method => <<"POST">>, handler => 'openapi_body_handler' }, - 'TestEchoBodyFreeFormObjectResponseString' => #{ + 'TestEchoBodyFreeFormObjectResponseString' => #{ + servers => [], + base_path => "", path => "/echo/body/FreeFormObject/response_string", method => <<"POST">>, handler => 'openapi_body_handler' }, - 'TestEchoBodyPet' => #{ + 'TestEchoBodyPet' => #{ + servers => [], + base_path => "", path => "/echo/body/Pet", method => <<"POST">>, handler => 'openapi_body_handler' }, - 'TestEchoBodyPetResponseString' => #{ + 'TestEchoBodyPetResponseString' => #{ + servers => [], + base_path => "", path => "/echo/body/Pet/response_string", method => <<"POST">>, handler => 'openapi_body_handler' }, - 'TestEchoBodyStringEnum' => #{ + 'TestEchoBodyStringEnum' => #{ + servers => [], + base_path => "", path => "/echo/body/string_enum", method => <<"POST">>, handler => 'openapi_body_handler' }, - 'TestEchoBodyTagResponseString' => #{ + 'TestEchoBodyTagResponseString' => #{ + servers => [], + base_path => "", path => "/echo/body/Tag/response_string", method => <<"POST">>, handler => 'openapi_body_handler' }, - 'TestFormIntegerBooleanString' => #{ + 'TestFormIntegerBooleanString' => #{ + servers => [], + base_path => "", path => "/form/integer/boolean/string", method => <<"POST">>, handler => 'openapi_form_handler' }, - 'TestFormObjectMultipart' => #{ + 'TestFormObjectMultipart' => #{ + servers => [], + base_path => "", path => "/form/object/multipart", method => <<"POST">>, handler => 'openapi_form_handler' }, - 'TestFormOneof' => #{ + 'TestFormOneof' => #{ + servers => [], + base_path => "", path => "/form/oneof", method => <<"POST">>, handler => 'openapi_form_handler' }, - 'TestHeaderIntegerBooleanStringEnums' => #{ + 'TestHeaderIntegerBooleanStringEnums' => #{ + servers => [], + base_path => "", path => "/header/integer/boolean/string/enums", method => <<"GET">>, handler => 'openapi_header_handler' }, - 'TestsPathString{pathString}Integer{pathInteger}{enumNonrefStringPath}{enumRefStringPath}' => #{ + 'TestsPathString{pathString}Integer{pathInteger}{enumNonrefStringPath}{enumRefStringPath}' => #{ + servers => [], + base_path => "", path => "/path/string/:path_string/integer/:path_integer/:enum_nonref_string_path/:enum_ref_string_path", method => <<"GET">>, handler => 'openapi_path_handler' }, - 'TestEnumRefString' => #{ + 'TestEnumRefString' => #{ + servers => [], + base_path => "", path => "/query/enum_ref_string", method => <<"GET">>, handler => 'openapi_query_handler' }, - 'TestQueryDatetimeDateString' => #{ + 'TestQueryDatetimeDateString' => #{ + servers => [], + base_path => "", path => "/query/datetime/date/string", method => <<"GET">>, handler => 'openapi_query_handler' }, - 'TestQueryIntegerBooleanString' => #{ + 'TestQueryIntegerBooleanString' => #{ + servers => [], + base_path => "", path => "/query/integer/boolean/string", method => <<"GET">>, handler => 'openapi_query_handler' }, - 'TestQueryStyleDeepObjectExplodeTrueObject' => #{ + 'TestQueryStyleDeepObjectExplodeTrueObject' => #{ + servers => [], + base_path => "", path => "/query/style_deepObject/explode_true/object", method => <<"GET">>, handler => 'openapi_query_handler' }, - 'TestQueryStyleDeepObjectExplodeTrueObjectAllOf' => #{ + 'TestQueryStyleDeepObjectExplodeTrueObjectAllOf' => #{ + servers => [], + base_path => "", path => "/query/style_deepObject/explode_true/object/allOf", method => <<"GET">>, handler => 'openapi_query_handler' }, - 'TestQueryStyleFormExplodeFalseArrayInteger' => #{ + 'TestQueryStyleFormExplodeFalseArrayInteger' => #{ + servers => [], + base_path => "", path => "/query/style_form/explode_false/array_integer", method => <<"GET">>, handler => 'openapi_query_handler' }, - 'TestQueryStyleFormExplodeFalseArrayString' => #{ + 'TestQueryStyleFormExplodeFalseArrayString' => #{ + servers => [], + base_path => "", path => "/query/style_form/explode_false/array_string", method => <<"GET">>, handler => 'openapi_query_handler' }, - 'TestQueryStyleFormExplodeTrueArrayString' => #{ + 'TestQueryStyleFormExplodeTrueArrayString' => #{ + servers => [], + base_path => "", path => "/query/style_form/explode_true/array_string", method => <<"GET">>, handler => 'openapi_query_handler' }, - 'TestQueryStyleFormExplodeTrueObject' => #{ + 'TestQueryStyleFormExplodeTrueObject' => #{ + servers => [], + base_path => "", path => "/query/style_form/explode_true/object", method => <<"GET">>, handler => 'openapi_query_handler' }, - 'TestQueryStyleFormExplodeTrueObjectAllOf' => #{ + 'TestQueryStyleFormExplodeTrueObjectAllOf' => #{ + servers => [], + base_path => "", path => "/query/style_form/explode_true/object/allOf", method => <<"GET">>, handler => 'openapi_query_handler' diff --git a/samples/server/echo_api/erlang-server/src/openapi_server.erl b/samples/server/echo_api/erlang-server/src/openapi_server.erl index 0cd992fe69d2..7670f1df06f4 100644 --- a/samples/server/echo_api/erlang-server/src/openapi_server.erl +++ b/samples/server/echo_api/erlang-server/src/openapi_server.erl @@ -1,4 +1,7 @@ -module(openapi_server). +-moduledoc """ +Echo Server API +""". -define(DEFAULT_LOGIC_HANDLER, openapi_logic_handler). diff --git a/samples/server/petstore/erlang-server/src/openapi_api.erl b/samples/server/petstore/erlang-server/src/openapi_api.erl index 16ad18e97823..74dc76329efa 100644 --- a/samples/server/petstore/erlang-server/src/openapi_api.erl +++ b/samples/server/petstore/erlang-server/src/openapi_api.erl @@ -46,7 +46,7 @@ accept_callback(Class, OperationID, Req, Context) -> -export_type([operation_id/0]). --dialyzer({nowarn_function, [to_binary/1, to_list/1, validate_response_body/4]}). +-dialyzer({nowarn_function, [to_binary/1, validate_response_body/4]}). -type rule() :: {type, binary} | @@ -484,24 +484,31 @@ request_param_info('UpdateUser', 'User') -> request_param_info(OperationID, Name) -> error({unknown_param, OperationID, Name}). +-spec populate_request_params( + operation_id(), [request_param()], cowboy_req:req(), jesse_state:state(), map()) -> + {ok, map(), cowboy_req:req()} | {error, _, cowboy_req:req()}. populate_request_params(_, [], Req, _, Model) -> {ok, Model, Req}; -populate_request_params(OperationID, [FieldParams | T], Req0, ValidatorState, Model) -> - case populate_request_param(OperationID, FieldParams, Req0, ValidatorState) of - {ok, K, V, Req} -> - populate_request_params(OperationID, T, Req, ValidatorState, maps:put(K, V, Model)); +populate_request_params(OperationID, [ReqParamName | T], Req0, ValidatorState, Model0) -> + case populate_request_param(OperationID, ReqParamName, Req0, ValidatorState) of + {ok, V, Req} -> + Model = maps:put(ReqParamName, V, Model0), + populate_request_params(OperationID, T, Req, ValidatorState, Model); Error -> Error end. -populate_request_param(OperationID, Name, Req0, ValidatorState) -> - #{rules := Rules, source := Source} = request_param_info(OperationID, Name), - case get_value(Source, Name, Req0) of +-spec populate_request_param( + operation_id(), request_param(), cowboy_req:req(), jesse_state:state()) -> + {ok, term(), cowboy_req:req()} | {error, term(), cowboy_req:req()}. +populate_request_param(OperationID, ReqParamName, Req0, ValidatorState) -> + #{rules := Rules, source := Source} = request_param_info(OperationID, ReqParamName), + case get_value(Source, ReqParamName, Req0) of {error, Reason, Req} -> {error, Reason, Req}; {Value, Req} -> - case prepare_param(Rules, Name, Value, ValidatorState) of - {ok, Result} -> {ok, Name, Result, Req}; + case prepare_param(Rules, ReqParamName, Value, ValidatorState) of + {ok, Result} -> {ok, Result, Req}; {error, Reason} -> {error, Reason, Req} end @@ -511,112 +518,111 @@ populate_request_param(OperationID, Name, Req0, ValidatorState) -> validate_response_body(list, ReturnBaseType, Body, ValidatorState) -> [ - validate(schema, ReturnBaseType, Item, ValidatorState) + validate(schema, Item, ReturnBaseType, ValidatorState) || Item <- Body]; validate_response_body(_, ReturnBaseType, Body, ValidatorState) -> - validate(schema, ReturnBaseType, Body, ValidatorState). + validate(schema, Body, ReturnBaseType, ValidatorState). -validate(Rule = required, Name, Value, _ValidatorState) -> - case Value of - undefined -> validation_error(Rule, Name); - _ -> ok - end; -validate(not_required, _Name, _Value, _ValidatorState) -> +-spec validate(rule(), term(), request_param(), jesse_state:state()) -> + ok | {ok, term()}. +validate(required, undefined, ReqParamName, _) -> + validation_error(required, ReqParamName, undefined); +validate(required, _Value, _ReqParamName, _) -> + ok; +validate(not_required, _Value, _ReqParamName, _) -> + ok; +validate(_, undefined, _ReqParamName, _) -> + ok; +validate({type, boolean}, Value, _ReqParamName, _) when is_boolean(Value) -> + {ok, Value}; +validate({type, integer}, Value, _ReqParamName, _) when is_integer(Value) -> + ok; +validate({type, float}, Value, _ReqParamName, _) when is_float(Value) -> ok; -validate(_, _Name, undefined, _ValidatorState) -> +validate({type, binary}, Value, _ReqParamName, _) when is_binary(Value) -> ok; -validate(Rule = {type, integer}, Name, Value, _ValidatorState) -> +validate(Rule = {type, binary}, Value, ReqParamName, _) -> + validation_error(Rule, ReqParamName, Value); +validate(Rule = {type, boolean}, Value, ReqParamName, _) -> + case binary_to_lower(Value) of + <<"true">> -> {ok, true}; + <<"false">> -> {ok, false}; + _ -> validation_error(Rule, ReqParamName, Value) + end; +validate(Rule = {type, integer}, Value, ReqParamName, _) -> try {ok, to_int(Value)} catch error:badarg -> - validation_error(Rule, Name) + validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {type, float}, Name, Value, _ValidatorState) -> +validate(Rule = {type, float}, Value, ReqParamName, _) -> try {ok, to_float(Value)} catch error:badarg -> - validation_error(Rule, Name) - end; -validate(Rule = {type, binary}, Name, Value, _ValidatorState) -> - case is_binary(Value) of - true -> ok; - false -> validation_error(Rule, Name) - end; -validate(_Rule = {type, boolean}, _Name, Value, _ValidatorState) when is_boolean(Value) -> - {ok, Value}; -validate(Rule = {type, boolean}, Name, Value, _ValidatorState) -> - V = binary_to_lower(Value), - try - case binary_to_existing_atom(V, utf8) of - B when is_boolean(B) -> {ok, B}; - _ -> validation_error(Rule, Name) - end - catch - error:badarg -> - validation_error(Rule, Name) + validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {type, date}, Name, Value, _ValidatorState) -> +validate(Rule = {type, date}, Value, ReqParamName, _) -> case is_binary(Value) of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {type, datetime}, Name, Value, _ValidatorState) -> +validate(Rule = {type, datetime}, Value, ReqParamName, _) -> case is_binary(Value) of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {enum, Values}, Name, Value, _ValidatorState) -> +validate(Rule = {enum, Values}, Value, ReqParamName, _) -> try FormattedValue = erlang:binary_to_existing_atom(Value, utf8), case lists:member(FormattedValue, Values) of true -> {ok, FormattedValue}; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end catch error:badarg -> - validation_error(Rule, Name) + validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {max, Max}, Name, Value, _ValidatorState) -> +validate(Rule = {max, Max}, Value, ReqParamName, _) -> case Value =< Max of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {exclusive_max, ExclusiveMax}, Name, Value, _ValidatorState) -> +validate(Rule = {exclusive_max, ExclusiveMax}, Value, ReqParamName, _) -> case Value > ExclusiveMax of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {min, Min}, Name, Value, _ValidatorState) -> +validate(Rule = {min, Min}, Value, ReqParamName, _) -> case Value >= Min of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {exclusive_min, ExclusiveMin}, Name, Value, _ValidatorState) -> +validate(Rule = {exclusive_min, ExclusiveMin}, Value, ReqParamName, _) -> case Value =< ExclusiveMin of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {max_length, MaxLength}, Name, Value, _ValidatorState) -> +validate(Rule = {max_length, MaxLength}, Value, ReqParamName, _) -> case size(Value) =< MaxLength of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {min_length, MinLength}, Name, Value, _ValidatorState) -> +validate(Rule = {min_length, MinLength}, Value, ReqParamName, _) -> case size(Value) >= MinLength of true -> ok; - false -> validation_error(Rule, Name) + false -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = {pattern, Pattern}, Name, Value, _ValidatorState) -> +validate(Rule = {pattern, Pattern}, Value, ReqParamName, _) -> {ok, MP} = re:compile(Pattern), case re:run(Value, MP) of {match, _} -> ok; - _ -> validation_error(Rule, Name) + _ -> validation_error(Rule, ReqParamName, Value) end; -validate(Rule = schema, Name, Value, ValidatorState) -> - Definition = list_to_binary("#/components/schemas/" ++ to_list(Name)), +validate(Rule = schema, Value, ReqParamName, ValidatorState) -> + Definition = iolist_to_binary(["#/components/schemas/", atom_to_binary(ReqParamName)]), try _ = validate_with_schema(Value, Definition, ValidatorState), ok @@ -626,7 +632,7 @@ validate(Rule = schema, Name, Value, ValidatorState) -> type => schema_invalid, error => Error }, - validation_error(Rule, Name, Info); + validation_error(Rule, ReqParamName, Value, Info); throw:[{data_invalid, Schema, Error, _, Path} | _] -> Info = #{ type => data_invalid, @@ -634,25 +640,25 @@ validate(Rule = schema, Name, Value, ValidatorState) -> schema => Schema, path => Path }, - validation_error(Rule, Name, Info) + validation_error(Rule, ReqParamName, Value, Info) end; -validate(Rule, Name, _Value, _ValidatorState) -> - ?LOG_INFO(#{what => "Cannot validate rule", name => Name, rule => Rule}), +validate(Rule, _Value, ReqParamName, _) -> + ?LOG_INFO(#{what => "Cannot validate rule", name => ReqParamName, rule => Rule}), error({unknown_validation_rule, Rule}). --spec validation_error(Rule :: any(), Name :: any()) -> no_return(). -validation_error(ViolatedRule, Name) -> - validation_error(ViolatedRule, Name, #{}). +-spec validation_error(rule(), request_param(), term()) -> no_return(). +validation_error(ViolatedRule, Name, Value) -> + validation_error(ViolatedRule, Name, Value, #{}). --spec validation_error(Rule :: any(), Name :: any(), Info :: #{_ := _}) -> no_return(). -validation_error(ViolatedRule, Name, Info) -> - throw({wrong_param, Name, ViolatedRule, Info}). +-spec validation_error(rule(), request_param(), term(), Info :: #{_ := _}) -> no_return(). +validation_error(ViolatedRule, Name, Value, Info) -> + throw({wrong_param, Name, Value, ViolatedRule, Info}). --spec get_value(body | qs_val | header | binding, Name :: any(), Req0 :: cowboy_req:req()) -> - {Value :: any(), Req :: cowboy_req:req()} | - {error, Reason :: any(), Req :: cowboy_req:req()}. +-spec get_value(body | qs_val | header | binding, request_param(), cowboy_req:req()) -> + {any(), cowboy_req:req()} | + {error, any(), cowboy_req:req()}. get_value(body, _Name, Req0) -> - {ok, Body, Req} = cowboy_req:read_body(Req0), + {ok, Body, Req} = read_entire_body(Req0), case prepare_body(Body) of {error, Reason} -> {error, Reason, Req}; @@ -668,17 +674,30 @@ get_value(header, Name, Req) -> Value = maps:get(to_header(Name), Headers, undefined), {Value, Req}; get_value(binding, Name, Req) -> - Value = cowboy_req:binding(to_binding(Name), Req), + Value = cowboy_req:binding(Name, Req), {Value, Req}. +-spec read_entire_body(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}. +read_entire_body(Req) -> + read_entire_body(Req, []). + +-spec read_entire_body(cowboy_req:req(), iodata()) -> {ok, binary(), cowboy_req:req()}. +read_entire_body(Request, Acc) -> % { + case cowboy_req:read_body(Request) of + {ok, Data, NewRequest} -> + {ok, iolist_to_binary(lists:reverse([Data | Acc])), NewRequest}; + {more, Data, NewRequest} -> + read_entire_body(NewRequest, [Data | Acc]) + end. + prepare_body(<<>>) -> <<>>; prepare_body(Body) -> try json:decode(Body) catch - error:_ -> - {error, {invalid_body, not_json, Body}} + error:Error -> + {error, {invalid_json, Body, Error}} end. validate_with_schema(Body, Definition, ValidatorState) -> @@ -688,18 +707,17 @@ validate_with_schema(Body, Definition, ValidatorState) -> ValidatorState ). -prepare_param(Rules, Name, Value, ValidatorState) -> +-spec prepare_param([rule()], request_param(), term(), jesse_state:state()) -> + {ok, term()} | {error, Reason :: any()}. +prepare_param(Rules, ReqParamName, Value, ValidatorState) -> + Fun = fun(Rule, Acc) -> + case validate(Rule, Acc, ReqParamName, ValidatorState) of + ok -> Acc; + {ok, Prepared} -> Prepared + end + end, try - Result = lists:foldl( - fun(Rule, Acc) -> - case validate(Rule, Name, Acc, ValidatorState) of - ok -> Acc; - {ok, Prepared} -> Prepared - end - end, - Value, - Rules - ), + Result = lists:foldl(Fun, Value, Rules), {ok, Result} catch throw:Reason -> @@ -713,40 +731,28 @@ to_binary(V) when is_atom(V) -> atom_to_binary(V, utf8); to_binary(V) when is_integer(V) -> integer_to_binary(V); to_binary(V) when is_float(V) -> float_to_binary(V). --spec to_list(iodata() | atom() | number()) -> binary(). -to_list(V) when is_list(V) -> V; -to_list(V) when is_binary(V) -> binary_to_list(V); -to_list(V) when is_atom(V) -> atom_to_list(V); -to_list(V) when is_integer(V) -> integer_to_list(V); -to_list(V) when is_float(V) -> float_to_list(V). +-spec to_float(binary() | list()) -> integer(). +to_float(Data) when is_binary(Data) -> + binary_to_float(Data); +to_float(Data) when is_list(Data) -> + list_to_float(Data). --spec to_float(iodata()) -> float(). -to_float(V) -> - binary_to_float(iolist_to_binary([V])). - --spec to_int(integer() | binary() | list()) -> integer(). -to_int(Data) when is_integer(Data) -> - Data; +-spec to_int(binary() | list()) -> integer(). to_int(Data) when is_binary(Data) -> binary_to_integer(Data); to_int(Data) when is_list(Data) -> list_to_integer(Data). --spec to_header(iodata() | atom() | number()) -> binary(). +-spec to_header(request_param()) -> binary(). to_header(Name) -> - to_binary(string:lowercase(to_binary(Name))). + to_binary(string:lowercase(atom_to_binary(Name, utf8))). binary_to_lower(V) when is_binary(V) -> string:lowercase(V). --spec to_qs(iodata() | atom() | number()) -> binary(). +-spec to_qs(request_param()) -> binary(). to_qs(Name) -> - to_binary(Name). - --spec to_binding(iodata() | atom() | number()) -> atom(). -to_binding(Name) -> - Prepared = to_binary(Name), - binary_to_existing_atom(Prepared, utf8). + atom_to_binary(Name, utf8). -spec get_opt(any(), []) -> any(). get_opt(Key, Opts) -> diff --git a/samples/server/petstore/erlang-server/src/openapi_pet_handler.erl b/samples/server/petstore/erlang-server/src/openapi_pet_handler.erl index 837ea11d1447..f08106e94b1b 100644 --- a/samples/server/petstore/erlang-server/src/openapi_pet_handler.erl +++ b/samples/server/petstore/erlang-server/src/openapi_pet_handler.erl @@ -1,5 +1,32 @@ -%% basic handler -module(openapi_pet_handler). +-moduledoc """ +Exposes the following operation IDs: + +- `POST` to `/pet`, OperationId: `AddPet`: +Add a new pet to the store. + +- `DELETE` to `/pet/:petId`, OperationId: `DeletePet`: +Deletes a pet. + +- `GET` to `/pet/findByStatus`, OperationId: `FindPetsByStatus`: +Finds Pets by status. + +- `GET` to `/pet/findByTags`, OperationId: `FindPetsByTags`: +Finds Pets by tags. + +- `GET` to `/pet/:petId`, OperationId: `GetPetById`: +Find pet by ID. + +- `PUT` to `/pet`, OperationId: `UpdatePet`: +Update an existing pet. + +- `POST` to `/pet/:petId`, OperationId: `UpdatePetWithForm`: +Updates a pet in the store with form data. + +- `POST` to `/pet/:petId/uploadImage`, OperationId: `UploadFile`: +uploads an image. + +""". -behaviour(cowboy_rest). diff --git a/samples/server/petstore/erlang-server/src/openapi_router.erl b/samples/server/petstore/erlang-server/src/openapi_router.erl index 618d2024794b..b079e015b646 100644 --- a/samples/server/petstore/erlang-server/src/openapi_router.erl +++ b/samples/server/petstore/erlang-server/src/openapi_router.erl @@ -19,118 +19,171 @@ get_paths(LogicHandler) -> group_paths() -> maps:fold( - fun(OperationID, #{path := Path, method := Method, handler := Handler}, Acc) -> - case maps:find(Path, Acc) of + fun(OperationID, #{servers := Servers, base_path := BasePath, path := Path, + method := Method, handler := Handler}, Acc) -> + FullPaths = build_full_paths(Servers, BasePath, Path), + merge_paths(FullPaths, OperationID, Method, Handler, Acc) + end, #{}, get_operations()). + +build_full_paths([], BasePath, Path) -> + [lists:append([BasePath, Path])]; +build_full_paths(Servers, _BasePath, Path) -> + [lists:append([Server, Path]) || Server <- Servers ]. + +merge_paths(FullPaths, OperationID, Method, Handler, Acc) -> + lists:foldl( + fun(Path, Acc0) -> + case maps:find(Path, Acc0) of {ok, PathInfo0 = #{operations := Operations0}} -> Operations = Operations0#{Method => OperationID}, PathInfo = PathInfo0#{operations => Operations}, - Acc#{Path => PathInfo}; + Acc0#{Path => PathInfo}; error -> Operations = #{Method => OperationID}, PathInfo = #{handler => Handler, operations => Operations}, - Acc#{Path => PathInfo} + Acc0#{Path => PathInfo} end - end, #{}, get_operations()). + end, Acc, FullPaths). get_operations() -> #{ - 'AddPet' => #{ - path => "/v2/pet", + 'AddPet' => #{ + servers => [], + base_path => "/v2", + path => "/pet", method => <<"POST">>, handler => 'openapi_pet_handler' }, - 'DeletePet' => #{ - path => "/v2/pet/:petId", + 'DeletePet' => #{ + servers => [], + base_path => "/v2", + path => "/pet/:petId", method => <<"DELETE">>, handler => 'openapi_pet_handler' }, - 'FindPetsByStatus' => #{ - path => "/v2/pet/findByStatus", + 'FindPetsByStatus' => #{ + servers => [], + base_path => "/v2", + path => "/pet/findByStatus", method => <<"GET">>, handler => 'openapi_pet_handler' }, - 'FindPetsByTags' => #{ - path => "/v2/pet/findByTags", + 'FindPetsByTags' => #{ + servers => [], + base_path => "/v2", + path => "/pet/findByTags", method => <<"GET">>, handler => 'openapi_pet_handler' }, - 'GetPetById' => #{ - path => "/v2/pet/:petId", + 'GetPetById' => #{ + servers => [], + base_path => "/v2", + path => "/pet/:petId", method => <<"GET">>, handler => 'openapi_pet_handler' }, - 'UpdatePet' => #{ - path => "/v2/pet", + 'UpdatePet' => #{ + servers => [], + base_path => "/v2", + path => "/pet", method => <<"PUT">>, handler => 'openapi_pet_handler' }, - 'UpdatePetWithForm' => #{ - path => "/v2/pet/:petId", + 'UpdatePetWithForm' => #{ + servers => [], + base_path => "/v2", + path => "/pet/:petId", method => <<"POST">>, handler => 'openapi_pet_handler' }, - 'UploadFile' => #{ - path => "/v2/pet/:petId/uploadImage", + 'UploadFile' => #{ + servers => [], + base_path => "/v2", + path => "/pet/:petId/uploadImage", method => <<"POST">>, handler => 'openapi_pet_handler' }, - 'DeleteOrder' => #{ - path => "/v2/store/order/:orderId", + 'DeleteOrder' => #{ + servers => [], + base_path => "/v2", + path => "/store/order/:orderId", method => <<"DELETE">>, handler => 'openapi_store_handler' }, - 'GetInventory' => #{ - path => "/v2/store/inventory", + 'GetInventory' => #{ + servers => [], + base_path => "/v2", + path => "/store/inventory", method => <<"GET">>, handler => 'openapi_store_handler' }, - 'GetOrderById' => #{ - path => "/v2/store/order/:orderId", + 'GetOrderById' => #{ + servers => [], + base_path => "/v2", + path => "/store/order/:orderId", method => <<"GET">>, handler => 'openapi_store_handler' }, - 'PlaceOrder' => #{ - path => "/v2/store/order", + 'PlaceOrder' => #{ + servers => [], + base_path => "/v2", + path => "/store/order", method => <<"POST">>, handler => 'openapi_store_handler' }, - 'CreateUser' => #{ - path => "/v2/user", + 'CreateUser' => #{ + servers => [], + base_path => "/v2", + path => "/user", method => <<"POST">>, handler => 'openapi_user_handler' }, - 'CreateUsersWithArrayInput' => #{ - path => "/v2/user/createWithArray", + 'CreateUsersWithArrayInput' => #{ + servers => [], + base_path => "/v2", + path => "/user/createWithArray", method => <<"POST">>, handler => 'openapi_user_handler' }, - 'CreateUsersWithListInput' => #{ - path => "/v2/user/createWithList", + 'CreateUsersWithListInput' => #{ + servers => [], + base_path => "/v2", + path => "/user/createWithList", method => <<"POST">>, handler => 'openapi_user_handler' }, - 'DeleteUser' => #{ - path => "/v2/user/:username", + 'DeleteUser' => #{ + servers => [], + base_path => "/v2", + path => "/user/:username", method => <<"DELETE">>, handler => 'openapi_user_handler' }, - 'GetUserByName' => #{ - path => "/v2/user/:username", + 'GetUserByName' => #{ + servers => [], + base_path => "/v2", + path => "/user/:username", method => <<"GET">>, handler => 'openapi_user_handler' }, - 'LoginUser' => #{ - path => "/v2/user/login", + 'LoginUser' => #{ + servers => [], + base_path => "/v2", + path => "/user/login", method => <<"GET">>, handler => 'openapi_user_handler' }, - 'LogoutUser' => #{ - path => "/v2/user/logout", + 'LogoutUser' => #{ + servers => [], + base_path => "/v2", + path => "/user/logout", method => <<"GET">>, handler => 'openapi_user_handler' }, - 'UpdateUser' => #{ - path => "/v2/user/:username", + 'UpdateUser' => #{ + servers => [], + base_path => "/v2", + path => "/user/:username", method => <<"PUT">>, handler => 'openapi_user_handler' } diff --git a/samples/server/petstore/erlang-server/src/openapi_server.erl b/samples/server/petstore/erlang-server/src/openapi_server.erl index 0cd992fe69d2..f3cd231b0d54 100644 --- a/samples/server/petstore/erlang-server/src/openapi_server.erl +++ b/samples/server/petstore/erlang-server/src/openapi_server.erl @@ -1,4 +1,7 @@ -module(openapi_server). +-moduledoc """ +This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. +""". -define(DEFAULT_LOGIC_HANDLER, openapi_logic_handler). diff --git a/samples/server/petstore/erlang-server/src/openapi_store_handler.erl b/samples/server/petstore/erlang-server/src/openapi_store_handler.erl index d98988d8d6e3..a4d601a65bdf 100644 --- a/samples/server/petstore/erlang-server/src/openapi_store_handler.erl +++ b/samples/server/petstore/erlang-server/src/openapi_store_handler.erl @@ -1,5 +1,20 @@ -%% basic handler -module(openapi_store_handler). +-moduledoc """ +Exposes the following operation IDs: + +- `DELETE` to `/store/order/:orderId`, OperationId: `DeleteOrder`: +Delete purchase order by ID. + +- `GET` to `/store/inventory`, OperationId: `GetInventory`: +Returns pet inventories by status. + +- `GET` to `/store/order/:orderId`, OperationId: `GetOrderById`: +Find purchase order by ID. + +- `POST` to `/store/order`, OperationId: `PlaceOrder`: +Place an order for a pet. + +""". -behaviour(cowboy_rest). diff --git a/samples/server/petstore/erlang-server/src/openapi_user_handler.erl b/samples/server/petstore/erlang-server/src/openapi_user_handler.erl index b1db5d7bb513..35a2056e703e 100644 --- a/samples/server/petstore/erlang-server/src/openapi_user_handler.erl +++ b/samples/server/petstore/erlang-server/src/openapi_user_handler.erl @@ -1,5 +1,32 @@ -%% basic handler -module(openapi_user_handler). +-moduledoc """ +Exposes the following operation IDs: + +- `POST` to `/user`, OperationId: `CreateUser`: +Create user. + +- `POST` to `/user/createWithArray`, OperationId: `CreateUsersWithArrayInput`: +Creates list of users with given input array. + +- `POST` to `/user/createWithList`, OperationId: `CreateUsersWithListInput`: +Creates list of users with given input array. + +- `DELETE` to `/user/:username`, OperationId: `DeleteUser`: +Delete user. + +- `GET` to `/user/:username`, OperationId: `GetUserByName`: +Get user by user name. + +- `GET` to `/user/login`, OperationId: `LoginUser`: +Logs user into the system. + +- `GET` to `/user/logout`, OperationId: `LogoutUser`: +Logs out current logged in user session. + +- `PUT` to `/user/:username`, OperationId: `UpdateUser`: +Updated user. + +""". -behaviour(cowboy_rest).