From 9d7d8c028ef5ccb913c098e145839627e221840c Mon Sep 17 00:00:00 2001 From: Anton Belyaev Date: Mon, 20 Nov 2017 18:25:09 +0300 Subject: [PATCH] CAPI-200: introduce handler behaviour and get rid of logic_handler --- .../languages/ErlangServerCodegen.java | 5 +- .../erlang-server/api.type_spec.mustache | 1 + .../resources/erlang-server/handler.mustache | 70 +++++++++++-------- .../erlang-server/handler_api.mustache | 42 +++++------ .../erlang-server/logic_handler.mustache | 65 ----------------- .../resources/erlang-server/router.mustache | 21 +++--- .../resources/erlang-server/schema.mustache | 2 +- .../resources/erlang-server/types.mustache | 11 ++- .../resources/erlang-server/utils.mustache | 5 ++ .../erlang-server/validation.mustache | 3 +- 10 files changed, 91 insertions(+), 134 deletions(-) create mode 100644 modules/swagger-codegen/src/main/resources/erlang-server/api.type_spec.mustache delete mode 100644 modules/swagger-codegen/src/main/resources/erlang-server/logic_handler.mustache diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ErlangServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ErlangServerCodegen.java index 073b2fe120f..5d1bf1514b8 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ErlangServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ErlangServerCodegen.java @@ -119,7 +119,6 @@ public void processOpts() { supportingFiles.add(new SupportingFile("utils.mustache", "", toSourceFilePath("utils", "erl"))); supportingFiles.add(new SupportingFile("types.mustache", "", toPackageNameSrcFile("erl"))); supportingFiles.add(new SupportingFile("handler_api.mustache", "", toSourceFilePath("handler_api", "erl"))); - supportingFiles.add(new SupportingFile("logic_handler.mustache", "", toSourceFilePath("logic_handler", "erl"))); supportingFiles.add(new SupportingFile("validation.mustache", "", toSourceFilePath("validation", "erl"))); supportingFiles.add(new SupportingFile("param_validator.mustache", "", toSourceFilePath("param_validator", "erl"))); supportingFiles.add(new SupportingFile("schema_validator.mustache", "", toSourceFilePath("schema_validator", "erl"))); @@ -221,11 +220,11 @@ public String toModelName(String name) { public String toOperationId(String operationId) { // method name cannot use reserved keyword, e.g. return if (isReservedWord(operationId)) { - LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + camelize(sanitizeName("call_" + operationId))); + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + underscore(sanitizeName("call_" + operationId))); operationId = "call_" + operationId; } - return camelize(operationId); + return underscore(operationId); } @Override diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/api.type_spec.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/api.type_spec.mustache new file mode 100644 index 00000000000..8181341cd93 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/erlang-server/api.type_spec.mustache @@ -0,0 +1 @@ +{{^isContainer}}{{#isString}}binary(){{/isString}}{{#isInteger}}integer(){{/isInteger}}{{#isLong}}integer(){{/isLong}}{{#isFloat}}float(){{/isFloat}}{{#isDouble}}float(){{/isDouble}}{{#isByteArray}}binary(){{/isByteArray}}{{#isBinary}}binary(){{/isBinary}}{{#isBoolean}}boolean(){{/isBoolean}}{{#isDate}}binary(){{/isDate}}{{#isDateTime}}binary(){{/isDateTime}}{{#isEnum}}{{#allowableValues}}{{#values}}'{{.}}'{{^-last}} | {{/-last}}{{/values}}{{/allowableValues}}{{/isEnum}}{{/isContainer}} diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/handler.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/handler.mustache index e69e659fdf7..b60782fe2de 100644 --- a/modules/swagger-codegen/src/main/resources/erlang-server/handler.mustache +++ b/modules/swagger-codegen/src/main/resources/erlang-server/handler.mustache @@ -21,9 +21,30 @@ %% Handlers -export([handle_request_json/2]). +{{#operations}}{{#operation}}{{#authMethods}}{{#isApiKey}} +-callback authorize_api_key + (OperationID :: {{operationID}}, ApiKey :: {{packageName}}:api_key(), Opts :: undefined | term()) -> {{packageName}}::auth_result(); +{{/isApiKey}}{{/authMethods}}{{/operation}}{{/operations}} + (_, _) -> true. + +%% Handler behaviour definition +{{#operations}}{{#operation}} +-callback {{operationId}}(Request, Context) -> + {ok | error, {Code, Headers, Body}} +when + Request :: #{ +{{#allParams}} {{^isBodyParam}}'{{baseName}}'{{/isBodyParam}}{{#isBodyParam}}'{{dataType}}'{{/isBodyParam}} {{#required}}:={{/required}}{{^required}}=>{{/required}} {{#isListContainer}}[{{^isBodyParam}}{{>api.type_spec}}{{/isBodyParam}}{{#isBodyParam}}{{packageName}}:body_schema(){{/isBodyParam}}]{{/isListContainer}}{{^isListContainer}}{{^isBodyParam}}{{>api.type_spec}}{{/isBodyParam}}{{#isBodyParam}}{{packageName}}:body_schema(){{/isBodyParam}}{{/isListContainer}}{{^hasMore}}{{/hasMore}}{{#hasMore}},{{/hasMore}} +{{/allParams}} + }, + Context :: {{packageName}}:request_context(), + Code :: {{#responses}}{{code}}{{#hasMore}} | {{/hasMore}}{{/responses}}, + Headers :: cowboy:http_headers(), + Body :: {{packageName}}:body_schema() | [{{packageName}}:body_schema()] | undefined. +{{/operation}}{{/operations}} + -record(state, { operation_id :: {{packageName}}:operation_id(), - logic_handler :: module(), + logic_handler :: {{packageName}}_router:handeler(), context :: {{packageName}}:request_context() }). @@ -77,39 +98,18 @@ allowed_methods(Req, State) -> Req :: cowboy_req:req(), State :: state() }. -{{#operations}}{{#operation}} +{{#operations}}{{#operation}}{{#authMethods}}{{#isApiKey}} is_authorized( Req0, State = #state{ - operation_id = '{{operationId}}' = OperationID, - logic_handler = LogicHandler, - context = Context + operation_id = '{{operationId}}' = OperationID } ) -> -{{#authMethods}} - {{#isApiKey}} From = {{#isKeyInQuery}}qs_val{{/isKeyInQuery}}{{#isKeyInHeader}}header{{/isKeyInHeader}}, - Result = {{packageName}}_handler_api:authorize_api_key( - LogicHandler, - OperationID, - From, - '{{keyParamName}}', - Req0 - ), - case Result of - {true, AuthContext, Req} -> - NewContext = Context#{ - auth_context => AuthContext - }, - {true, Req, State#state{context = NewContext}}; - {false, AuthHeader, Req} -> - {{false, AuthHeader}, Req, State} - end; - {{/isApiKey}} -{{/authMethods}} -{{/operation}}{{/operations}} + authorize_api_key({{packageName}}_handler_api:get_api_key(From, '{{keyParamName}}', Req0)); +{{/isApiKey}}{{/authMethods}}{{/operation}}{{/operations}} is_authorized(Req, State) -> - {{false, <<"">>}, Req, State}. + handle_auth_result(false, Req, State). -spec content_types_accepted(Req :: cowboy_req:req(), State :: state()) -> { @@ -222,12 +222,26 @@ handle_request_json( %% Internal +authorize_api_key({undefined, Req}, State) -> + handle_auth_result(false, Req, State); +authorize_api_key({ApiKey, Req}, State = #state{ + operation_id = OperationID, + logic_handler = LogicHandler +}) -> + {Handler, Opts} = {{packageName}}_utils:get_mod_opts(LogicHandler), + handle_auth_result(Handler:authorize_api_key(OperationID, ApiKey, Opts), Req, State). + +handle_auth_result(false, Req, State) -> + {{false, <<>>}, Req, State}; +handle_auth_result({true, AuthContext}, Req, State = #state{context = Context}) -> + {true, Req, State#state{context = Context#{auth_context => AuthContext}}}. populate_request(OperationID, Req) -> {{packageName}}_handler_api:populate_request(get_request_spec(OperationID), Req). handle_request(LogicHandler, OperationID, Populated, Context) -> - {{packageName}}_logic_handler:handle_request(LogicHandler, OperationID, Populated, Context). + {Handler, Opts} = {{packageName}}_utils:get_mod_opts(LogicHandler), + Handler:OperationID(Populated, Context). validate_response(error, _, _) -> ok; diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/handler_api.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/handler_api.mustache index a78828ca0fa..8dd88ee227d 100644 --- a/modules/swagger-codegen/src/main/resources/erlang-server/handler_api.mustache +++ b/modules/swagger-codegen/src/main/resources/erlang-server/handler_api.mustache @@ -1,7 +1,7 @@ %% -*- mode: erlang -*- -module({{packageName}}_handler_api). --export([authorize_api_key/5]). +-export([get_api_key/5]). -export([determine_peer/1]). -export([populate_request/2]). -export([validate_response/2]). @@ -30,35 +30,22 @@ %% API --spec authorize_api_key( - LogicHandler :: module(), - OperationID :: {{packageName}}:operation_id(), +-spec get_api_key( From :: header | qs_val, KeyParam :: iodata() | atom(), Req :: cowboy_req:req() -)-> - {true, Context :: {{packageName}}:auth_context(), Req ::cowboy_req:req()} | - {false, AuthHeader :: binary(), Req ::cowboy_req:req()}. +) -> + {\{{packageName}}:api_key(), Req :: cowboy_req:req()} | + {undefined , Req :: cowboy_req:req()}. -authorize_api_key(LogicHandler, OperationID, From, KeyParam, Req0) -> +get_api_key(From, KeyParam, Req0) -> {ok, ApiKey, Req} = get_value(From, KeyParam, Req0), case ApiKey of undefined -> AuthHeader = <<"">>, - {false, AuthHeader, Req}; + {undefined, 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 + {ApiKey, Req} end. -spec determine_peer(Req :: cowboy_req:req()) -> @@ -73,7 +60,7 @@ determine_peer(Req) -> {determine_peer_from_header(Value, Peer), Req2}. -spec populate_request(Spec :: request_spec(), Req :: cowboy_req:req()) -> - {ok, Populated :: {{packageName}}:object(), Req :: cowboy_req:req()} | + {ok, Populated :: {{packageName}}:object() , Req :: cowboy_req:req()} | {error, Message :: {{packageName}}:error_reason(), Req :: cowboy_req:req()}. populate_request(Spec, Req) -> @@ -81,7 +68,7 @@ populate_request(Spec, Req) -> -spec validate_response( Spec :: response_spec(), - RespBody :: {{packageName}}:object() | [{{packageName}}:object()] | undefined + RespBody :: {{packageName}}:body_schema() | [{{packageName}}:body_schema()] | undefined ) -> ok | no_return(). @@ -93,7 +80,12 @@ validate_response(Spec, RespBody) -> erlang:error({response_validation_failed, Error, RespBody}) end. --spec encode_response(Resp :: {{packageName}}_logic_handler:response()) -> +-spec encode_response(Response) -> + Response :: { + cowboy:http_status(), + cowboy:http_headers(), + {{packageName}}:body_schema() | [{{packageName}}:body_schema()] | undefined + }, Encoded :: response(). encode_response(Resp = {_, _, undefined}) -> @@ -216,7 +208,7 @@ get_value(binding, Name, Req0) -> {ok, Value, Req}. -spec decode_body(Body :: binary()) -> - {ok, Decoded :: {{packageName}}:object() | undefined} | + {ok, Decoded :: map() | undefined} | {error, Message :: {{packageName}}:error_reason()}. decode_body(<<>>) -> {ok, undefined}; diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/logic_handler.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/logic_handler.mustache deleted file mode 100644 index 01f50ca666c..00000000000 --- a/modules/swagger-codegen/src/main/resources/erlang-server/logic_handler.mustache +++ /dev/null @@ -1,65 +0,0 @@ --module({{packageName}}_logic_handler). - --export([handle_request/4]). - --type response() :: { - Status :: cowboy:http_status(), - Headers :: cowboy:http_headers(), - Body :: {{packageName}}:object() | [{{packageName}}:object()] | undefined -}. - --export_type([response/0]). - - -%% Behaviour definition - -{{#authMethods}} - {{#isApiKey}} --export([authorize_api_key/3]). - {{/isApiKey}} -{{/authMethods}} - -{{#authMethods}} - {{#isApiKey}} --callback authorize_api_key( - OperationID :: {{packageName}}:operation_id(), - ApiKey :: {{packageName}}:api_key() -) -> - Result :: boolean() | {boolean(), {{packageName}}:auth_context()}. - {{/isApiKey}} -{{/authMethods}} - - --callback handle_request( - OperationID :: {{packageName}}:operation_id(), - Request :: {{packageName}}:object(), - Context :: {{packageName}}:request_context() -) -> - {ok | error, response()}. - --spec handle_request( - Handler :: module(), - OperationID :: {{packageName}}:operation_id(), - Request :: {{packageName}}:object(), - Context :: {{packageName}}:request_context() -) -> - {ok | error, response()}. - - -%% API - -handle_request(Handler, OperationID, Request, Context) -> - Handler:handle_request(OperationID, Request, Context). - -{{#authMethods}} - {{#isApiKey}} --spec authorize_api_key( - Handler :: module(), - OperationID :: {{packageName}}:operation_id(), - ApiKey :: {{packageName}}:api_key() -) -> - Result :: false | {true, {{packageName}}:auth_context()}. -authorize_api_key(Handler, OperationID, ApiKey) -> - Handler:authorize_api_key(OperationID, ApiKey). - {{/isApiKey}} -{{/authMethods}} diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/router.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/router.mustache index 82c48bdfe66..32b9cb7a234 100644 --- a/modules/swagger-codegen/src/main/resources/erlang-server/router.mustache +++ b/modules/swagger-codegen/src/main/resources/erlang-server/router.mustache @@ -6,36 +6,38 @@ Method :: binary() => OperationID :: {{packageName}}:operation_id() }. +-type handler() :: module() | {module(), _Opts}. + -type init_opts() :: { Operations :: operations(), - LogicHandler :: atom() + LogicHandler :: handler() }. -export_type([init_opts/0]). --spec get_paths(LogicHandler :: atom()) -> [{'_',[{ +-spec get_paths(LogicHandlers :: #{atom() => handler()}) -> [{'_',[{ Path :: string(), Handler :: atom(), InitOpts :: init_opts() }]}]. -get_paths(LogicHandler) -> +get_paths(LogicHandlers) -> PreparedPaths = maps:fold( - fun(Path, #{operations := Operations, handler := Handler}, Acc) -> - [{Path, Handler, Operations} | Acc] + fun(Path, #{operations := Operations, handler := Handler, key := Key}, Acc) -> + [{Path, Handler, {Key, Operations}} | Acc] end, [], group_paths() ), [ {'_', - [{P, H, {O, LogicHandler}} || {P, H, O} <- PreparedPaths] + [{P, H, {O, maps:get(K, LogicHandlers)}} || {P, H, {K, O}} <- PreparedPaths] } ]. group_paths() -> maps:fold( - fun(OperationID, #{path := Path, method := Method, handler := Handler}, Acc) -> + fun(OperationID, #{path := Path, method := Method, handler := Handler, key := Key}, Acc) -> case maps:find(Path, Acc) of {ok, PathInfo0 = #{operations := Operations0}} -> Operations = Operations0#{Method => OperationID}, @@ -43,7 +45,7 @@ group_paths() -> Acc#{Path => PathInfo}; error -> Operations = #{Method => OperationID}, - PathInfo = #{handler => Handler, operations => Operations}, + PathInfo = #{handler => Handler, operations => Operations, key => Key}, Acc#{Path => PathInfo} end end, @@ -56,6 +58,7 @@ get_operations() -> '{{operationId}}' => #{ path => "{{basePathWithoutHost}}{{path}}", method => <<"{{httpMethod}}">>, - handler => '{{classname}}' + handler => '{{classname}}', + key => '{{classVarName}}' }{{#hasMore}},{{/hasMore}}{{/operation}}{{#hasMore}},{{/hasMore}}{{/operations}}{{/apis}}{{/apiInfo}} }. diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/schema.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/schema.mustache index d0184de5a1d..30db55043bd 100644 --- a/modules/swagger-codegen/src/main/resources/erlang-server/schema.mustache +++ b/modules/swagger-codegen/src/main/resources/erlang-server/schema.mustache @@ -7,7 +7,7 @@ -define(DEFINITIONS, <<"definitions">>). --spec get() -> {{packageName}}:object(). +-spec get() -> map(). get() -> ct_expand:term(enumerate_discriminator_children(maps:with([?DEFINITIONS], get_raw()))). diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/types.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/types.mustache index a1c5b009806..6336b5a0581 100644 --- a/modules/swagger-codegen/src/main/resources/erlang-server/types.mustache +++ b/modules/swagger-codegen/src/main/resources/erlang-server/types.mustache @@ -1,3 +1,4 @@ +%% -*- mode: erlang -*- -module({{packageName}}). %% Type definitions @@ -10,10 +11,16 @@ -export_type([api_key/0]). -export_type([object/0]). --type auth_context() :: any(). -type operation_id() :: atom(). +-type auth_context() :: any(). +-type auth_result() :: false | {true, auth_context()}. -type api_key() :: binary(). --type object() :: map(). + +-type body_schema() :: #{binary() => term()}. +-type object() :: #{ + binary() => term(), + atom() => term() +}. -type client_peer() :: #{ ip_address => IP :: inet:ip_address(), diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/utils.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/utils.mustache index 823f21f7675..c8e79dc6d17 100644 --- a/modules/swagger-codegen/src/main/resources/erlang-server/utils.mustache +++ b/modules/swagger-codegen/src/main/resources/erlang-server/utils.mustache @@ -20,6 +20,11 @@ -export([join/1]). -export([join/2]). +-spec get_handler_and_opts({{package_name}}:handler()) -> {module(), undefined | term()}. +get_handler_and_opts({Handler, Opts}) -> + {Handler, Opts}; +get_handler_and_opts(Handler) -> + get_handler_and_opts({Handler, undefined}). -spec to_binary(iodata() | atom() | number()) -> binary(). diff --git a/modules/swagger-codegen/src/main/resources/erlang-server/validation.mustache b/modules/swagger-codegen/src/main/resources/erlang-server/validation.mustache index 7e31c507599..b10ed3865b9 100644 --- a/modules/swagger-codegen/src/main/resources/erlang-server/validation.mustache +++ b/modules/swagger-codegen/src/main/resources/erlang-server/validation.mustache @@ -1,3 +1,4 @@ +%% -*- mode: erlang -*- -module({{packageName}}_validation). -export([prepare_request_param/3]). @@ -52,7 +53,7 @@ prepare_request_param(Rules, Name, Value) -> -spec validate_response( Spec :: response_spec(), - Resp :: {{packageName}}:object() | [{{packageName}}:object()] | undefined + Resp :: {{packageName}}: | [{{packageName}}:body_schema()] | undefined ) -> ok | {error, Error :: error()}.