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
162 changes: 109 additions & 53 deletions modules/swagger-codegen/src/main/resources/erlang-server/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
-export([populate_request/3]).
-export([validate_response/4]).

-type operation_id() :: atom().
-type request_param() :: atom().

-export_type([operation_id/0]).

-spec request_params(OperationID :: operation_id()) -> [Param :: request_param()].
{{#apiInfo}}{{#apis}}
{{#operations}}{{#operation}}
request_params('{{operationId}}') ->
Expand All @@ -17,31 +22,56 @@ request_params('{{operationId}}') ->
request_params(_) ->
error(unknown_operation).

-type rule() ::
{type, 'binary'} |
{type, 'integer'} |
{type, 'float'} |
{type, 'binary'} |
{type, 'boolean'} |
{type, 'date'} |
{type, 'datetime'} |
{enum, [atom()]} |
{max, Max :: number()} |
{exclusive_max, Max :: number()} |
{min, Min :: number()} |
{exclusive_min, Min :: number()} |
{max_length, MaxLength :: integer()} |
{min_length, MaxLength :: integer()} |
{pattern, Pattern :: string()} |
schema |
required |
not_required.

-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}}) ->
#{
source => {{#isQueryParam}}qs_val{{/isQueryParam}} {{#isPathParam}}binding{{/isPathParam}} {{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}},
rules => [{{#isString}}
{type, 'binary'}, {{/isString}}{{#isInteger}}
{type, 'integer'}, {{/isInteger}}{{#isLong}}
{type, 'integer'}, {{/isLong}}{{#isFloat}}
{type, 'float'}, {{/isFloat}}{{#isDouble}}
{type, 'float'}, {{/isDouble}}{{#isByteArray}}
{type, 'binary'}, {{/isByteArray}}{{#isBinary}}
{type, 'binary'}, {{/isBinary}}{{#isBoolean}}
{type, 'boolean'}, {{/isBoolean}}{{#isDate}}
{type, 'date'}, {{/isDate}}{{#isDateTime}}
{type, 'datetime'}, {{/isDateTime}}{{#isEnum}}
{type, 'binary'},{{/isString}}{{#isInteger}}
{type, 'integer'},{{/isInteger}}{{#isLong}}
{type, 'integer'},{{/isLong}}{{#isFloat}}
{type, 'float'},{{/isFloat}}{{#isDouble}}
{type, 'float'},{{/isDouble}}{{#isByteArray}}
{type, 'binary'},{{/isByteArray}}{{#isBinary}}
{type, 'binary'},{{/isBinary}}{{#isBoolean}}
{type, 'boolean'},{{/isBoolean}}{{#isDate}}
{type, 'date'},{{/isDate}}{{#isDateTime}}
{type, 'datetime'},{{/isDateTime}}{{#isEnum}}
{enum, [{{#allowableValues}}{{#values}}'{{.}}'{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}] },{{/isEnum}}{{#maximum}}
{max, {{maximum}} }, {{/maximum}}{{#exclusiveMaximum}}
{exclusive_max, {{exclusiveMaximum}} }, {{/exclusiveMaximum}}{{#minimum}}
{min, {{minimum}} }, {{/minimum}}{{#exclusiveMinimum}}
{exclusive_min, {{exclusiveMinimum}} }, {{/exclusiveMinimum}}{{#maxLength}}
{max_length, {{maxLength}} }, {{/maxLength}}{{#minLength}}
{min_length, {{minLength}} }, {{/minLength}}{{#pattern}}
{pattern, "{{pattern}}" }, {{/pattern}}{{#isBodyParam}}
schema, {{/isBodyParam}}{{#required}}
{exclusive_max, {{exclusiveMaximum}} },{{/exclusiveMaximum}}{{#minimum}}
{min, {{minimum}} },{{/minimum}}{{#exclusiveMinimum}}
{exclusive_min, {{exclusiveMinimum}} },{{/exclusiveMinimum}}{{#maxLength}}
{max_length, {{maxLength}} },{{/maxLength}}{{#minLength}}
{min_length, {{minLength}} },{{/minLength}}{{#pattern}}
{pattern, "{{pattern}}" },{{/pattern}}{{#isBodyParam}}
schema,{{/isBodyParam}}{{#required}}
required{{/required}}{{^required}}
not_required{{/required}}
]
Expand All @@ -50,6 +80,14 @@ request_param_info('{{operationId}}', {{^isBodyParam}}'{{baseName}}'{{/isBodyPar
request_param_info(OperationID, Name) ->
error({unknown_param, OperationID, Name}).

-spec populate_request(
OperationID :: operation_id(),
Req :: cowboy_req:req(),
ValidatorState :: jesse_state:state()
) ->
{ok, Model :: #{}, Req :: cowboy_req:req()} |
{error, Reason :: any(), Req :: cowboy_req:req()}.

populate_request(OperationID, Req, ValidatorState) ->
Params = request_params(OperationID),
populate_request_params(OperationID, Params, Req, ValidatorState, #{}).
Expand All @@ -74,7 +112,12 @@ populate_request_param(OperationID, Name, Req0, ValidatorState) ->
{error, Reason, Req}
end.


-spec validate_response(
OperationID :: operation_id(),
Code :: 200..599,
Body :: jesse:json_term(),
ValidatorState :: jesse_state:state()
) -> ok | no_return().
{{#apiInfo}}{{#apis}}
{{#operations}}{{#operation}}
{{#responses}}
Expand All @@ -86,12 +129,6 @@ validate_response('{{operationId}}', {{code}}, Body, ValidatorState) ->
validate_response(_OperationID, _Code, _Body, _ValidatorState) ->
ok.

validate_response_body(undefined, _, Body, _) ->
case Body of
#{} -> ok;
_ -> throw(invalid_response)
end;

validate_response_body('list', ReturnBaseType, Body, ValidatorState) ->
[
validate(schema, ReturnBaseType, Item, ValidatorState)
Expand All @@ -101,7 +138,7 @@ validate_response_body(_, ReturnBaseType, Body, ValidatorState) ->
validate(schema, ReturnBaseType, Body, ValidatorState).

%%%
validate( Rule = required, Name, Value, _ValidatorState) ->
validate(Rule = required, Name, Value, _ValidatorState) ->
case Value of
undefined -> validation_error(Rule, Name);
_ -> ok
Expand All @@ -110,35 +147,35 @@ validate( Rule = required, Name, Value, _ValidatorState) ->
validate(not_required, _Name, _Value, _ValidatorState) ->
ok;

validate( _, _Name, undefined, _ValidatorState) ->
validate(_, _Name, undefined, _ValidatorState) ->
ok;

validate( Rule = {type, 'integer'}, Name, Value, _ValidatorState) ->
validate(Rule = {type, 'integer'}, Name, Value, _ValidatorState) ->
try
{ok, {{packageName}}_utils:to_int(Value)}
catch
_:Error ->
validation_error(Rule, {Name, Error})
error:badarg ->
validation_error(Rule, Name)
end;

validate( Rule = {type, 'float'}, Name, Value, _ValidatorState) ->
validate(Rule = {type, 'float'}, Name, Value, _ValidatorState) ->
try
{ok, {{packageName}}_utils:to_float(Value)}
catch
_:_ ->
error:badarg ->
validation_error(Rule, Name)
end;

validate( Rule = {type, 'binary'}, Name, Value, _ValidatorState) ->
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) ->
validate(_Rule = {type, 'boolean'}, _Name, Value, _ValidatorState) when is_boolean(Value) ->
{ok, Value};

validate( Rule = {type, 'boolean'}, Name, Value, _ValidatorState) ->
validate(Rule = {type, 'boolean'}, Name, Value, _ValidatorState) ->
V = binary_to_lower(Value),
try
case binary_to_existing_atom(V, utf8) of
Expand All @@ -150,19 +187,19 @@ validate( Rule = {type, 'boolean'}, Name, Value, _ValidatorState) ->
validation_error(Rule, Name)
end;

validate( Rule = {type, 'date'}, Name, Value, _ValidatorState) ->
validate(Rule = {type, 'date'}, Name, Value, _ValidatorState) ->
case is_binary(Value) of
true -> ok;
false -> validation_error(Rule, Name)
end;

validate( Rule = {type, 'datetime'}, Name, Value, _ValidatorState) ->
validate(Rule = {type, 'datetime'}, Name, Value, _ValidatorState) ->
case is_binary(Value) of
true -> ok;
false -> validation_error(Rule, Name)
end;

validate( Rule = {enum, Values}, Name, Value, _ValidatorState) ->
validate(Rule = {enum, Values}, Name, Value, _ValidatorState) ->
try
FormattedValue = erlang:binary_to_existing_atom(Value, utf8),
case lists:member(FormattedValue, Values) of
Expand All @@ -174,65 +211,84 @@ validate( Rule = {enum, Values}, Name, Value, _ValidatorState) ->
validation_error(Rule, Name)
end;

validate( Rule = {max, Max}, Name, Value, _ValidatorState) ->
validate(Rule = {max, Max}, Name, Value, _ValidatorState) ->
case Value >= Max of
true -> ok;
false -> validation_error(Rule, Name)
end;

validate( Rule = {exclusive_max, ExclusiveMax}, Name, Value, _ValidatorState) ->
validate(Rule = {exclusive_max, ExclusiveMax}, Name, Value, _ValidatorState) ->
case Value > ExclusiveMax of
true -> ok;
false -> validation_error(Rule, Name)
end;

validate( Rule = {min, Min}, Name, Value, _ValidatorState) ->
validate(Rule = {min, Min}, Name, Value, _ValidatorState) ->
case Value =< Min of
true -> ok;
false -> validation_error(Rule, Name)
end;

validate( Rule = {exclusive_min, ExclusiveMin}, Name, Value, _ValidatorState) ->
validate(Rule = {exclusive_min, ExclusiveMin}, Name, Value, _ValidatorState) ->
case Value =< ExclusiveMin of
true -> ok;
false -> validation_error(Rule, Name)
end;

validate( Rule = {max_length, MaxLength}, Name, Value, _ValidatorState) ->
validate(Rule = {max_length, MaxLength}, Name, Value, _ValidatorState) ->
case size(Value) =< MaxLength of
true -> ok;
false -> validation_error(Rule, Name)
end;

validate( Rule = {min_length, MinLength}, Name, Value, _ValidatorState) ->
validate(Rule = {min_length, MinLength}, Name, Value, _ValidatorState) ->
case size(Value) >= MinLength of
true -> ok;
false -> validation_error(Rule, Name)
end;

validate( Rule = {pattern, Pattern}, Name, Value, _ValidatorState) ->
validate(Rule = {pattern, Pattern}, Name, Value, _ValidatorState) ->
{ok, MP} = re:compile(Pattern),
case re:run(Value, MP) of
{match, _} -> ok;
_ -> validation_error(Rule, Name)
end;

validate( Rule = schema, Name, Value, ValidatorState) ->
validate(Rule = schema, Name, Value, ValidatorState) ->
Definition = list_to_binary("#/definitions/" ++ {{packageName}}_utils:to_list(Name)),
try
validate_with_schema(Value, Definition, ValidatorState),
_ = validate_with_schema(Value, Definition, ValidatorState),
ok
catch
_:_ ->
validation_error(Rule, Name)
throw:[{schema_invalid, _, Error} | _] ->
Info = #{
type => schema_invalid,
error => Error
},
validation_error(Rule, Name, Info);
throw:[{data_invalid, Schema, Error, _, Path} | _] ->
Info = #{
type => data_invalid,
error => Error,
schema => Schema,
path => Path
},
validation_error(Rule, Name, Info)
end;

validate(Rule, Name, Value, _ValidatorState) ->
io:format("Skipping unknown validation: ~p~n", [{Rule, Name, Value}]),
ok.
validate(Rule, Name, _Value, _ValidatorState) ->
error_logger:info_msg("Can't validate ~p with ~p", [Name, 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 :: any(), Name :: any(), Info :: #{}) -> no_return().

validation_error(Name, Violated) ->
throw({wrong_param, Name, Violated}).
validation_error(ViolatedRule, Name, Info) ->
throw({wrong_param, Name, ViolatedRule, Info}).
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...Тем более ты здесь явно бросаешь исключения определённого класса.


get_value(body, _Name, Req0) ->
{ok, Body, Req} = cowboy_req:body(Req0),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,49 @@

-export([authorize_api_key/5]).

authorize_api_key(From, KeyParam, OperationID, Req0, LogicHandler) ->
{ApiKey, Req} = get_api_key(From, KeyParam, Req0),
case ApiKey of
undefined ->
AuthHeader = <<"">>,
{false, AuthHeader, Req};
_ ->
case LogicHandler:authorize_api_key(ApiKey, OperationID) of
{true, Context} ->
{true, Context, Req};
false ->
AuthHeader = <<"">>,
{false, AuthHeader, Req}
end
end.
-spec authorize_api_key(
LogicHandler :: atom(),
OperationID :: {{packageName}}_api:operation_id(),
From :: header | qs_val,
KeyParam :: iodata() | atom(),
Req ::cowboy_req:req()
)-> {true, Context :: #{binary() => any()}, Req ::cowboy_req:req()} |
{false, AuthHeader :: binary(), Req ::cowboy_req:req()}.

authorize_api_key(LogicHandler, OperationID, From, KeyParam, Req0) ->
{ApiKey, Req} = get_api_key(From, KeyParam, Req0),
case ApiKey of
undefined ->
AuthHeader = <<"">>,
{false, AuthHeader, Req};
_ ->
Result = {{packageName}}_logic_handler:authorize_api_key(
LogicHandler,
OperationID,
ApiKey
),
case Result of
{true, Context} ->
true, Context, Req};
false ->
AuthHeader = <<"">>,
{false, AuthHeader, Req}
end
end.

get_api_key(header, KeyParam, Req0) ->
{Headers, Req} = cowboy_req:headers(Req0),
{swagger_utils:get_opt({{packageName}}_utils:to_header(KeyParam), Headers), Req};
{
swagger_utils:get_opt(
{{packageName}}_utils:to_header(KeyParam),
Headers
),
Req
};

get_api_key(qs_val, KeyParam, Req0) ->
{QS, Req} = cowboy_req:qs_vals(Req0),
{swagger_utils:get_opt(KeyParam, QS), Req}.
{ {{packageName}}_utils:get_opt(KeyParam, QS), Req}.



Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,22 @@

{{#authMethods}}
{{#isApiKey}}
-spec authorize_api_key(OperationID :: {{packageName}}_api:operation_id(), ApiKey :: binary()) -> {true, #{}}.

authorize_api_key(_, _) -> {true, #{}}.
{{/isApiKey}}
{{/authMethods}}

-spec handle_request(
OperationID :: {{packageName}}_api:operation_id(),
Req :: cowboy_req:req(),
Context :: #{}
) ->
{Status :: cowboy:http_status(), Headers :: cowboy:http_headers(), Body :: #{}}.

handle_request(OperationID, Req, Context) ->
io:format(user, "Got request to process: ~p~n", [{OperationID, Req, Context}]),
{501, [], <<"Not Implemented">>}.
error_logger:error_msg(
"Got not implemented request to process: ~p~n",
[{OperationID, Req, Context}]
),
{501, [], #{}}.
Loading