Open tobbbles opened 1 year ago
@barttenbrinke As the Jason->Poison author and the creator of the type decoding magic, any ideas?
This is still broken on the new implementation and blocking usage of openapi-generator in Elixir for any string enum components.
@tobbbles Sorry, this only just appeared in my inbox for some reason /cc @wpiekutowski
TLDR - Swagger and thus the openapi generator sees the Enum as a authenticatorAssuranceLevel struct, while it basically is just a string. As it is not really doable to handle enums in a struct like this in Elixir, just returning the string might just be the simplest solution here.
We need a simpler example than the whole ORY library
Hi @barttenbrinke, thanks for looking around to this. I've made a couple of attempts to fix or investigate this but I don't get any further. What I find very weird is I'm only seeing this behaviour when the string enum is a component/outer enum
This is currently existing in the petstore sample and the generated Elixir client in the repository.
The pet store 3_0 enum test: https://github.com/OpenAPITools/openapi-generator/blob/8e9a17fe0252c1290f585d1fb680c4e64cb5495a/modules/openapi-generator/src/test/resources/3_0/petstore-with-fake-endpoints-models-for-testing.yaml#L1653-L1689
generates the following module: https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/elixir/lib/openapi_petstore/model/enum_test.ex
Which here we can see there is no issue with enum_string
being just a type of String.t
; however the outer enum is generated into a module as seen with the Ory example https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/elixir/lib/openapi_petstore/model/outer_enum.ex
As it is not really doable to handle enums in a struct like this in Elixir
Would we instead aim to infer the type of out enums and use those as the base type for these fields, compared to generating modules for outer enums? On one hand I like that a module could be generated with a word list for valid enum values; however like you say Elixir doesn't lend itself well to this style of enum.
I believe adding the following test case will highlight this:
defmodule EnumTest do
use ExUnit.Case, async: true
alias OpenapiPetstore.Deserializer
alias OpenapiPetstore.Model.EnumTest
@valid_json """
{
"enum_string": "UPPER",
"outerEnum": "placed"
}
"""
test "jason_decode/2 with valid JSON" do
assert Deserializer.jason_decode(@valid_json, EnumTest) ==
{:ok,
%EnumTest{
enum_string: "UPPER",
outerEnum: "placed"
}}
end
end
With the output:
$ mix test
...
1) test jason_decode/2 with valid JSON (EnumTest)
test/enum_test.exs:14
** (FunctionClauseError) no function clause matching in OpenapiPetstore.Deserializer.to_struct/2
The following arguments were given to OpenapiPetstore.Deserializer.to_struct/2:
# 1
"placed"
# 2
OpenapiPetstore.Model.OuterEnum
Attempted function clauses (showing 3 out of 3):
defp to_struct(nil, _)
defp to_struct(list, module) when is_list(list) and is_atom(module)
defp to_struct(map, module) when is_map(map) and is_atom(module)
code: assert Deserializer.jason_decode(@valid_json, EnumTest) ==
stacktrace:
(openapi_petstore 1.0.0) lib/openapi_petstore/deserializer.ex:97: OpenapiPetstore.Deserializer.to_struct/2
(elixir 1.15.7) lib/map.ex:916: Map.update!/3
(openapi_petstore 1.0.0) lib/openapi_petstore/model/enum_test.ex:36: OpenapiPetstore.Model.EnumTest.decode/1
(openapi_petstore 1.0.0) lib/openapi_petstore/deserializer.ex:19: OpenapiPetstore.Deserializer.jason_decode/2
test/enum_test.exs:15: (test)
@barttenbrinke @tobbbles, apologies for the ping. Is this bug being tracked and worked on? I'm having issues verifying sessions in my Elixir project, which renders the Ory client unusable.
Frontend.to_session(Ory.Connection.new(), Cookie: cookies(conn))
[error] ** (FunctionClauseError) no function clause matching in Ory.Deserializer.to_struct/2
@binajmen The core issue is that there is no simple test / example that reproduces the issue at the moment. All enum tests work in the OpenAPITools suite and the simple test @tobbbles made also succeeds. I am not working on it atm, if somebody creates a test / example that reproduces the error I would be able to fix it quite quickly I think.
I would love to help, but I'm clueless about where to start. @tobbbles, is there a reason why your test succeeded with the OpenAPITools suite?
Hi folks, finally got some vacation time to circle back on this. Are we sure the example I posted succeeds? With a fresh checkout I have the following; however in https://github.com/OpenAPITools/openapi-generator/pull/19435 where I introduce this test case, it looks like the CI is not executing it.
Could someone check to reproduce as I have please?
$ cat >samples/client/petstore/elixir/test/issue.exs<<EOF
defmodule IssueTest do
use ExUnit.Case, async: true
alias OpenapiPetstore.Deserializer
alias OpenapiPetstore.Model.EnumTest
@valid_json """
{
"enum_string": "UPPER",
"outerEnum": "placed"
}
"""
test "jason_decode/2 with valid JSON" do
assert Deserializer.jason_decode(@valid_json, EnumTest) ==
{:ok,
%EnumTest{
enum_string: "UPPER",
outerEnum: "placed"
}}
end
end
EOF
$ cd samples/client/petstore/elixir
$ mix deps.get
...
$ mix test test/issue.exs
Running ExUnit with seed: 24428, max_cases: 64
1) test jason_decode/2 with valid JSON (IssueTest)
test/issue.exs:14
** (FunctionClauseError) no function clause matching in OpenapiPetstore.Deserializer.to_struct/2
The following arguments were given to OpenapiPetstore.Deserializer.to_struct/2:
# 1
"placed"
# 2
OpenapiPetstore.Model.OuterEnum
Attempted function clauses (showing 3 out of 3):
defp to_struct(nil, _)
defp to_struct(list, module) when is_list(list) and is_atom(module)
defp to_struct(map, module) when is_map(map) and is_atom(module)
code: assert Deserializer.jason_decode(@valid_json, EnumTest) ==
stacktrace:
(openapi_petstore 1.0.0) lib/openapi_petstore/deserializer.ex:97: OpenapiPetstore.Deserializer.to_struct/2
(elixir 1.17.2) lib/map.ex:916: Map.update!/3
(openapi_petstore 1.0.0) lib/openapi_petstore/model/enum_test.ex:36: OpenapiPetstore.Model.EnumTest.decode/1
(openapi_petstore 1.0.0) lib/openapi_petstore/deserializer.ex:19: OpenapiPetstore.Deserializer.jason_decode/2
test/issue.exs:15: (test)
Finished in 0.05 seconds (0.05s async, 0.00s sync)
1 test, 1 failure
I think the main root issue here is the generated enum modules are just empty struct,
iex(3)> OpenapiPetstore.Model.OuterEnum.__struct__
%OpenapiPetstore.Model.OuterEnum{}
Which is more or less unusable.
Bug Report Checklist
Description
16061 appears to have introduced a bug when attempting to deserialize a string into an enum model.
Here we see
to_struct/2
being called on the value of "aal1", to be decoded into theOry.Model.AuthenticatorAssuranceLevel
model. However there is no implementation from this.This is a regression from the prior generators, and the expected output is to be able to deserialize into these modules/structs.
openapi-generator version
v7.0.0
OpenAPI declaration file content or url
Any string-typed enum field, for example this from the Ory SDK
Generation Details
The above spec will output the following model module.
Steps to reproduce
Generate any OpenAPI schema with an enum string model and you will get this. For further testing using the Ory SDK, you can checkout my fork and generate the elixir client by commenting out lines 235-333 in
scripts/generate.sh
and running$ FORCE_PROJECT=client FORCE_VERSION=(cat spec/client/latest) ./scripts/generate.sh
to invoke the openapi-generator-cli to generate the client atclients/client/elixir/
.Related issues/PRs
16061 Introduced this regression
Suggest a fix.
Implement a way to deserialize enum modules. Ideally this would go further and implement a word list to validate whether a string value is a valid enum when trying to deserialize into the