Elixir: Switch Poison to Jason (#16061)

* Switch Poison to Jason

* generate-samples.sh

* Finalize Poison -> Jason switch

* parse date-time values to Elixir DateTime
* improve formatting in various places, so there's less changes by `mix
  format` later
* fix Java version in flake.nix

* Use List.delete/2 instead of Enum.reject/2 for performance reasons

* mix format test/*

* Install dialyxir and fix reported issues

* Fix RequestBuilder.decode/2 hardcoded module name

* Update docs

* Revert changes to API spec (HTTP -> HTTPS)

* Revert uneeded change to Elixir code generator

* Use HTTP in Elixir tests

HTTPS doesn't work for folks who setup petstore.swagger.io as described
in docs/faq-contributing.md.

---------

Co-authored-by: Wojciech Piekutowski <wojciech@piekutowski.net>
This commit is contained in:
Bart ten Brinke 2023-07-20 18:36:50 +02:00 committed by GitHub
parent ddc2b3e560
commit 4ece8e992a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 625 additions and 351 deletions

View File

@ -48,7 +48,6 @@ These options may be applied as additional-properties (cli) or configOptions (pl
<li>AnyType</li> <li>AnyType</li>
<li>Atom</li> <li>Atom</li>
<li>Boolean</li> <li>Boolean</li>
<li>DateTime</li>
<li>Decimal</li> <li>Decimal</li>
<li>Float</li> <li>Float</li>
<li>Integer</li> <li>Integer</li>

View File

@ -13,7 +13,7 @@
devShells.default = pkgs.mkShell devShells.default = pkgs.mkShell
{ {
buildInputs = with pkgs;[ buildInputs = with pkgs;[
jdk8 jdk11
maven maven
]; ];
}; };

View File

@ -59,9 +59,10 @@ public class ElixirClientCodegen extends DefaultCodegen {
String supportedElixirVersion = "1.10"; String supportedElixirVersion = "1.10";
List<String> extraApplications = Arrays.asList(":logger"); List<String> extraApplications = Arrays.asList(":logger");
List<String> deps = Arrays.asList( List<String> deps = Arrays.asList(
"{:tesla, \"~> 1.4\"}", "{:tesla, \"~> 1.7\"}",
"{:poison, \"~> 3.0\"}", "{:jason, \"~> 1.4\"}",
"{:ex_doc, \"~> 0.28\", only: :dev, runtime: false}" "{:ex_doc, \"~> 0.30\", only: :dev, runtime: false}",
"{:dialyxir, \"~> 1.3\", only: [:dev, :test], runtime: false}"
); );
public ElixirClientCodegen() { public ElixirClientCodegen() {
@ -194,7 +195,6 @@ public class ElixirClientCodegen extends DefaultCodegen {
"AnyType", "AnyType",
"Tuple", "Tuple",
"PID", "PID",
"DateTime",
"map()", // This is a workaround, since the DefaultCodeGen uses our elixir TypeSpec datetype to evaluate the primitive "map()", // This is a workaround, since the DefaultCodeGen uses our elixir TypeSpec datetype to evaluate the primitive
"any()" "any()"
) )
@ -210,7 +210,7 @@ public class ElixirClientCodegen extends DefaultCodegen {
typeMapping.put("string", "String"); typeMapping.put("string", "String");
typeMapping.put("byte", "Integer"); typeMapping.put("byte", "Integer");
typeMapping.put("boolean", "Boolean"); typeMapping.put("boolean", "Boolean");
typeMapping.put("Date", "DateTime"); typeMapping.put("Date", "Date");
typeMapping.put("DateTime", "DateTime"); typeMapping.put("DateTime", "DateTime");
typeMapping.put("file", "String"); typeMapping.put("file", "String");
typeMapping.put("map", "Map"); typeMapping.put("map", "Map");
@ -575,7 +575,12 @@ public class ElixirClientCodegen extends DefaultCodegen {
} else if (ModelUtils.isBooleanSchema(p)) { } else if (ModelUtils.isBooleanSchema(p)) {
return "boolean()"; return "boolean()";
} else if (!StringUtils.isEmpty(p.get$ref())) { } else if (!StringUtils.isEmpty(p.get$ref())) {
switch (super.getTypeDeclaration(p)) {
case "String":
return "String.t";
default:
return this.moduleName + ".Model." + super.getTypeDeclaration(p) + ".t"; return this.moduleName + ".Model." + super.getTypeDeclaration(p) + ".t";
}
} else if (ModelUtils.isFileSchema(p)) { } else if (ModelUtils.isFileSchema(p)) {
return "String.t"; return "String.t";
} else if (ModelUtils.isStringSchema(p)) { } else if (ModelUtils.isStringSchema(p)) {
@ -662,28 +667,23 @@ public class ElixirClientCodegen extends DefaultCodegen {
} }
public String decodedStruct() { public String decodedStruct() {
// Let Poison decode the entire response into a generic blob // Let Jason decode the entire response into a generic blob
if (isMap) { if (isMap) {
return "%{}"; return "%{}";
} }
// Primitive return type, don't even try to decode // Primitive return type, don't even try to decode
if (baseType == null || (containerType == null && primitiveType)) { if (baseType == null || (containerType == null && primitiveType)) {
return "false"; return "false";
} else if (isArray && languageSpecificPrimitives().contains(baseType)) { } else if (isArray && languageSpecificPrimitives().contains(baseType)) {
return "[]"; return "[]";
} }
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (isArray) {
sb.append("[");
}
sb.append("%");
sb.append(moduleName); sb.append(moduleName);
sb.append(".Model."); sb.append(".Model.");
sb.append(baseType); sb.append(baseType);
sb.append("{}");
if (isArray) {
sb.append("]");
}
return sb.toString(); return sb.toString();
} }
@ -768,6 +768,24 @@ public class ElixirClientCodegen extends DefaultCodegen {
this.replacedPathName = replacedPathName; this.replacedPathName = replacedPathName;
} }
private void translateBaseType(StringBuilder returnEntry, String baseType) {
switch (baseType) {
case "AnyType":
returnEntry.append("any()");
break;
case "Boolean":
returnEntry.append("boolean()");
break;
case "Float":
returnEntry.append("float()");
break;
default:
returnEntry.append(baseType);
returnEntry.append(".t");
break;
}
}
public String typespec() { public String typespec() {
StringBuilder sb = new StringBuilder("@spec "); StringBuilder sb = new StringBuilder("@spec ");
sb.append(underscore(operationId)); sb.append(underscore(operationId));
@ -793,12 +811,7 @@ public class ElixirClientCodegen extends DefaultCodegen {
returnEntry.append(".Model."); returnEntry.append(".Model.");
} }
if (exResponse.baseType.equals("AnyType")) { translateBaseType(returnEntry, exResponse.baseType);
returnEntry.append("any()");
}else {
returnEntry.append(exResponse.baseType);
returnEntry.append(".t");
}
} else { } else {
if (exResponse.containerType.equals("array") || if (exResponse.containerType.equals("array") ||
exResponse.containerType.equals("set")) { exResponse.containerType.equals("set")) {
@ -808,12 +821,8 @@ public class ElixirClientCodegen extends DefaultCodegen {
returnEntry.append(".Model."); returnEntry.append(".Model.");
} }
if (exResponse.baseType.equals("AnyType")) { translateBaseType(returnEntry, exResponse.baseType);
returnEntry.append("any())"); returnEntry.append(")");
}else {
returnEntry.append(exResponse.baseType);
returnEntry.append(".t)");
}
} else if (exResponse.containerType.equals("map")) { } else if (exResponse.containerType.equals("map")) {
returnEntry.append("map()"); returnEntry.append("map()");
} }

View File

@ -216,7 +216,7 @@ defmodule {{moduleName}}.Connection do
tesla_options = Application.get_env(:tesla, __MODULE__, []) tesla_options = Application.get_env(:tesla, __MODULE__, [])
middleware = Keyword.get(tesla_options, :middleware, []) middleware = Keyword.get(tesla_options, :middleware, [])
json_engine = Keyword.get(tesla_options, :json, Poison) json_engine = Keyword.get(tesla_options, :json, Jason)
user_agent = user_agent =
Keyword.get( Keyword.get(

View File

@ -4,37 +4,81 @@ defmodule {{moduleName}}.Deserializer do
Helper functions for deserializing responses into models Helper functions for deserializing responses into models
""" """
@jason_decode_opts [keys: :strings]
def jason_decode(json) do
Jason.decode(json, @jason_decode_opts)
end
def jason_decode(json, module) do
json
|> jason_decode()
|> case do
{:ok, decoded} -> {:ok, to_struct(decoded, module)}
{:error, _} = error -> error
end
end
@doc """ @doc """
Update the provided model with a deserialization of a nested value Update the provided model with a deserialization of a nested value
""" """
@spec deserialize(struct(), :atom, :atom, struct(), keyword()) :: struct() @spec deserialize(struct(), atom(), :date | :datetime | :list | :map | :struct, module()) ::
def deserialize(model, field, :list, mod, options) do struct()
def deserialize(model, field, :list, module) do
model model
|> Map.update!(field, &(Poison.Decode.decode(&1, Keyword.merge(options, [as: [struct(mod)]])))) |> Map.update!(field, fn
nil ->
nil
list ->
Enum.map(list, &to_struct(&1, module))
end)
end end
def deserialize(model, field, :struct, mod, options) do def deserialize(model, field, :struct, module) do
model model
|> Map.update!(field, &(Poison.Decode.decode(&1, Keyword.merge(options, [as: struct(mod)])))) |> Map.update!(field, fn
nil ->
nil
value ->
to_struct(value, module)
end)
end end
def deserialize(model, field, :map, mod, options) do def deserialize(model, field, :map, module) do
maybe_transform_map = fn maybe_transform_map = fn
nil -> nil ->
nil nil
existing_value -> existing_value ->
Map.new(existing_value, fn Map.new(existing_value, fn
{key, val} -> {key, value} ->
{key, Poison.Decode.decode(val, Keyword.merge(options, as: struct(mod)))} {key, to_struct(value, module)}
end) end)
end end
Map.update!(model, field, maybe_transform_map) Map.update!(model, field, maybe_transform_map)
end end
def deserialize(model, field, :date, _, _options) do def deserialize(model, field, :date, _) do
value = Map.get(model, field) value = Map.get(model, field)
case is_binary(value) do
true ->
case Date.from_iso8601(value) do
{:ok, date} -> Map.put(model, field, date)
_ -> model
end
false ->
model
end
end
def deserialize(model, field, :datetime, _) do
value = Map.get(model, field)
case is_binary(value) do case is_binary(value) do
true -> true ->
case DateTime.from_iso8601(value) do case DateTime.from_iso8601(value) do
@ -46,4 +90,23 @@ defmodule {{moduleName}}.Deserializer do
model model
end end
end end
defp to_struct(map_or_list, module)
defp to_struct(nil, _), do: nil
defp to_struct(list, module) when is_list(list) and is_atom(module) do
Enum.map(list, &to_struct(&1, module))
end
defp to_struct(map, module) when is_map(map) and is_atom(module) do
model = struct(module)
model
|> Map.keys()
|> List.delete(:__struct__)
|> Enum.reduce(model, fn field, acc ->
Map.replace(acc, field, Map.get(map, Atom.to_string(field)))
end)
|> module.decode()
end
end end

View File

@ -9,7 +9,9 @@ defmodule {{moduleName}}.Mixfile do
build_embedded: Mix.env() == :prod, build_embedded: Mix.env() == :prod,
start_permanent: Mix.env() == :prod, start_permanent: Mix.env() == :prod,
package: package(), package: package(),
description: "{{appDescription}}", description: """
{{appDescription}}
""",
deps: deps() deps: deps()
] ]
end end

View File

@ -4,7 +4,7 @@
{{&description}} {{&description}}
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
{{#vars}}{{#atom}}{{&baseName}}{{/atom}}{{^-last}}, {{#vars}}{{#atom}}{{&baseName}}{{/atom}}{{^-last}},
{{/-last}}{{/vars}} {{/-last}}{{/vars}}
@ -14,22 +14,21 @@
{{#vars}}{{#atom}}{{&baseName}}{{/atom}} => {{{datatype}}}{{#isNullable}} | nil{{/isNullable}}{{^isNullable}}{{^required}} | nil{{/required}}{{/isNullable}}{{^-last}}, {{#vars}}{{#atom}}{{&baseName}}{{/atom}} => {{{datatype}}}{{#isNullable}} | nil{{/isNullable}}{{^isNullable}}{{^required}} | nil{{/required}}{{/isNullable}}{{^-last}},
{{/-last}}{{/vars}} {{/-last}}{{/vars}}
} }
end
defimpl Poison.Decoder, for: {{&moduleName}}.Model.{{&classname}} do
{{#hasComplexVars}} {{#hasComplexVars}}
import {{&moduleName}}.Deserializer alias {{&moduleName}}.Deserializer
def decode(value, options) do
def decode(value) do
value value
{{#vars}} {{#vars}}
{{^isPrimitiveType}} {{^isPrimitiveType}}
{{#baseType}}|> deserialize({{#atom}}{{&baseName}}{{/atom}}, {{#isArray}}:list, {{&moduleName}}.Model.{{{items.baseType}}}{{/isArray}}{{#isMap}}:map, {{&moduleName}}.Model.{{{items.baseType}}}{{/isMap}}{{#isDate}}:date, nil{{/isDate}}{{#isDateTime}}:date, nil{{/isDateTime}}{{^isDate}}{{^isDateTime}}{{^isMap}}{{^isArray}}:struct, {{moduleName}}.Model.{{baseType}}{{/isArray}}{{/isMap}}{{/isDateTime}}{{/isDate}}, options) {{#baseType}} |> Deserializer.deserialize({{#atom}}{{&baseName}}{{/atom}}, {{#isArray}}:list, {{&moduleName}}.Model.{{{items.baseType}}}{{/isArray}}{{#isMap}}:map, {{&moduleName}}.Model.{{{items.baseType}}}{{/isMap}}{{#isDate}}:date, nil{{/isDate}}{{#isDateTime}}:datetime, nil{{/isDateTime}}{{^isDate}}{{^isDateTime}}{{^isMap}}{{^isArray}}:struct, {{moduleName}}.Model.{{baseType}}{{/isArray}}{{/isMap}}{{/isDateTime}}{{/isDate}})
{{/baseType}} {{/baseType}}
{{/isPrimitiveType}} {{/isPrimitiveType}}
{{/vars}} {{/vars}}
{{/hasComplexVars}} {{/hasComplexVars}}
{{^hasComplexVars}} {{^hasComplexVars}}
def decode(value, _options) do def decode(value) do
value value
{{/hasComplexVars}} {{/hasComplexVars}}
end end

View File

@ -94,7 +94,7 @@ defmodule {{moduleName}}.RequestBuilder do
Tesla.Multipart.add_field( Tesla.Multipart.add_field(
multipart, multipart,
key, key,
Poison.encode!(value), Jason.encode!(value),
headers: [{:"Content-Type", "application/json"}] headers: [{:"Content-Type", "application/json"}]
) )
end) end)
@ -146,8 +146,8 @@ defmodule {{moduleName}}.RequestBuilder do
Map.put_new(request, :body, "") Map.put_new(request, :body, "")
end end
@type status_code :: 100..599 @type status_code :: :default | 100..599
@type response_mapping :: [{status_code, struct() | false}] @type response_mapping :: [{status_code, false | %{} | module()}]
@doc """ @doc """
Evaluate the response from a Tesla request. Evaluate the response from a Tesla request.
@ -185,5 +185,11 @@ defmodule {{moduleName}}.RequestBuilder do
defp decode(%Tesla.Env{} = env, false), do: {:ok, env} defp decode(%Tesla.Env{} = env, false), do: {:ok, env}
defp decode(%Tesla.Env{body: body}, struct), do: Poison.decode(body, as: struct) defp decode(%Tesla.Env{body: body}, %{}) do
{{moduleName}}.Deserializer.jason_decode(body)
end
defp decode(%Tesla.Env{body: body}, module) do
{{moduleName}}.Deserializer.jason_decode(body, module)
end
end end

View File

@ -36,7 +36,7 @@ defmodule OpenapiPetstore.Api.AnotherFake do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.Client{}} {200, OpenapiPetstore.Model.Client}
]) ])
end end
end end

View File

@ -32,7 +32,7 @@ defmodule OpenapiPetstore.Api.Default do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{:default, %OpenapiPetstore.Model.FooGetDefaultResponse{}} {:default, OpenapiPetstore.Model.FooGetDefaultResponse}
]) ])
end end
end end

View File

@ -33,7 +33,7 @@ defmodule OpenapiPetstore.Api.Fake do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.FakeBigDecimalMap200Response{}} {200, OpenapiPetstore.Model.FakeBigDecimalMap200Response}
]) ])
end end
@ -61,7 +61,7 @@ defmodule OpenapiPetstore.Api.Fake do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.HealthCheckResult{}} {200, OpenapiPetstore.Model.HealthCheckResult}
]) ])
end end
@ -117,7 +117,7 @@ defmodule OpenapiPetstore.Api.Fake do
- `{:ok, boolean()}` on success - `{:ok, boolean()}` on success
- `{:error, Tesla.Env.t}` on failure - `{:error, Tesla.Env.t}` on failure
""" """
@spec fake_outer_boolean_serialize(Tesla.Env.client, keyword()) :: {:ok, Boolean.t} | {:error, Tesla.Env.t} @spec fake_outer_boolean_serialize(Tesla.Env.client, keyword()) :: {:ok, boolean()} | {:error, Tesla.Env.t}
def fake_outer_boolean_serialize(connection, opts \\ []) do def fake_outer_boolean_serialize(connection, opts \\ []) do
optional_params = %{ optional_params = %{
:body => :body :body => :body
@ -169,7 +169,7 @@ defmodule OpenapiPetstore.Api.Fake do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.OuterComposite{}} {200, OpenapiPetstore.Model.OuterComposite}
]) ])
end end
@ -187,7 +187,7 @@ defmodule OpenapiPetstore.Api.Fake do
- `{:ok, float()}` on success - `{:ok, float()}` on success
- `{:error, Tesla.Env.t}` on failure - `{:error, Tesla.Env.t}` on failure
""" """
@spec fake_outer_number_serialize(Tesla.Env.client, keyword()) :: {:ok, Float.t} | {:error, Tesla.Env.t} @spec fake_outer_number_serialize(Tesla.Env.client, keyword()) :: {:ok, float()} | {:error, Tesla.Env.t}
def fake_outer_number_serialize(connection, opts \\ []) do def fake_outer_number_serialize(connection, opts \\ []) do
optional_params = %{ optional_params = %{
:body => :body :body => :body
@ -269,7 +269,7 @@ defmodule OpenapiPetstore.Api.Fake do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.OuterObjectWithEnumProperty{}} {200, OpenapiPetstore.Model.OuterObjectWithEnumProperty}
]) ])
end end
@ -391,7 +391,7 @@ defmodule OpenapiPetstore.Api.Fake do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.Client{}} {200, OpenapiPetstore.Model.Client}
]) ])
end end

View File

@ -36,7 +36,7 @@ defmodule OpenapiPetstore.Api.FakeClassnameTags123 do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.Client{}} {200, OpenapiPetstore.Model.Client}
]) ])
end end
end end

View File

@ -105,7 +105,7 @@ defmodule OpenapiPetstore.Api.Pet do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, [%OpenapiPetstore.Model.Pet{}]}, {200, OpenapiPetstore.Model.Pet},
{400, false} {400, false}
]) ])
end end
@ -137,7 +137,7 @@ defmodule OpenapiPetstore.Api.Pet do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, [%OpenapiPetstore.Model.Pet{}]}, {200, OpenapiPetstore.Model.Pet},
{400, false} {400, false}
]) ])
end end
@ -168,7 +168,7 @@ defmodule OpenapiPetstore.Api.Pet do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.Pet{}}, {200, OpenapiPetstore.Model.Pet},
{400, false}, {400, false},
{404, false} {404, false}
]) ])
@ -283,7 +283,7 @@ defmodule OpenapiPetstore.Api.Pet do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.ApiResponse{}} {200, OpenapiPetstore.Model.ApiResponse}
]) ])
end end
@ -321,7 +321,7 @@ defmodule OpenapiPetstore.Api.Pet do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.ApiResponse{}} {200, OpenapiPetstore.Model.ApiResponse}
]) ])
end end
end end

View File

@ -95,7 +95,7 @@ defmodule OpenapiPetstore.Api.Store do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.Order{}}, {200, OpenapiPetstore.Model.Order},
{400, false}, {400, false},
{404, false} {404, false}
]) ])
@ -128,7 +128,7 @@ defmodule OpenapiPetstore.Api.Store do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.Order{}}, {200, OpenapiPetstore.Model.Order},
{400, false} {400, false}
]) ])
end end

View File

@ -159,7 +159,7 @@ defmodule OpenapiPetstore.Api.User do
connection connection
|> Connection.request(request) |> Connection.request(request)
|> evaluate_response([ |> evaluate_response([
{200, %OpenapiPetstore.Model.User{}}, {200, OpenapiPetstore.Model.User},
{400, false}, {400, false},
{404, false} {404, false}
]) ])

View File

@ -153,7 +153,7 @@ defmodule OpenapiPetstore.Connection do
tesla_options = Application.get_env(:tesla, __MODULE__, []) tesla_options = Application.get_env(:tesla, __MODULE__, [])
middleware = Keyword.get(tesla_options, :middleware, []) middleware = Keyword.get(tesla_options, :middleware, [])
json_engine = Keyword.get(tesla_options, :json, Poison) json_engine = Keyword.get(tesla_options, :json, Jason)
user_agent = user_agent =
Keyword.get( Keyword.get(

View File

@ -6,37 +6,81 @@ defmodule OpenapiPetstore.Deserializer do
Helper functions for deserializing responses into models Helper functions for deserializing responses into models
""" """
@jason_decode_opts [keys: :strings]
def jason_decode(json) do
Jason.decode(json, @jason_decode_opts)
end
def jason_decode(json, module) do
json
|> jason_decode()
|> case do
{:ok, decoded} -> {:ok, to_struct(decoded, module)}
{:error, _} = error -> error
end
end
@doc """ @doc """
Update the provided model with a deserialization of a nested value Update the provided model with a deserialization of a nested value
""" """
@spec deserialize(struct(), :atom, :atom, struct(), keyword()) :: struct() @spec deserialize(struct(), atom(), :date | :datetime | :list | :map | :struct, module()) ::
def deserialize(model, field, :list, mod, options) do struct()
def deserialize(model, field, :list, module) do
model model
|> Map.update!(field, &(Poison.Decode.decode(&1, Keyword.merge(options, [as: [struct(mod)]])))) |> Map.update!(field, fn
nil ->
nil
list ->
Enum.map(list, &to_struct(&1, module))
end)
end end
def deserialize(model, field, :struct, mod, options) do def deserialize(model, field, :struct, module) do
model model
|> Map.update!(field, &(Poison.Decode.decode(&1, Keyword.merge(options, [as: struct(mod)])))) |> Map.update!(field, fn
nil ->
nil
value ->
to_struct(value, module)
end)
end end
def deserialize(model, field, :map, mod, options) do def deserialize(model, field, :map, module) do
maybe_transform_map = fn maybe_transform_map = fn
nil -> nil ->
nil nil
existing_value -> existing_value ->
Map.new(existing_value, fn Map.new(existing_value, fn
{key, val} -> {key, value} ->
{key, Poison.Decode.decode(val, Keyword.merge(options, as: struct(mod)))} {key, to_struct(value, module)}
end) end)
end end
Map.update!(model, field, maybe_transform_map) Map.update!(model, field, maybe_transform_map)
end end
def deserialize(model, field, :date, _, _options) do def deserialize(model, field, :date, _) do
value = Map.get(model, field) value = Map.get(model, field)
case is_binary(value) do
true ->
case Date.from_iso8601(value) do
{:ok, date} -> Map.put(model, field, date)
_ -> model
end
false ->
model
end
end
def deserialize(model, field, :datetime, _) do
value = Map.get(model, field)
case is_binary(value) do case is_binary(value) do
true -> true ->
case DateTime.from_iso8601(value) do case DateTime.from_iso8601(value) do
@ -48,4 +92,23 @@ defmodule OpenapiPetstore.Deserializer do
model model
end end
end end
defp to_struct(map_or_list, module)
defp to_struct(nil, _), do: nil
defp to_struct(list, module) when is_list(list) and is_atom(module) do
Enum.map(list, &to_struct(&1, module))
end
defp to_struct(map, module) when is_map(map) and is_atom(module) do
model = struct(module)
model
|> Map.keys()
|> List.delete(:__struct__)
|> Enum.reduce(model, fn field, acc ->
Map.replace(acc, field, Map.get(map, Atom.to_string(field)))
end)
|> module.decode()
end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.FooGetDefaultResponse do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:string :string
] ]
@ -14,13 +14,12 @@ defmodule OpenapiPetstore.Model.FooGetDefaultResponse do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:string => OpenapiPetstore.Model.Foo.t | nil :string => OpenapiPetstore.Model.Foo.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.FooGetDefaultResponse do alias OpenapiPetstore.Deserializer
import OpenapiPetstore.Deserializer
def decode(value, options) do def decode(value) do
value value
|> deserialize(:string, :struct, OpenapiPetstore.Model.Foo, options) |> Deserializer.deserialize(:string, :struct, OpenapiPetstore.Model.Foo)
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.SpecialModelName do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:"$special[property.name]" :"$special[property.name]"
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.SpecialModelName do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:"$special[property.name]" => integer() | nil :"$special[property.name]" => integer() | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.SpecialModelName do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.AdditionalPropertiesClass do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:map_property, :map_property,
:map_of_map_property :map_of_map_property
@ -16,10 +16,8 @@ defmodule OpenapiPetstore.Model.AdditionalPropertiesClass do
:map_property => %{optional(String.t) => String.t} | nil, :map_property => %{optional(String.t) => String.t} | nil,
:map_of_map_property => %{optional(String.t) => %{optional(String.t) => String.t}} | nil :map_of_map_property => %{optional(String.t) => %{optional(String.t) => String.t}} | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.AdditionalPropertiesClass do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.AllOfWithSingleRef do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:username, :username,
:SingleRefType :SingleRefType
@ -16,13 +16,12 @@ defmodule OpenapiPetstore.Model.AllOfWithSingleRef do
:username => String.t | nil, :username => String.t | nil,
:SingleRefType => OpenapiPetstore.Model.SingleRefType.t | nil :SingleRefType => OpenapiPetstore.Model.SingleRefType.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.AllOfWithSingleRef do alias OpenapiPetstore.Deserializer
import OpenapiPetstore.Deserializer
def decode(value, options) do def decode(value) do
value value
|> deserialize(:SingleRefType, :struct, OpenapiPetstore.Model.SingleRefType, options) |> Deserializer.deserialize(:SingleRefType, :struct, OpenapiPetstore.Model.SingleRefType)
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Animal do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:className, :className,
:color :color
@ -16,10 +16,8 @@ defmodule OpenapiPetstore.Model.Animal do
:className => String.t, :className => String.t,
:color => String.t | nil :color => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Animal do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.ApiResponse do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:code, :code,
:type, :type,
@ -18,10 +18,8 @@ defmodule OpenapiPetstore.Model.ApiResponse do
:type => String.t | nil, :type => String.t | nil,
:message => String.t | nil :message => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.ApiResponse do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.ArrayOfArrayOfNumberOnly do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:ArrayArrayNumber :ArrayArrayNumber
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.ArrayOfArrayOfNumberOnly do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:ArrayArrayNumber => [[float()]] | nil :ArrayArrayNumber => [[float()]] | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.ArrayOfArrayOfNumberOnly do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.ArrayOfNumberOnly do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:ArrayNumber :ArrayNumber
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.ArrayOfNumberOnly do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:ArrayNumber => [float()] | nil :ArrayNumber => [float()] | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.ArrayOfNumberOnly do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.ArrayTest do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:array_of_string, :array_of_string,
:array_array_of_integer, :array_array_of_integer,
@ -18,10 +18,8 @@ defmodule OpenapiPetstore.Model.ArrayTest do
:array_array_of_integer => [[integer()]] | nil, :array_array_of_integer => [[integer()]] | nil,
:array_array_of_model => [[OpenapiPetstore.Model.ReadOnlyFirst.t]] | nil :array_array_of_model => [[OpenapiPetstore.Model.ReadOnlyFirst.t]] | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.ArrayTest do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Capitalization do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:smallCamel, :smallCamel,
:CapitalCamel, :CapitalCamel,
@ -24,10 +24,8 @@ defmodule OpenapiPetstore.Model.Capitalization do
:SCA_ETH_Flow_Points => String.t | nil, :SCA_ETH_Flow_Points => String.t | nil,
:ATT_NAME => String.t | nil :ATT_NAME => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Capitalization do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Cat do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:className, :className,
:color, :color,
@ -18,10 +18,8 @@ defmodule OpenapiPetstore.Model.Cat do
:color => String.t | nil, :color => String.t | nil,
:declawed => boolean() | nil :declawed => boolean() | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Cat do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Category do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:id, :id,
:name :name
@ -16,10 +16,8 @@ defmodule OpenapiPetstore.Model.Category do
:id => integer() | nil, :id => integer() | nil,
:name => String.t :name => String.t
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Category do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.ClassModel do
Model for testing model with \"_class\" property Model for testing model with \"_class\" property
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:_class :_class
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.ClassModel do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:_class => String.t | nil :_class => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.ClassModel do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Client do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:client :client
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.Client do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:client => String.t | nil :client => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Client do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.DeprecatedObject do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:name :name
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.DeprecatedObject do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:name => String.t | nil :name => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.DeprecatedObject do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Dog do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:className, :className,
:color, :color,
@ -18,10 +18,8 @@ defmodule OpenapiPetstore.Model.Dog do
:color => String.t | nil, :color => String.t | nil,
:breed => String.t | nil :breed => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Dog do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.EnumArrays do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:just_symbol, :just_symbol,
:array_enum :array_enum
@ -16,10 +16,8 @@ defmodule OpenapiPetstore.Model.EnumArrays do
:just_symbol => String.t | nil, :just_symbol => String.t | nil,
:array_enum => [String.t] | nil :array_enum => [String.t] | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.EnumArrays do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.EnumClass do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.EnumClass do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.EnumClass do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.EnumTest do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:enum_string, :enum_string,
:enum_string_required, :enum_string_required,
@ -28,16 +28,15 @@ defmodule OpenapiPetstore.Model.EnumTest do
:outerEnumDefaultValue => OpenapiPetstore.Model.OuterEnumDefaultValue.t | nil, :outerEnumDefaultValue => OpenapiPetstore.Model.OuterEnumDefaultValue.t | nil,
:outerEnumIntegerDefaultValue => OpenapiPetstore.Model.OuterEnumIntegerDefaultValue.t | nil :outerEnumIntegerDefaultValue => OpenapiPetstore.Model.OuterEnumIntegerDefaultValue.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.EnumTest do alias OpenapiPetstore.Deserializer
import OpenapiPetstore.Deserializer
def decode(value, options) do def decode(value) do
value value
|> deserialize(:outerEnum, :struct, OpenapiPetstore.Model.OuterEnum, options) |> Deserializer.deserialize(:outerEnum, :struct, OpenapiPetstore.Model.OuterEnum)
|> deserialize(:outerEnumInteger, :struct, OpenapiPetstore.Model.OuterEnumInteger, options) |> Deserializer.deserialize(:outerEnumInteger, :struct, OpenapiPetstore.Model.OuterEnumInteger)
|> deserialize(:outerEnumDefaultValue, :struct, OpenapiPetstore.Model.OuterEnumDefaultValue, options) |> Deserializer.deserialize(:outerEnumDefaultValue, :struct, OpenapiPetstore.Model.OuterEnumDefaultValue)
|> deserialize(:outerEnumIntegerDefaultValue, :struct, OpenapiPetstore.Model.OuterEnumIntegerDefaultValue, options) |> Deserializer.deserialize(:outerEnumIntegerDefaultValue, :struct, OpenapiPetstore.Model.OuterEnumIntegerDefaultValue)
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.FakeBigDecimalMap200Response do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:someId, :someId,
:someMap :someMap
@ -16,10 +16,8 @@ defmodule OpenapiPetstore.Model.FakeBigDecimalMap200Response do
:someId => float() | nil, :someId => float() | nil,
:someMap => %{optional(String.t) => float()} | nil :someMap => %{optional(String.t) => float()} | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.FakeBigDecimalMap200Response do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.File do
Must be named `File` for test. Must be named `File` for test.
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:sourceURI :sourceURI
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.File do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:sourceURI => String.t | nil :sourceURI => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.File do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.FileSchemaTestClass do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:file, :file,
:files :files
@ -16,14 +16,13 @@ defmodule OpenapiPetstore.Model.FileSchemaTestClass do
:file => OpenapiPetstore.Model.File.t | nil, :file => OpenapiPetstore.Model.File.t | nil,
:files => [OpenapiPetstore.Model.File.t] | nil :files => [OpenapiPetstore.Model.File.t] | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.FileSchemaTestClass do alias OpenapiPetstore.Deserializer
import OpenapiPetstore.Deserializer
def decode(value, options) do def decode(value) do
value value
|> deserialize(:file, :struct, OpenapiPetstore.Model.File, options) |> Deserializer.deserialize(:file, :struct, OpenapiPetstore.Model.File)
|> deserialize(:files, :list, OpenapiPetstore.Model.File, options) |> Deserializer.deserialize(:files, :list, OpenapiPetstore.Model.File)
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Foo do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:bar :bar
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.Foo do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:bar => String.t | nil :bar => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Foo do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.FormatTest do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:integer, :integer,
:int32, :int32,
@ -44,13 +44,13 @@ defmodule OpenapiPetstore.Model.FormatTest do
:pattern_with_digits => String.t | nil, :pattern_with_digits => String.t | nil,
:pattern_with_digits_and_delimiter => String.t | nil :pattern_with_digits_and_delimiter => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.FormatTest do alias OpenapiPetstore.Deserializer
import OpenapiPetstore.Deserializer
def decode(value, options) do def decode(value) do
value value
|> deserialize(:date, :date, nil, options) |> Deserializer.deserialize(:date, :date, nil)
|> Deserializer.deserialize(:dateTime, :datetime, nil)
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.HasOnlyReadOnly do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:bar, :bar,
:foo :foo
@ -16,10 +16,8 @@ defmodule OpenapiPetstore.Model.HasOnlyReadOnly do
:bar => String.t | nil, :bar => String.t | nil,
:foo => String.t | nil :foo => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.HasOnlyReadOnly do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.HealthCheckResult do
Just a string to inform instance is up and running. Make it nullable in hope to get it as pointer in generated model. Just a string to inform instance is up and running. Make it nullable in hope to get it as pointer in generated model.
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:NullableMessage :NullableMessage
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.HealthCheckResult do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:NullableMessage => String.t | nil :NullableMessage => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.HealthCheckResult do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.List do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:"123-list" :"123-list"
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.List do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:"123-list" => String.t | nil :"123-list" => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.List do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.MapTest do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:map_map_of_string, :map_map_of_string,
:map_of_enum_string, :map_of_enum_string,
@ -20,10 +20,8 @@ defmodule OpenapiPetstore.Model.MapTest do
:direct_map => %{optional(String.t) => boolean()} | nil, :direct_map => %{optional(String.t) => boolean()} | nil,
:indirect_map => %{optional(String.t) => boolean()} | nil :indirect_map => %{optional(String.t) => boolean()} | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.MapTest do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.MixedPropertiesAndAdditionalPropertiesClass do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:uuid, :uuid,
:dateTime, :dateTime,
@ -18,13 +18,13 @@ defmodule OpenapiPetstore.Model.MixedPropertiesAndAdditionalPropertiesClass do
:dateTime => DateTime.t | nil, :dateTime => DateTime.t | nil,
:map => %{optional(String.t) => OpenapiPetstore.Model.Animal.t} | nil :map => %{optional(String.t) => OpenapiPetstore.Model.Animal.t} | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.MixedPropertiesAndAdditionalPropertiesClass do alias OpenapiPetstore.Deserializer
import OpenapiPetstore.Deserializer
def decode(value, options) do def decode(value) do
value value
|> deserialize(:map, :map, OpenapiPetstore.Model.Animal, options) |> Deserializer.deserialize(:dateTime, :datetime, nil)
|> Deserializer.deserialize(:map, :map, OpenapiPetstore.Model.Animal)
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Model200Response do
Model for testing model name starting with number Model for testing model name starting with number
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:name, :name,
:class :class
@ -16,10 +16,8 @@ defmodule OpenapiPetstore.Model.Model200Response do
:name => integer() | nil, :name => integer() | nil,
:class => String.t | nil :class => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Model200Response do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Name do
Model for testing model name same as property name Model for testing model name same as property name
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:name, :name,
:snake_case, :snake_case,
@ -20,10 +20,8 @@ defmodule OpenapiPetstore.Model.Name do
:property => String.t | nil, :property => String.t | nil,
:"123Number" => integer() | nil :"123Number" => integer() | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Name do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.NullableClass do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:integer_prop, :integer_prop,
:number_prop, :number_prop,
@ -36,13 +36,13 @@ defmodule OpenapiPetstore.Model.NullableClass do
:object_and_items_nullable_prop => %{optional(String.t) => map()} | nil, :object_and_items_nullable_prop => %{optional(String.t) => map()} | nil,
:object_items_nullable => %{optional(String.t) => map()} | nil :object_items_nullable => %{optional(String.t) => map()} | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.NullableClass do alias OpenapiPetstore.Deserializer
import OpenapiPetstore.Deserializer
def decode(value, options) do def decode(value) do
value value
|> deserialize(:date_prop, :date, nil, options) |> Deserializer.deserialize(:date_prop, :date, nil)
|> Deserializer.deserialize(:datetime_prop, :datetime, nil)
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.NumberOnly do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:JustNumber :JustNumber
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.NumberOnly do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:JustNumber => float() | nil :JustNumber => float() | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.NumberOnly do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.ObjectWithDeprecatedFields do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:uuid, :uuid,
:id, :id,
@ -18,15 +18,14 @@ defmodule OpenapiPetstore.Model.ObjectWithDeprecatedFields do
:uuid => String.t | nil, :uuid => String.t | nil,
:id => float() | nil, :id => float() | nil,
:deprecatedRef => OpenapiPetstore.Model.DeprecatedObject.t | nil, :deprecatedRef => OpenapiPetstore.Model.DeprecatedObject.t | nil,
:bars => [OpenapiPetstore.Model.String.t] | nil :bars => [String.t] | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.ObjectWithDeprecatedFields do alias OpenapiPetstore.Deserializer
import OpenapiPetstore.Deserializer
def decode(value, options) do def decode(value) do
value value
|> deserialize(:deprecatedRef, :struct, OpenapiPetstore.Model.DeprecatedObject, options) |> Deserializer.deserialize(:deprecatedRef, :struct, OpenapiPetstore.Model.DeprecatedObject)
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Order do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:id, :id,
:petId, :petId,
@ -24,11 +24,12 @@ defmodule OpenapiPetstore.Model.Order do
:status => String.t | nil, :status => String.t | nil,
:complete => boolean() | nil :complete => boolean() | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Order do alias OpenapiPetstore.Deserializer
def decode(value, _options) do
def decode(value) do
value value
|> Deserializer.deserialize(:shipDate, :datetime, nil)
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.OuterComposite do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:my_number, :my_number,
:my_string, :my_string,
@ -18,10 +18,8 @@ defmodule OpenapiPetstore.Model.OuterComposite do
:my_string => String.t | nil, :my_string => String.t | nil,
:my_boolean => boolean() | nil :my_boolean => boolean() | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.OuterComposite do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.OuterEnum do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.OuterEnum do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.OuterEnum do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.OuterEnumDefaultValue do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.OuterEnumDefaultValue do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.OuterEnumDefaultValue do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.OuterEnumInteger do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.OuterEnumInteger do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.OuterEnumInteger do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.OuterEnumIntegerDefaultValue do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.OuterEnumIntegerDefaultValue do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.OuterEnumIntegerDefaultValue do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.OuterObjectWithEnumProperty do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:value :value
] ]
@ -14,13 +14,12 @@ defmodule OpenapiPetstore.Model.OuterObjectWithEnumProperty do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:value => OpenapiPetstore.Model.OuterEnumInteger.t :value => OpenapiPetstore.Model.OuterEnumInteger.t
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.OuterObjectWithEnumProperty do alias OpenapiPetstore.Deserializer
import OpenapiPetstore.Deserializer
def decode(value, options) do def decode(value) do
value value
|> deserialize(:value, :struct, OpenapiPetstore.Model.OuterEnumInteger, options) |> Deserializer.deserialize(:value, :struct, OpenapiPetstore.Model.OuterEnumInteger)
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Pet do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:id, :id,
:category, :category,
@ -24,14 +24,13 @@ defmodule OpenapiPetstore.Model.Pet do
:tags => [OpenapiPetstore.Model.Tag.t] | nil, :tags => [OpenapiPetstore.Model.Tag.t] | nil,
:status => String.t | nil :status => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Pet do alias OpenapiPetstore.Deserializer
import OpenapiPetstore.Deserializer
def decode(value, options) do def decode(value) do
value value
|> deserialize(:category, :struct, OpenapiPetstore.Model.Category, options) |> Deserializer.deserialize(:category, :struct, OpenapiPetstore.Model.Category)
|> deserialize(:tags, :list, OpenapiPetstore.Model.Tag, options) |> Deserializer.deserialize(:tags, :list, OpenapiPetstore.Model.Tag)
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.ReadOnlyFirst do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:bar, :bar,
:baz :baz
@ -16,10 +16,8 @@ defmodule OpenapiPetstore.Model.ReadOnlyFirst do
:bar => String.t | nil, :bar => String.t | nil,
:baz => String.t | nil :baz => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.ReadOnlyFirst do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Return do
Model for testing reserved words Model for testing reserved words
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:return :return
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.Return do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
:return => integer() | nil :return => integer() | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Return do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.SingleRefType do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
] ]
@ -14,10 +14,8 @@ defmodule OpenapiPetstore.Model.SingleRefType do
@type t :: %__MODULE__{ @type t :: %__MODULE__{
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.SingleRefType do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.Tag do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:id, :id,
:name :name
@ -16,10 +16,8 @@ defmodule OpenapiPetstore.Model.Tag do
:id => integer() | nil, :id => integer() | nil,
:name => String.t | nil :name => String.t | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.Tag do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -6,7 +6,7 @@ defmodule OpenapiPetstore.Model.User do
""" """
@derive [Poison.Encoder] @derive Jason.Encoder
defstruct [ defstruct [
:id, :id,
:username, :username,
@ -28,10 +28,8 @@ defmodule OpenapiPetstore.Model.User do
:phone => String.t | nil, :phone => String.t | nil,
:userStatus => integer() | nil :userStatus => integer() | nil
} }
end
defimpl Poison.Decoder, for: OpenapiPetstore.Model.User do def decode(value) do
def decode(value, _options) do
value value
end end
end end

View File

@ -96,7 +96,7 @@ defmodule OpenapiPetstore.RequestBuilder do
Tesla.Multipart.add_field( Tesla.Multipart.add_field(
multipart, multipart,
key, key,
Poison.encode!(value), Jason.encode!(value),
headers: [{:"Content-Type", "application/json"}] headers: [{:"Content-Type", "application/json"}]
) )
end) end)
@ -148,8 +148,8 @@ defmodule OpenapiPetstore.RequestBuilder do
Map.put_new(request, :body, "") Map.put_new(request, :body, "")
end end
@type status_code :: 100..599 @type status_code :: :default | 100..599
@type response_mapping :: [{status_code, struct() | false}] @type response_mapping :: [{status_code, false | %{} | module()}]
@doc """ @doc """
Evaluate the response from a Tesla request. Evaluate the response from a Tesla request.
@ -187,5 +187,11 @@ defmodule OpenapiPetstore.RequestBuilder do
defp decode(%Tesla.Env{} = env, false), do: {:ok, env} defp decode(%Tesla.Env{} = env, false), do: {:ok, env}
defp decode(%Tesla.Env{body: body}, struct), do: Poison.decode(body, as: struct) defp decode(%Tesla.Env{body: body}, %{}) do
OpenapiPetstore.Deserializer.jason_decode(body)
end
defp decode(%Tesla.Env{body: body}, module) do
OpenapiPetstore.Deserializer.jason_decode(body, module)
end
end end

View File

@ -9,7 +9,9 @@ defmodule OpenapiPetstore.Mixfile do
build_embedded: Mix.env() == :prod, build_embedded: Mix.env() == :prod,
start_permanent: Mix.env() == :prod, start_permanent: Mix.env() == :prod,
package: package(), package: package(),
description: "This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \&quot; \\", description: """
This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \&quot; \\
""",
deps: deps() deps: deps()
] ]
end end
@ -33,9 +35,10 @@ defmodule OpenapiPetstore.Mixfile do
# Type "mix help deps" for more examples and options # Type "mix help deps" for more examples and options
defp deps do defp deps do
[ [
{:tesla, "~> 1.4"}, {:tesla, "~> 1.7"},
{:poison, "~> 3.0"}, {:jason, "~> 1.4"},
{:ex_doc, "~> 0.28", only: :dev, runtime: false} {:ex_doc, "~> 0.30", only: :dev, runtime: false},
{:dialyxir, "~> 1.3", only: [:dev, :test], runtime: false}
] ]
end end

View File

@ -0,0 +1,49 @@
defmodule DeserializerTest do
use ExUnit.Case, async: true
alias OpenapiPetstore.Deserializer
alias OpenapiPetstore.Model.{Category, Pet, Tag}
@valid_json """
{
"id": 14,
"category": {
"id": 75,
"name": "sea dragons"
},
"name": "Nagga",
"photoUrls": [
"https://example.com/nagga1.jpg",
"https://example.com/nagga2.jpg"
],
"tags": [
{
"id": 99,
"name": "dragon"
},
{
"id": 23,
"name": "sea"
}
],
"status": "foo"
}
"""
test "jason_decode/2 with valid JSON" do
assert Deserializer.jason_decode(@valid_json, Pet) ==
{:ok,
%Pet{
id: 14,
category: %Category{id: 75, name: "sea dragons"},
name: "Nagga",
photoUrls: ["https://example.com/nagga1.jpg", "https://example.com/nagga2.jpg"],
tags: [%Tag{id: 99, name: "dragon"}, %Tag{id: 23, name: "sea"}],
status: "foo"
}}
end
test "jason_decode/2 with invalid JSON" do
assert Deserializer.jason_decode(~s/{: 1}/, Pet) ==
{:error, %Jason.DecodeError{data: "{: 1}", position: 1, token: nil}}
end
end

View File

@ -0,0 +1,84 @@
defmodule FormatTest do
use ExUnit.Case, async: true
alias OpenapiPetstore.Model.FormatTest
test "decode all properties (not nil)" do
assert %FormatTest{
integer: 1,
int32: 2,
int64: 3,
number: 4.1,
float: 5.2,
double: 6.3,
decimal: "7.4",
string: "Hello world!",
byte: "U3dhZ2dlciByb2Nrcw==",
binary: <<1, 2, 3>>,
date: "2013-10-20",
dateTime: "2013-10-20T19:20:30+01:00",
uuid: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
password: "green?horse",
pattern_with_digits: "1234567890",
pattern_with_digits_and_delimiter: "Image_01"
}
|> FormatTest.decode() ==
%FormatTest{
integer: 1,
int32: 2,
int64: 3,
number: 4.1,
float: 5.2,
double: 6.3,
decimal: "7.4",
string: "Hello world!",
byte: "U3dhZ2dlciByb2Nrcw==",
binary: <<1, 2, 3>>,
date: ~D[2013-10-20],
dateTime: ~U[2013-10-20T18:20:30Z],
uuid: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
password: "green?horse",
pattern_with_digits: "1234567890",
pattern_with_digits_and_delimiter: "Image_01"
}
end
test "decode all properties (some are nil)" do
assert %FormatTest{
integer: nil,
int32: nil,
int64: nil,
number: 4.1,
float: nil,
double: nil,
decimal: nil,
string: nil,
byte: "U3dhZ2dlciByb2Nrcw==",
binary: nil,
date: "2013-10-20",
dateTime: nil,
uuid: nil,
password: "green?horse",
pattern_with_digits: nil,
pattern_with_digits_and_delimiter: nil
}
|> FormatTest.decode() ==
%FormatTest{
integer: nil,
int32: nil,
int64: nil,
number: 4.1,
float: nil,
double: nil,
decimal: nil,
string: nil,
byte: "U3dhZ2dlciByb2Nrcw==",
binary: nil,
date: ~D[2013-10-20],
dateTime: nil,
uuid: nil,
password: "green?horse",
pattern_with_digits: nil,
pattern_with_digits_and_delimiter: nil
}
end
end

View File

@ -0,0 +1,46 @@
defmodule MixedPropertiesAndAdditionalPropertiesClass do
use ExUnit.Case, async: true
alias OpenapiPetstore.Model.MixedPropertiesAndAdditionalPropertiesClass, as: Model
alias OpenapiPetstore.Model.Animal
test "decode all properties (not nil)" do
assert %Model{
uuid: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
dateTime: "2013-10-20T19:20:30+01:00",
map: %{
"doggie" => %{"className" => "DOG", "color" => "yellow", "breed" => "Shiba Inu"},
"meow" => %{"className" => "CAT", "color" => "white", "declawed" => false}
}
}
|> Model.decode() ==
%Model{
uuid: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
dateTime: ~U[2013-10-20T18:20:30Z],
map: %{
# TODO values should be Dog and Cat structs instead of an Animal
"doggie" => %Animal{
className: "DOG",
color: "yellow"
},
"meow" => %Animal{
className: "CAT",
color: "white"
}
}
}
end
test "decode all properties (nil)" do
assert %Model{
uuid: nil,
dateTime: nil,
map: nil
}
|> Model.decode() ==
%Model{
uuid: nil,
dateTime: nil,
map: nil
}
end
end

View File

@ -1,60 +1,66 @@
defmodule PetTest do defmodule PetTest do
use ExUnit.Case use ExUnit.Case, async: true
alias OpenapiPetstore.Connection alias OpenapiPetstore.Connection
alias OpenapiPetstore.Api.Pet, as: PetApi alias OpenapiPetstore.Api.Pet, as: PetApi
alias OpenapiPetstore.Model.Pet alias OpenapiPetstore.Model.Pet
alias OpenapiPetstore.Model.Category alias OpenapiPetstore.Model.Category
alias OpenapiPetstore.Model.Tag alias OpenapiPetstore.Model.Tag
test "add and delete a pet" do setup do
%{connection: Connection.new()}
end
test "add and delete a pet", %{connection: connection} do
petId = 10007 petId = 10007
pet = %Pet{ pet = %Pet{
:id => petId, :id => petId,
:name => "elixir client test", :name => "elixir client test",
:photoUrls => ["http://test_elixir_unit_test.com"], :photoUrls => ["http://test_elixir_unit_test.com"],
:category => %Category{:id => petId, :name => "test elixir category"}, :category => %Category{:id => petId, :name => "test elixir category"},
:tags => [%Tag{:id => petId, :name => "test elixir tag"}], :tags => [%Tag{:id => petId, :name => "test elixir tag"}]
} }
{:ok, response} = PetApi.add_pet(Connection.new, pet)
{:ok, %Tesla.Env{} = response} = PetApi.add_pet(connection, pet)
assert response.status == 200 assert response.status == 200
{:ok, pet} = PetApi.get_pet_by_id(Connection.new, petId) {:ok, pet} = PetApi.get_pet_by_id(connection, petId)
assert pet.id == petId assert pet.id == petId
assert pet.name == "elixir client test" assert pet.name == "elixir client test"
assert List.first(pet.photoUrls) == "http://test_elixir_unit_test.com" assert pet.photoUrls == ["http://test_elixir_unit_test.com"]
assert pet.category.id == petId assert pet.category == %Category{id: petId, name: "test elixir category"}
assert pet.category.name == "test elixir category" assert pet.tags == [%Tag{:id => petId, :name => "test elixir tag"}]
assert List.first(pet.tags) == %Tag{:id => petId, :name => "test elixir tag"}
{:ok, response} = PetApi.delete_pet(Connection.new, petId) {:ok, response} = PetApi.delete_pet(connection, petId)
assert response.status == 200 assert response.status == 200
{:ok, response} = PetApi.get_pet_by_id(Connection.new, petId) {:ok, response} = PetApi.get_pet_by_id(connection, petId)
assert response.status == 404 assert response.status == 404
end end
test "update a pet" do test "update a pet", %{connection: connection} do
petId = 10007 petId = 10007
pet = %Pet{ pet = %Pet{
:id => petId, :id => petId,
:name => "elixir client updatePet", :name => "elixir client updatePet",
:status => "pending", :status => "pending",
:photoUrls => ["http://test_elixir_unit_test.com"] :photoUrls => ["http://test_elixir_unit_test.com"]
} }
{:ok, response} = PetApi.update_pet(Connection.new, pet)
{:ok, response} = PetApi.update_pet(connection, pet)
assert response.status == 200 assert response.status == 200
{:ok, pet} = PetApi.get_pet_by_id(Connection.new, petId) {:ok, pet} = PetApi.get_pet_by_id(connection, petId)
assert pet.id == petId assert pet.id == petId
assert pet.name == "elixir client updatePet" assert pet.name == "elixir client updatePet"
assert pet.status == "pending" assert pet.status == "pending"
end end
test "find pet by status" do test "find pet by status", %{connection: connection} do
{:ok, listPets} = PetApi.find_pets_by_status(Connection.new, "available") {:ok, listPets} = PetApi.find_pets_by_status(connection, "available")
assert List.first(listPets) != nil assert List.first(listPets) != nil
{:ok, listPets} = PetApi.find_pets_by_status(Connection.new, "unknown_and_incorrect_status") {:ok, listPets} = PetApi.find_pets_by_status(connection, "unknown_and_incorrect_status")
assert List.first(listPets) == nil assert List.first(listPets) == nil
end end
end end

View File

@ -0,0 +1,17 @@
defmodule StoreTest do
use ExUnit.Case, async: true
alias OpenapiPetstore.Connection
alias OpenapiPetstore.Api.Store, as: StoreApi
setup do
%{connection: Connection.new()}
end
test "fetch inventory", %{connection: connection} do
{:ok, inventory} = StoreApi.get_inventory(connection)
assert is_map(inventory)
assert Enum.all?(Map.keys(inventory), &is_binary/1)
assert Enum.all?(Map.values(inventory), &is_integer/1)
end
end