Files
openapi-generator/samples/server/petstore/erlang-server/src/openapi_api.erl
William Cheng 0204bf4ae2 Squashed commit of the following:
commit c5a0d0f7394aa742fa336fff7e7c1d3049761868
Merge: 8c4991ba3e f8ff8c87609
Author: William Cheng <wing328hk@gmail.com>
Date:   Tue Aug 17 18:28:12 2021 +0800

    Merge branch 'mustache-linting' of https://github.com/NathanBaulch/openapi-generator into NathanBaulch-mustache-linting

commit f8ff8c87609b1ca36fa26fb8474806999638195e
Author: Nathan Baulch <nathan.baulch@gmail.com>
Date:   Thu Aug 5 14:12:47 2021 +1000

    Reorder tags that handle missing values

commit f5d8a33709d6a3f846a9fe4520b78c3d637051d9
Author: Nathan Baulch <nathan.baulch@gmail.com>
Date:   Thu Aug 5 14:08:59 2021 +1000

    Use dot notation where possible

commit 493d14921e2333f3ae19ef6fc89318b7e263a80c
Author: Nathan Baulch <nathan.baulch@gmail.com>
Date:   Thu Aug 5 14:10:49 2021 +1000

    Remove empty tags

commit 32480dc53f48227d55531b94e307d72671373737
Author: Nathan Baulch <nathan.baulch@gmail.com>
Date:   Thu Aug 5 10:41:58 2021 +1000

    Remove redundant sections

commit a8edabd722c34aa094b4aeb11c22664529c3a219
Author: Nathan Baulch <nathan.baulch@gmail.com>
Date:   Wed Aug 4 22:02:22 2021 +1000

    Trim extra EOF new lines

commit e89bd7458e3594bf0d30e580bc9408e45b018a57
Author: Nathan Baulch <nathan.baulch@gmail.com>
Date:   Wed Aug 4 21:59:26 2021 +1000

    Trim trailing whitespace
2021-08-17 18:37:51 +08:00

757 lines
20 KiB
Erlang

-module(openapi_api).
-export([request_params/1]).
-export([request_param_info/2]).
-export([populate_request/3]).
-export([validate_response/4]).
%% exported to silence openapi complains
-export([get_value/3, validate_response_body/4]).
-type operation_id() :: atom().
-type request_param() :: atom().
-export_type([operation_id/0]).
-spec request_params(OperationID :: operation_id()) -> [Param :: request_param()].
request_params('AddPet') ->
[
'Pet'
];
request_params('DeletePet') ->
[
'petId',
'api_key'
];
request_params('FindPetsByStatus') ->
[
'status'
];
request_params('FindPetsByTags') ->
[
'tags'
];
request_params('GetPetById') ->
[
'petId'
];
request_params('UpdatePet') ->
[
'Pet'
];
request_params('UpdatePetWithForm') ->
[
'petId',
'name',
'status'
];
request_params('UploadFile') ->
[
'petId',
'additionalMetadata',
'file'
];
request_params('DeleteOrder') ->
[
'orderId'
];
request_params('GetInventory') ->
[
];
request_params('GetOrderById') ->
[
'orderId'
];
request_params('PlaceOrder') ->
[
'Order'
];
request_params('CreateUser') ->
[
'User'
];
request_params('CreateUsersWithArrayInput') ->
[
'list'
];
request_params('CreateUsersWithListInput') ->
[
'list'
];
request_params('DeleteUser') ->
[
'username'
];
request_params('GetUserByName') ->
[
'username'
];
request_params('LoginUser') ->
[
'username',
'password'
];
request_params('LogoutUser') ->
[
];
request_params('UpdateUser') ->
[
'username',
'User'
];
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()]
}.
request_param_info('AddPet', 'Pet') ->
#{
source => body,
rules => [
schema,
required
]
};
request_param_info('DeletePet', 'petId') ->
#{
source => binding ,
rules => [
{type, 'integer'},
required
]
};
request_param_info('DeletePet', 'api_key') ->
#{
source => header,
rules => [
{type, 'binary'},
not_required
]
};
request_param_info('FindPetsByStatus', 'status') ->
#{
source => qs_val ,
rules => [
{enum, ['available', 'pending', 'sold'] },
required
]
};
request_param_info('FindPetsByTags', 'tags') ->
#{
source => qs_val ,
rules => [
required
]
};
request_param_info('GetPetById', 'petId') ->
#{
source => binding ,
rules => [
{type, 'integer'},
required
]
};
request_param_info('UpdatePet', 'Pet') ->
#{
source => body,
rules => [
schema,
required
]
};
request_param_info('UpdatePetWithForm', 'petId') ->
#{
source => binding ,
rules => [
{type, 'integer'},
required
]
};
request_param_info('UpdatePetWithForm', 'name') ->
#{
source => body,
rules => [
{type, 'binary'},
not_required
]
};
request_param_info('UpdatePetWithForm', 'status') ->
#{
source => body,
rules => [
{type, 'binary'},
not_required
]
};
request_param_info('UploadFile', 'petId') ->
#{
source => binding ,
rules => [
{type, 'integer'},
required
]
};
request_param_info('UploadFile', 'additionalMetadata') ->
#{
source => body,
rules => [
{type, 'binary'},
not_required
]
};
request_param_info('UploadFile', 'file') ->
#{
source => body,
rules => [
{type, 'binary'},
not_required
]
};
request_param_info('DeleteOrder', 'orderId') ->
#{
source => binding ,
rules => [
{type, 'binary'},
required
]
};
request_param_info('GetOrderById', 'orderId') ->
#{
source => binding ,
rules => [
{type, 'integer'},
{max, 5 },
{min, 1 },
required
]
};
request_param_info('PlaceOrder', 'Order') ->
#{
source => body,
rules => [
schema,
required
]
};
request_param_info('CreateUser', 'User') ->
#{
source => body,
rules => [
schema,
required
]
};
request_param_info('CreateUsersWithArrayInput', 'list') ->
#{
source => body,
rules => [
schema,
required
]
};
request_param_info('CreateUsersWithListInput', 'list') ->
#{
source => body,
rules => [
schema,
required
]
};
request_param_info('DeleteUser', 'username') ->
#{
source => binding ,
rules => [
{type, 'binary'},
required
]
};
request_param_info('GetUserByName', 'username') ->
#{
source => binding ,
rules => [
{type, 'binary'},
required
]
};
request_param_info('LoginUser', 'username') ->
#{
source => qs_val ,
rules => [
{type, 'binary'},
{pattern, "^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$" },
required
]
};
request_param_info('LoginUser', 'password') ->
#{
source => qs_val ,
rules => [
{type, 'binary'},
required
]
};
request_param_info('UpdateUser', 'username') ->
#{
source => binding ,
rules => [
{type, 'binary'},
required
]
};
request_param_info('UpdateUser', 'User') ->
#{
source => body,
rules => [
schema,
required
]
};
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, #{}).
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));
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
{error, Reason, Req} ->
{error, Reason, Req};
{Value, Req} ->
case prepare_param(Rules, Name, Value, ValidatorState) of
{ok, Result} -> {ok, Name, Result, Req};
{error, Reason} ->
{error, Reason, Req}
end
end.
-spec validate_response(
OperationID :: operation_id(),
Code :: 200..599,
Body :: jesse:json_term(),
ValidatorState :: jesse_state:state()
) -> ok | no_return().
validate_response('AddPet', 200, Body, ValidatorState) ->
validate_response_body('Pet', 'Pet', Body, ValidatorState);
validate_response('AddPet', 405, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('DeletePet', 400, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('FindPetsByStatus', 200, Body, ValidatorState) ->
validate_response_body('list', 'Pet', Body, ValidatorState);
validate_response('FindPetsByStatus', 400, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('FindPetsByTags', 200, Body, ValidatorState) ->
validate_response_body('list', 'Pet', Body, ValidatorState);
validate_response('FindPetsByTags', 400, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('GetPetById', 200, Body, ValidatorState) ->
validate_response_body('Pet', 'Pet', Body, ValidatorState);
validate_response('GetPetById', 400, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('GetPetById', 404, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('UpdatePet', 200, Body, ValidatorState) ->
validate_response_body('Pet', 'Pet', Body, ValidatorState);
validate_response('UpdatePet', 400, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('UpdatePet', 404, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('UpdatePet', 405, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('UpdatePetWithForm', 405, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('UploadFile', 200, Body, ValidatorState) ->
validate_response_body('ApiResponse', 'ApiResponse', Body, ValidatorState);
validate_response('DeleteOrder', 400, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('DeleteOrder', 404, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('GetInventory', 200, Body, ValidatorState) ->
validate_response_body('map', 'integer', Body, ValidatorState);
validate_response('GetOrderById', 200, Body, ValidatorState) ->
validate_response_body('Order', 'Order', Body, ValidatorState);
validate_response('GetOrderById', 400, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('GetOrderById', 404, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('PlaceOrder', 200, Body, ValidatorState) ->
validate_response_body('Order', 'Order', Body, ValidatorState);
validate_response('PlaceOrder', 400, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('CreateUser', 0, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('CreateUsersWithArrayInput', 0, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('CreateUsersWithListInput', 0, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('DeleteUser', 400, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('DeleteUser', 404, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('GetUserByName', 200, Body, ValidatorState) ->
validate_response_body('User', 'User', Body, ValidatorState);
validate_response('GetUserByName', 400, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('GetUserByName', 404, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('LoginUser', 200, Body, ValidatorState) ->
validate_response_body('binary', 'string', Body, ValidatorState);
validate_response('LoginUser', 400, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('LogoutUser', 0, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('UpdateUser', 400, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response('UpdateUser', 404, Body, ValidatorState) ->
validate_response_body('', '', Body, ValidatorState);
validate_response(_OperationID, _Code, _Body, _ValidatorState) ->
ok.
validate_response_body('list', ReturnBaseType, Body, ValidatorState) ->
[
validate(schema, ReturnBaseType, Item, ValidatorState)
|| Item <- Body];
validate_response_body(_, ReturnBaseType, Body, ValidatorState) ->
validate(schema, ReturnBaseType, Body, ValidatorState).
%%%
validate(Rule = required, Name, Value, _ValidatorState) ->
case Value of
undefined -> validation_error(Rule, Name);
_ -> ok
end;
validate(not_required, _Name, _Value, _ValidatorState) ->
ok;
validate(_, _Name, undefined, _ValidatorState) ->
ok;
validate(Rule = {type, 'integer'}, Name, Value, _ValidatorState) ->
try
{ok, openapi_utils:to_int(Value)}
catch
error:badarg ->
validation_error(Rule, Name)
end;
validate(Rule = {type, 'float'}, Name, Value, _ValidatorState) ->
try
{ok, openapi_utils: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)
end;
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) ->
case is_binary(Value) of
true -> ok;
false -> validation_error(Rule, Name)
end;
validate(Rule = {enum, Values}, Name, Value, _ValidatorState) ->
try
FormattedValue = erlang:binary_to_existing_atom(Value, utf8),
case lists:member(FormattedValue, Values) of
true -> {ok, FormattedValue};
false -> validation_error(Rule, Name)
end
catch
error:badarg ->
validation_error(Rule, Name)
end;
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) ->
case Value > ExclusiveMax of
true -> ok;
false -> validation_error(Rule, Name)
end;
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) ->
case Value =< ExclusiveMin of
true -> ok;
false -> validation_error(Rule, Name)
end;
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) ->
case size(Value) >= MinLength of
true -> ok;
false -> validation_error(Rule, Name)
end;
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) ->
Definition = list_to_binary("#/components/schemas/" ++ openapi_utils:to_list(Name)),
try
_ = validate_with_schema(Value, Definition, ValidatorState),
ok
catch
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) ->
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(ViolatedRule, Name, Info) ->
throw({wrong_param, Name, 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()}.
get_value(body, _Name, Req0) ->
{ok, Body, Req} = cowboy_req:read_body(Req0),
case prepare_body(Body) of
{error, Reason} ->
{error, Reason, Req};
Value ->
{Value, Req}
end;
get_value(qs_val, Name, Req) ->
QS = cowboy_req:parse_qs(Req),
Value = openapi_utils:get_opt(openapi_utils:to_qs(Name), QS),
{Value, Req};
get_value(header, Name, Req) ->
Headers = cowboy_req:headers(Req),
Value = maps:get(openapi_utils:to_header(Name), Headers, undefined),
{Value, Req};
get_value(binding, Name, Req) ->
Value = cowboy_req:binding(openapi_utils:to_binding(Name), Req),
{Value, Req}.
prepare_body(Body) ->
case Body of
<<"">> -> <<"">>;
_ ->
try
jsx:decode(Body, [return_maps])
catch
error:_ ->
{error, {invalid_body, not_json, Body}}
end
end.
validate_with_schema(Body, Definition, ValidatorState) ->
jesse_schema_validator:validate_with_state(
[{<<"$ref">>, Definition}],
Body,
ValidatorState
).
prepare_param(Rules, Name, Value, ValidatorState) ->
try
Result = lists:foldl(
fun(Rule, Acc) ->
case validate(Rule, Name, Acc, ValidatorState) of
ok -> Acc;
{ok, Prepared} -> Prepared
end
end,
Value,
Rules
),
{ok, Result}
catch
throw:Reason ->
{error, Reason}
end.
binary_to_lower(V) when is_binary(V) ->
list_to_binary(string:to_lower(openapi_utils:to_list(V))).