Skip to content

Commit

Permalink
[erlang-client] fix URL paths (#14988)
Browse files Browse the repository at this point in the history
* integers parameters in URL did not work as expected
* so now, if the parameter is an integer, we convert it to binary before
  passing to `hackney_url:make_url/3`
  • Loading branch information
dweinstein authored Mar 21, 2023
1 parent 849708d commit fc91fca
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
Cfg = maps:get(cfg, Optional, application:get_env({{packageName}}_api, config, #{})),

Method = {{httpMethod}},
Path = ["{{{replacedPathName}}}"],
Path = [?BASE_URL, "{{{replacedPathName}}}"],
QS = {{#queryParams.isEmpty}}[]{{/queryParams.isEmpty}}{{^queryParams.isEmpty}}lists:flatten([{{#joinWithComma}}{{#queryParams}}{{#required}}{{#qsEncode}}{{this}}{{/qsEncode}} {{/required}}{{/queryParams}}{{/joinWithComma}}])++{{packageName}}_utils:optional_params([{{#joinWithComma}}{{#queryParams}}{{^required}} '{{baseName}}'{{/required}}{{/queryParams}}{{/joinWithComma}}], _OptionalParams){{/queryParams.isEmpty}},
Headers = {{#headerParams.isEmpty}}[]{{/headerParams.isEmpty}}{{^headerParams.isEmpty}}[{{#headerParams}}{{#required}} {<<"{{baseName}}">>, {{paramName}}}{{/required}}{{/headerParams}}]++{{packageName}}_utils:optional_params([{{#joinWithComma}}{{#headerParams}}{{^required}} '{{baseName}}'{{/required}}{{/headerParams}}{{/joinWithComma}}], _OptionalParams){{/headerParams.isEmpty}},
Body1 = {{^formParams.isEmpty}}{form, [{{#joinWithComma}}{{#formParams}}{{#required}} {<<"{{baseName}}">>, {{paramName}}}{{/required}}{{/formParams}}{{/joinWithComma}}]++{{packageName}}_utils:optional_params([{{#joinWithComma}}{{#formParams}}{{^required}} '{{baseName}}'{{/required}}{{/formParams}}{{/joinWithComma}}], _OptionalParams)}{{/formParams.isEmpty}}{{#formParams.isEmpty}}{{#bodyParams.isEmpty}}[]{{/bodyParams.isEmpty}}{{^bodyParams.isEmpty}}{{#bodyParams}}{{paramName}}{{/bodyParams}}{{/bodyParams.isEmpty}}{{/formParams.isEmpty}},
ContentTypeHeader = {{packageName}}_utils:select_header_content_type([{{#consumes}}{{^-first}}, {{/-first}}<<"{{mediaType}}">>{{/consumes}}]),
Opts = maps:get(hackney_opts, Optional, []),

{{packageName}}_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
{{packageName}}_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).

{{/operation}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
request(_Ctx, Method, Path, QS, Headers, Body, Opts, Cfg) ->
{Headers1, QS1} = update_params_with_auth(Cfg, Headers, QS),
Host = maps:get(host, Cfg, "localhost:8001"),
Url = hackney_url:make_url(Host, Path, QS1),
Path1 = prepare_path(Path),
Url = hackney_url:make_url(Host, Path1, QS1),

ConfigHackneyOpts = maps:get(hackney_opts, Cfg, []),
Body1 = case lists:keyfind(<<"Content-Type">>, 1, Headers1) of
Expand All @@ -38,6 +39,14 @@ request(_Ctx, Method, Path, QS, Headers, Body, Opts, Cfg) ->
headers => RespHeaders}}
end.

prepare_path(Path) ->
lists:map(fun convert/1, Path).

convert(PathPart) when is_integer(PathPart) ->
integer_to_binary(PathPart);
convert(PathPart) when is_list(PathPart) or is_binary(PathPart) ->
PathPart.

decode_response(Headers, Body) ->
case lists:keyfind(<<"Content-Type">>, 1, Headers) of
{_, <<"application/json", _/binary>>} ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6.5.0-SNAPSHOT
5.3.0-SNAPSHOT
37 changes: 21 additions & 16 deletions samples/client/petstore/erlang-client/src/petstore_pet_api.erl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
-define(BASE_URL, <<"/v2">>).

%% @doc Add a new pet to the store
%%
-spec add_pet(ctx:ctx(), petstore_pet:petstore_pet()) -> {ok, petstore_pet:petstore_pet(), petstore_utils:response_info()} | {ok, hackney:client_ref()} | {error, term(), petstore_utils:response_info()}.
add_pet(Ctx, PetstorePet) ->
add_pet(Ctx, PetstorePet, #{}).
Expand All @@ -22,16 +23,17 @@ add_pet(Ctx, PetstorePet, Optional) ->
Cfg = maps:get(cfg, Optional, application:get_env(petstore_api, config, #{})),

Method = post,
Path = ["/pet"],
Path = [?BASE_URL, "/pet"],
QS = [],
Headers = [],
Body1 = PetstorePet,
ContentTypeHeader = petstore_utils:select_header_content_type([<<"application/json">>, <<"application/xml">>]),
Opts = maps:get(hackney_opts, Optional, []),

petstore_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
petstore_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).

%% @doc Deletes a pet
%%
-spec delete_pet(ctx:ctx(), integer()) -> {ok, [], petstore_utils:response_info()} | {ok, hackney:client_ref()} | {error, term(), petstore_utils:response_info()}.
delete_pet(Ctx, PetId) ->
delete_pet(Ctx, PetId, #{}).
Expand All @@ -42,14 +44,14 @@ delete_pet(Ctx, PetId, Optional) ->
Cfg = maps:get(cfg, Optional, application:get_env(petstore_api, config, #{})),

Method = delete,
Path = ["/pet/", PetId, ""],
Path = [?BASE_URL, "/pet/", PetId, ""],
QS = [],
Headers = []++petstore_utils:optional_params(['api_key'], _OptionalParams),
Body1 = [],
ContentTypeHeader = petstore_utils:select_header_content_type([]),
Opts = maps:get(hackney_opts, Optional, []),

petstore_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
petstore_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).

%% @doc Finds Pets by status
%% Multiple status values can be provided with comma separated strings
Expand All @@ -63,14 +65,14 @@ find_pets_by_status(Ctx, Status, Optional) ->
Cfg = maps:get(cfg, Optional, application:get_env(petstore_api, config, #{})),

Method = get,
Path = ["/pet/findByStatus"],
Path = [?BASE_URL, "/pet/findByStatus"],
QS = lists:flatten([[{<<"status">>, X} || X <- Status]])++petstore_utils:optional_params([], _OptionalParams),
Headers = [],
Body1 = [],
ContentTypeHeader = petstore_utils:select_header_content_type([]),
Opts = maps:get(hackney_opts, Optional, []),

petstore_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
petstore_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).

%% @doc Finds Pets by tags
%% Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
Expand All @@ -84,14 +86,14 @@ find_pets_by_tags(Ctx, Tags, Optional) ->
Cfg = maps:get(cfg, Optional, application:get_env(petstore_api, config, #{})),

Method = get,
Path = ["/pet/findByTags"],
Path = [?BASE_URL, "/pet/findByTags"],
QS = lists:flatten([[{<<"tags">>, X} || X <- Tags]])++petstore_utils:optional_params([], _OptionalParams),
Headers = [],
Body1 = [],
ContentTypeHeader = petstore_utils:select_header_content_type([]),
Opts = maps:get(hackney_opts, Optional, []),

petstore_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
petstore_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).

%% @doc Find pet by ID
%% Returns a single pet
Expand All @@ -105,16 +107,17 @@ get_pet_by_id(Ctx, PetId, Optional) ->
Cfg = maps:get(cfg, Optional, application:get_env(petstore_api, config, #{})),

Method = get,
Path = ["/pet/", PetId, ""],
Path = [?BASE_URL, "/pet/", PetId, ""],
QS = [],
Headers = [],
Body1 = [],
ContentTypeHeader = petstore_utils:select_header_content_type([]),
Opts = maps:get(hackney_opts, Optional, []),

petstore_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
petstore_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).

%% @doc Update an existing pet
%%
-spec update_pet(ctx:ctx(), petstore_pet:petstore_pet()) -> {ok, petstore_pet:petstore_pet(), petstore_utils:response_info()} | {ok, hackney:client_ref()} | {error, term(), petstore_utils:response_info()}.
update_pet(Ctx, PetstorePet) ->
update_pet(Ctx, PetstorePet, #{}).
Expand All @@ -125,16 +128,17 @@ update_pet(Ctx, PetstorePet, Optional) ->
Cfg = maps:get(cfg, Optional, application:get_env(petstore_api, config, #{})),

Method = put,
Path = ["/pet"],
Path = [?BASE_URL, "/pet"],
QS = [],
Headers = [],
Body1 = PetstorePet,
ContentTypeHeader = petstore_utils:select_header_content_type([<<"application/json">>, <<"application/xml">>]),
Opts = maps:get(hackney_opts, Optional, []),

petstore_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
petstore_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).

%% @doc Updates a pet in the store with form data
%%
-spec update_pet_with_form(ctx:ctx(), integer()) -> {ok, [], petstore_utils:response_info()} | {ok, hackney:client_ref()} | {error, term(), petstore_utils:response_info()}.
update_pet_with_form(Ctx, PetId) ->
update_pet_with_form(Ctx, PetId, #{}).
Expand All @@ -145,16 +149,17 @@ update_pet_with_form(Ctx, PetId, Optional) ->
Cfg = maps:get(cfg, Optional, application:get_env(petstore_api, config, #{})),

Method = post,
Path = ["/pet/", PetId, ""],
Path = [?BASE_URL, "/pet/", PetId, ""],
QS = [],
Headers = [],
Body1 = {form, []++petstore_utils:optional_params(['name', 'status'], _OptionalParams)},
ContentTypeHeader = petstore_utils:select_header_content_type([<<"application/x-www-form-urlencoded">>]),
Opts = maps:get(hackney_opts, Optional, []),

petstore_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
petstore_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).

%% @doc uploads an image
%%
-spec upload_file(ctx:ctx(), integer()) -> {ok, petstore_api_response:petstore_api_response(), petstore_utils:response_info()} | {ok, hackney:client_ref()} | {error, term(), petstore_utils:response_info()}.
upload_file(Ctx, PetId) ->
upload_file(Ctx, PetId, #{}).
Expand All @@ -165,13 +170,13 @@ upload_file(Ctx, PetId, Optional) ->
Cfg = maps:get(cfg, Optional, application:get_env(petstore_api, config, #{})),

Method = post,
Path = ["/pet/", PetId, "/uploadImage"],
Path = [?BASE_URL, "/pet/", PetId, "/uploadImage"],
QS = [],
Headers = [],
Body1 = {form, []++petstore_utils:optional_params(['additionalMetadata', 'file'], _OptionalParams)},
ContentTypeHeader = petstore_utils:select_header_content_type([<<"multipart/form-data">>]),
Opts = maps:get(hackney_opts, Optional, []),

petstore_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
petstore_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).


17 changes: 9 additions & 8 deletions samples/client/petstore/erlang-client/src/petstore_store_api.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ delete_order(Ctx, OrderId, Optional) ->
Cfg = maps:get(cfg, Optional, application:get_env(petstore_api, config, #{})),

Method = delete,
Path = ["/store/order/", OrderId, ""],
Path = [?BASE_URL, "/store/order/", OrderId, ""],
QS = [],
Headers = [],
Body1 = [],
ContentTypeHeader = petstore_utils:select_header_content_type([]),
Opts = maps:get(hackney_opts, Optional, []),

petstore_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
petstore_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).

%% @doc Returns pet inventories by status
%% Returns a map of status codes to quantities
Expand All @@ -40,14 +40,14 @@ get_inventory(Ctx, Optional) ->
Cfg = maps:get(cfg, Optional, application:get_env(petstore_api, config, #{})),

Method = get,
Path = ["/store/inventory"],
Path = [?BASE_URL, "/store/inventory"],
QS = [],
Headers = [],
Body1 = [],
ContentTypeHeader = petstore_utils:select_header_content_type([]),
Opts = maps:get(hackney_opts, Optional, []),

petstore_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
petstore_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).

%% @doc Find purchase order by ID
%% For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions
Expand All @@ -61,16 +61,17 @@ get_order_by_id(Ctx, OrderId, Optional) ->
Cfg = maps:get(cfg, Optional, application:get_env(petstore_api, config, #{})),

Method = get,
Path = ["/store/order/", OrderId, ""],
Path = [?BASE_URL, "/store/order/", OrderId, ""],
QS = [],
Headers = [],
Body1 = [],
ContentTypeHeader = petstore_utils:select_header_content_type([]),
Opts = maps:get(hackney_opts, Optional, []),

petstore_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
petstore_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).

%% @doc Place an order for a pet
%%
-spec place_order(ctx:ctx(), petstore_order:petstore_order()) -> {ok, petstore_order:petstore_order(), petstore_utils:response_info()} | {ok, hackney:client_ref()} | {error, term(), petstore_utils:response_info()}.
place_order(Ctx, PetstoreOrder) ->
place_order(Ctx, PetstoreOrder, #{}).
Expand All @@ -81,13 +82,13 @@ place_order(Ctx, PetstoreOrder, Optional) ->
Cfg = maps:get(cfg, Optional, application:get_env(petstore_api, config, #{})),

Method = post,
Path = ["/store/order"],
Path = [?BASE_URL, "/store/order"],
QS = [],
Headers = [],
Body1 = PetstoreOrder,
ContentTypeHeader = petstore_utils:select_header_content_type([<<"application/json">>]),
Opts = maps:get(hackney_opts, Optional, []),

petstore_utils:request(Ctx, Method, [?BASE_URL, Path], QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).
petstore_utils:request(Ctx, Method, Path, QS, ContentTypeHeader++Headers, Body1, Opts, Cfg).


Loading

0 comments on commit fc91fca

Please sign in to comment.