Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,32 @@ and `validate_response/4` respectively.

For example, the user-defined `Module:accept_callback/4` can be implemented as follows:
```
-spec accept_callback(atom(), openapi_api:operation_id(), cowboy_req:req(), context()) ->
{cowboy:http_status(), cowboy:http_headers(), json:encode_value()}.
accept_callback(Class, OperationID, Req, Context) ->
-spec accept_callback(
Class :: openapi_api:class(),
OperationID :: openapi_api:operation_id(),
Req :: cowboy_req:req(),
Context :: openapi_logic_handler:context()) ->
{openapi_logic_handler:accept_callback_return(),
cowboy_req:req(),
openapi_logic_handler:context()}.
accept_callback(Class, OperationID, Req0, Context0) ->
ValidatorState = openapi_api:prepare_validator(),
case openapi_api:populate_request(OperationID, Req0, ValidatorState) of
{ok, Populated, Req1} ->
{Code, Headers, Body} = openapi_logic_handler:handle_request(
LogicHandler,
OperationID,
Req1,
maps:merge(State#state.context, Populated)
),
_ = openapi_api:validate_response(
OperationID,
Code,
Body,
ValidatorState
),
PreparedBody = prepare_body(Code, Body),
Response = {ok, {Code, Headers, PreparedBody}},
process_response(Response, Req1, State);
{ok, Model, Req1} ->
Context1 = maps:merge(Context0, Model),
case do_accept_callback(Class, OperationID, Req1, Context1) of
{false, Req2, Context2} ->
{false, Req2, Context2};
{{true, Code, Body}, Req2, Context2} ->
case validate_response(OperationID, Code, Body, ValidatorState) of
ok ->
process_response({ok, Code, Body}, Req2, Context2);
{error, Reason} ->
process_response({error, Reason}, Req2, Context2)
end
end;
{error, Reason, Req1} ->
process_response({error, Reason}, Req1, State)
process_response({error, Reason}, Req1, Context0)
end.
```
""".
Expand All @@ -41,10 +44,17 @@ accept_callback(Class, OperationID, Req, Context) ->
-ignore_xref([populate_request/3, validate_response/4]).
-ignore_xref([prepare_validator/0, prepare_validator/1, prepare_validator/2]).

-type operation_id() :: atom().
-type class() ::
{{#apiInfo}}{{#apis}}{{#operations}} {{^-first}}| {{/-first}}'{{pathPrefix}}'{{#-last}}.{{/-last}}
{{/operations}}{{/apis}}{{/apiInfo}}

-type operation_id() ::
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} '{{operationIdOriginal}}' | %% {{summary}}
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} {error, unknown_operation}.

-type request_param() :: atom().

-export_type([operation_id/0]).
-export_type([class/0, operation_id/0]).

-dialyzer({nowarn_function, [validate_response_body/4]}).

Expand All @@ -64,6 +74,7 @@ accept_callback(Class, OperationID, Req, Context) ->
{max_length, MaxLength :: integer()} |
{min_length, MaxLength :: integer()} |
{pattern, Pattern :: string()} |
{schema, object | list, binary()} |
schema |
required |
not_required.
Expand Down Expand Up @@ -111,25 +122,25 @@ for the `OperationID` operation.
Body :: jesse:json_term(),
ValidatorState :: jesse_state:state()) ->
ok | {ok, term()} | [ok | {ok, term()}] | no_return().
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}{{#responses}}validate_response('{{operationId}}', {{code}}, Body, ValidatorState) ->
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}{{#responses}}validate_response('{{operationIdOriginal}}', {{code}}, Body, ValidatorState) ->
validate_response_body('{{dataType}}', '{{baseType}}', Body, ValidatorState);
{{/responses}}
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}validate_response(_OperationID, _Code, _Body, _ValidatorState) ->
ok.

%%%
-spec request_params(OperationID :: operation_id()) -> [Param :: request_param()].
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}request_params('{{operationId}}') ->
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}request_params('{{operationIdOriginal}}') ->
[{{#allParams}}{{^isBodyParam}}
'{{baseName}}'{{/isBodyParam}}{{#isBodyParam}}
'{{dataType}}'{{/isBodyParam}}{{^-last}},{{/-last}}{{/allParams}}
{{#content.application/json.schema.openApiType}}'{{content.application/json.schema.openApiType}}'{{/content.application/json.schema.openApiType}}{{^content.application/json.schema.openApiType}}'{{dataType}}'{{/content.application/json.schema.openApiType}}{{/isBodyParam}}{{^-last}},{{/-last}}{{/allParams}}
];
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}request_params(_) ->
error(unknown_operation).

-spec request_param_info(OperationID :: operation_id(), Name :: request_param()) ->
#{source => qs_val | binding | header | body, rules => [rule()]}.
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}{{#allParams}}request_param_info('{{operationId}}', {{^isBodyParam}}'{{baseName}}'{{/isBodyParam}}{{#isBodyParam}}'{{dataType}}'{{/isBodyParam}}) ->
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}{{#allParams}}request_param_info('{{operationIdOriginal}}', {{^isBodyParam}}'{{baseName}}'{{/isBodyParam}}{{#isBodyParam}}{{#content.application/json.schema.openApiType}}'{{content.application/json.schema.openApiType}}'{{/content.application/json.schema.openApiType}}{{^content.application/json.schema.openApiType}}'{{dataType}}'{{/content.application/json.schema.openApiType}}{{/isBodyParam}}) ->
#{
source => {{#isQueryParam}}qs_val{{/isQueryParam}}{{#isPathParam}}binding{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}{{#isFormParam}}body{{/isFormParam}},
rules => [{{#isString}}
Expand All @@ -150,8 +161,9 @@ for the `OperationID` operation.
{exclusive_min, {{minimum}}},{{/exclusiveMinimum}}{{/minimum}}{{#maxLength}}
{max_length, {{maxLength}}},{{/maxLength}}{{#minLength}}
{min_length, {{minLength}}},{{/minLength}}{{#pattern}}
{pattern, "{{{pattern}}}"},{{/pattern}}{{#isBodyParam}}
schema,{{/isBodyParam}}{{#required}}
{pattern, "{{{pattern}}}"},{{/pattern}}{{#isBodyParam}}{{#content.application/json.schema.ref}}
{schema, object, <<"{{content.application/json.schema.ref}}">>},{{/content.application/json.schema.ref}}{{#content.application/json.schema.items.ref}}
{schema, list, <<"{{content.application/json.schema.items.ref}}">>},{{/content.application/json.schema.items.ref}}{{/isBodyParam}}{{#required}}
required{{/required}}{{^required}}
not_required{{/required}}
]
Expand Down Expand Up @@ -189,8 +201,6 @@ populate_request_param(OperationID, ReqParamName, Req0, ValidatorState) ->
end
end.

-include_lib("kernel/include/logger.hrl").

validate_response_body(list, ReturnBaseType, Body, ValidatorState) ->
[
validate(schema, Item, ReturnBaseType, ValidatorState)
Expand Down Expand Up @@ -281,8 +291,15 @@ validate(Rule = {pattern, Pattern}, Value, ReqParamName, _) ->
{match, _} -> ok;
_ -> validation_error(Rule, ReqParamName, Value)
end;
validate(Rule = schema, Value, ReqParamName, ValidatorState) ->
validate(schema, Value, ReqParamName, ValidatorState) ->
Definition = iolist_to_binary(["#/components/schemas/", atom_to_binary(ReqParamName, utf8)]),
validate({schema, object, Definition}, Value, ReqParamName, ValidatorState);
validate({schema, list, Definition}, Value, ReqParamName, ValidatorState) ->
lists:foreach(
fun(Item) ->
validate({schema, object, Definition}, Item, ReqParamName, ValidatorState)
end, Value);
validate(Rule = {schema, object, Definition}, Value, ReqParamName, ValidatorState) ->
try
_ = validate_with_schema(Value, Definition, ValidatorState),
ok
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
-moduledoc """
Exposes the following operation IDs:
{{#operations}}{{#operation}}
- `{{httpMethod}}` to `{{path}}`, OperationId: `{{operationId}}`:
- `{{httpMethod}}` to `{{path}}`, OperationId: `{{operationIdOriginal}}`:
{{summary}}.
{{notes}}
{{/operation}}{{/operations}}
""".

Expand All @@ -23,8 +24,16 @@ Exposes the following operation IDs:

-ignore_xref([handle_type_accepted/2, handle_type_provided/2]).

-export_type([class/0, operation_id/0]).

-type class() :: '{{operations.pathPrefix}}'.

-type operation_id() ::
{{#operations}}{{#operation}} {{^-first}}| {{/-first}}'{{operationIdOriginal}}'{{#-last}}.{{/-last}} %% {{summary}}
{{/operation}}{{/operations}}

-record(state,
{operation_id :: {{packageName}}_api:operation_id(),
{operation_id :: operation_id(),
accept_callback :: {{packageName}}_logic_handler:accept_callback(),
provide_callback :: {{packageName}}_logic_handler:provide_callback(),
api_key_handler :: {{packageName}}_logic_handler:api_key_callback(),
Expand All @@ -48,7 +57,7 @@ init(Req, {Operations, Module}) ->

-spec allowed_methods(cowboy_req:req(), state()) ->
{[binary()], cowboy_req:req(), state()}.
{{#operations}}{{#operation}}allowed_methods(Req, #state{operation_id = '{{operationId}}'} = State) ->
{{#operations}}{{#operation}}allowed_methods(Req, #state{operation_id = '{{operationIdOriginal}}'} = State) ->
{[<<"{{httpMethod}}">>], Req, State};
{{/operation}}{{/operations}}allowed_methods(Req, State) ->
{[], Req, State}.
Expand All @@ -59,7 +68,7 @@ init(Req, {Operations, Module}) ->
{{#operation}}
{{#authMethods.size}}
is_authorized(Req0,
#state{operation_id = '{{operationId}}' = OperationID,
#state{operation_id = '{{operationIdOriginal}}' = OperationID,
api_key_handler = Handler} = State) ->
case {{packageName}}_auth:authorize_api_key(Handler, OperationID, {{#isApiKey.isKeyInQuery}}qs_val, {{/isApiKey.isKeyInQuery}}{{^isApiKey.isKeyInQuery}}header, {{/isApiKey.isKeyInQuery}}{{#isApiKey}}"{{keyParamName}}", {{/isApiKey}}{{^isApiKey}}"authorization", {{/isApiKey}}Req0) of
{true, Context, Req} ->
Expand All @@ -75,7 +84,7 @@ is_authorized(Req, State) ->

-spec content_types_accepted(cowboy_req:req(), state()) ->
{[{binary(), atom()}], cowboy_req:req(), state()}.
{{#operations}}{{#operation}}content_types_accepted(Req, #state{operation_id = '{{operationId}}'} = State) ->
{{#operations}}{{#operation}}content_types_accepted(Req, #state{operation_id = '{{operationIdOriginal}}'} = State) ->
{{^consumes.size}}
{[], Req, State};
{{/consumes.size}}
Expand All @@ -91,14 +100,14 @@ is_authorized(Req, State) ->

-spec valid_content_headers(cowboy_req:req(), state()) ->
{boolean(), cowboy_req:req(), state()}.
{{#operations}}{{#operation}}valid_content_headers(Req, #state{operation_id = '{{operationId}}'} = State) ->
{{#operations}}{{#operation}}valid_content_headers(Req, #state{operation_id = '{{operationIdOriginal}}'} = State) ->
{true, Req, State};
{{/operation}}{{/operations}}valid_content_headers(Req, State) ->
{false, Req, State}.

-spec content_types_provided(cowboy_req:req(), state()) ->
{[{binary(), atom()}], cowboy_req:req(), state()}.
{{#operations}}{{#operation}}content_types_provided(Req, #state{operation_id = '{{operationId}}'} = State) ->
{{#operations}}{{#operation}}content_types_provided(Req, #state{operation_id = '{{operationIdOriginal}}'} = State) ->
{{^produces.size}}
{[], Req, State};
{{/produces.size}}
Expand All @@ -115,8 +124,8 @@ is_authorized(Req, State) ->
-spec delete_resource(cowboy_req:req(), state()) ->
{boolean(), cowboy_req:req(), state()}.
delete_resource(Req, State) ->
{Res, Req1, State} = handle_type_accepted(Req, State),
{true =:= Res, Req1, State}.
{Res, Req1, State1} = handle_type_accepted(Req, State),
{true =:= Res, Req1, State1}.

-spec handle_type_accepted(cowboy_req:req(), state()) ->
{ {{packageName}}_logic_handler:accept_callback_return(), cowboy_req:req(), state()}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
-type api_key_callback() ::
fun(({{packageName}}_api:operation_id(), binary()) -> {true, context()} | {false, iodata()}).
-type accept_callback() ::
fun((atom(), {{packageName}}_api:operation_id(), cowboy_req:req(), context()) ->
fun(({{packageName}}_api:class(), {{packageName}}_api:operation_id(), cowboy_req:req(), context()) ->
{accept_callback_return(), cowboy_req:req(), context()}).
-type provide_callback() ::
fun((atom(), {{packageName}}_api:operation_id(), cowboy_req:req(), context()) ->
fun(({{packageName}}_api:class(), {{packageName}}_api:operation_id(), cowboy_req:req(), context()) ->
{cowboy_req:resp_body(), cowboy_req:req(), context()}).
-type context() :: #{_ := _}.

Expand All @@ -26,10 +26,10 @@
-callback api_key_callback({{packageName}}_api:operation_id(), binary()) ->
{true, context()} | {false, iodata()}.

-callback accept_callback(atom(), {{packageName}}_api:operation_id(), cowboy_req:req(), context()) ->
-callback accept_callback({{packageName}}_api:class(), {{packageName}}_api:operation_id(), cowboy_req:req(), context()) ->
{accept_callback_return(), cowboy_req:req(), context()}.

-callback provide_callback(atom(), {{packageName}}_api:operation_id(), cowboy_req:req(), context()) ->
-callback provide_callback({{packageName}}_api:class(), {{packageName}}_api:operation_id(), cowboy_req:req(), context()) ->
{cowboy_req:resp_body(), cowboy_req:req(), context()}.

-export([api_key_callback/2, accept_callback/4, provide_callback/4]).
Expand All @@ -42,7 +42,7 @@ api_key_callback(OperationID, ApiKey) ->
api_key => ApiKey}),
{true, #{}}.

-spec accept_callback(atom(), {{packageName}}_api:operation_id(), cowboy_req:req(), context()) ->
-spec accept_callback({{packageName}}_api:class(), {{packageName}}_api:operation_id(), cowboy_req:req(), context()) ->
{accept_callback_return(), cowboy_req:req(), context()}.
accept_callback(Class, OperationID, Req, Context) ->
?LOG_ERROR(#{what => "Got not implemented request to process",
Expand All @@ -52,7 +52,7 @@ accept_callback(Class, OperationID, Req, Context) ->
context => Context}),
{false, Req, Context}.

-spec provide_callback(atom(), {{packageName}}_api:operation_id(), cowboy_req:req(), context()) ->
-spec provide_callback({{packageName}}_api:class(), {{packageName}}_api:operation_id(), cowboy_req:req(), context()) ->
{cowboy_req:resp_body(), cowboy_req:req(), context()}.
provide_callback(Class, OperationID, Req, Context) ->
?LOG_ERROR(#{what => "Got not implemented request to process",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ merge_paths(FullPaths, OperationID, Method, Handler, Acc) ->

get_operations() ->
#{ {{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
'{{operationId}}' => #{
'{{operationIdOriginal}}' => #{
servers => [{{#servers}}"{{{url}}}"{{^-last}},{{/-last}}{{/servers}}],
base_path => "{{{basePathWithoutHost}}}",
path => "{{{path}}}",
Expand Down
Loading