Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failed to generate Scala code from the Fireblocks OpenAPI schema: Unsupported number type: class NumberSchema #353

Open
ikr opened this issue Mar 4, 2024 · 9 comments

Comments

@ikr
Copy link

ikr commented Mar 4, 2024

I followed the instructions in the README, providing the Fireblocks OpenAPI schema YAML as the source:

lazy val fireblocksClient = project
  .in(file("modules/fireblocks-client"))
  .enablePlugins(OpenApiSchema)
  .settings(name := "fireblocks-client", libraryDependencies ++= Bundles.akkaHttp)
  .settings(
    openapiTargetLanguage := Language.Scala,
    openapiSpec           := (Compile / resourceDirectory).value / "fireblocks-openapi.yaml"
  )

The code generation resulted in the following error:

[IJ]last fireblocksClient / openapiCodegen
[error] java.lang.RuntimeException: Unsupported number type: class NumberSchema {
[error]     class Schema {
[error]         type: number
[error]         format: null
[error]         $ref: null
[error]         description: Number of hours for expiration.This data is valid only it ticket not in DRAFT state and it will be used to calculate expiresAt value
[error]         title: null
[error]         multipleOf: null
[error]         maximum: null
[error]         exclusiveMaximum: null
[error]         minimum: null
[error]         exclusiveMinimum: null
[error]         maxLength: null
[error]         minLength: null
[error]         pattern: null
[error]         maxItems: null
[error]         minItems: null
[error]         uniqueItems: null
[error]         maxProperties: null
[error]         minProperties: null
[error]         required: null
[error]         not: null
[error]         properties: null
[error]         additionalProperties: null
[error]         nullable: null
[error]         readOnly: null
[error]         writeOnly: null
[error]         example: 13
[error]         externalDocs: null
[error]         deprecated: null
[error]         discriminator: null
[error]         xml: null
[error]     }
[error] }
[error]         at scala.sys.package$.error(package.scala:30)
[error]         at com.github.eikek.sbt.openapi.impl.Parser$.schemaType(Parser.scala:139)
[error]         at com.github.eikek.sbt.openapi.impl.Parser$.makeProperty(Parser.scala:100)
[error]         at com.github.eikek.sbt.openapi.impl.Parser$.$anonfun$makeSchemaClass$8(Parser.scala:79)
[error]         at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:286)
[error]         at scala.collection.Iterator.foreach(Iterator.scala:943)
[error]         at scala.collection.Iterator.foreach$(Iterator.scala:943)
[error]         at scala.collection.AbstractIterator.foreach(Iterator.scala:1431)
[error]         at scala.collection.IterableLike.foreach(IterableLike.scala:74)
[error]         at scala.collection.IterableLike.foreach$(IterableLike.scala:73)
[error]         at scala.collection.AbstractIterable.foreach(Iterable.scala:56)
[error]         at scala.collection.TraversableLike.map(TraversableLike.scala:286)
[error]         at scala.collection.TraversableLike.map$(TraversableLike.scala:279)
[error]         at scala.collection.AbstractTraversable.map(Traversable.scala:108)
[error]         at com.github.eikek.sbt.openapi.impl.Parser$.makeSchemaClass(Parser.scala:78)
[error]         at com.github.eikek.sbt.openapi.impl.Parser$.$anonfun$parse$1(Parser.scala:19)
[error]         at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:286)
[error]         at scala.collection.immutable.HashMap$HashMap1.foreach(HashMap.scala:400)
[error]         at scala.collection.immutable.HashMap$HashTrieMap.foreach(HashMap.scala:728)
[error]         at scala.collection.immutable.HashMap$HashTrieMap.foreach(HashMap.scala:728)
[error]         at scala.collection.TraversableLike.map(TraversableLike.scala:286)
[error]         at scala.collection.TraversableLike.map$(TraversableLike.scala:279)
[error]         at scala.collection.AbstractTraversable.map(Traversable.scala:108)
[error]         at com.github.eikek.sbt.openapi.impl.Parser$.parse(Parser.scala:18)
[error]         at com.github.eikek.sbt.openapi.OpenApiSchema$.generateCode(OpenApiSchema.scala:107)
[error]         at com.github.eikek.sbt.openapi.OpenApiSchema$.$anonfun$defaultSettings$5(OpenApiSchema.scala:68)
[error]         at scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error]         at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:63)
[error]         at sbt.std.Transform$$anon$4.work(Transform.scala:69)
[error]         at sbt.Execute.$anonfun$submit$2(Execute.scala:283)
[error]         at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:24)
[error]         at sbt.Execute.work(Execute.scala:292)
[error]         at sbt.Execute.$anonfun$submit$1(Execute.scala:283)
[error]         at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
[error]         at sbt.CompletionService$$anon$2.call(CompletionService.scala:65)
[error]         at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error]         at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
[error]         at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[error]         at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[error]         at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[error]         at java.base/java.lang.Thread.run(Thread.java:834)
[error] (fireblocksClient / openapiCodegen) Unsupported number type: class NumberSchema {
[error]     class Schema {
[error]         type: number
[error]         format: null
[error]         $ref: null
[error]         description: Number of hours for expiration.This data is valid only it ticket not in DRAFT state and it will be used to calculate expiresAt value
[error]         title: null
[error]         multipleOf: null
[error]         maximum: null
[error]         exclusiveMaximum: null
[error]         minimum: null
[error]         exclusiveMinimum: null
[error]         maxLength: null
[error]         minLength: null
[error]         pattern: null
[error]         maxItems: null
[error]         minItems: null
[error]         uniqueItems: null
[error]         maxProperties: null
[error]         minProperties: null
[error]         required: null
[error]         not: null
[error]         properties: null
[error]         additionalProperties: null
[error]         nullable: null
[error]         readOnly: null
[error]         writeOnly: null
[error]         example: 13
[error]         externalDocs: null
[error]         deprecated: null
[error]         discriminator: null
[error]         xml: null
[error]     }
[error] }
[IJ]
@eikek
Copy link
Owner

eikek commented Mar 4, 2024

I think the problem is that the type number is not given a format. Usually one would specify float or double or something similar. The code currently doesn't use a default.

@eikek
Copy link
Owner

eikek commented Mar 4, 2024

I think the plugin could just use Double as a default, if no format is given. Not sure though, if that is the intended type in your case?

@ikr
Copy link
Author

ikr commented Mar 4, 2024

Hardly. From what I see in the Fireblocks API, number means an integer.

So that must be a mistake in their schema? Does the OpenAPI meta-schema require the format to be always present?

@eikek
Copy link
Owner

eikek commented Mar 4, 2024

From what I can see here https://swagger.io/docs/specification/data-models/data-types/ it is not required. They only say that number means "any numbers" which is not so helpful in a schema :) They could be integer or floating points. Hm, then perhaps using BigDecimal as a default would be ok. I think I left it out, because I wasn't sure what this means. Imho when they want an integer, they should specify a format. OTOH they don't specify anything, so they should be fine receiving floating points or integer numbers.

@ikr
Copy link
Author

ikr commented Mar 4, 2024

Right, that makes sense.

What would be the best way to deal with it, and make the tool work? Should I consider a custom type mapping maybe?

@eikek
Copy link
Owner

eikek commented Mar 4, 2024

Good question. I think there must be some default, so the parsing code can continue. Then a custom mapping can be used to map this to some other type if needed.

@eikek
Copy link
Owner

eikek commented Mar 4, 2024

I just fixed that problem with the number schema, but there are more problems with the fireblocks yaml file :/. For example, there is SignedMessage which specifies an anonymous object property signature. This is currently not supported. I think this requires an adhoc generation of a case class in scala.

@ikr
Copy link
Author

ikr commented Mar 5, 2024

Oh, cool! Thanks for looking into it!
Yes, there are quite a few anonymous types in the schema. In particular, some are members of a oneOf specifier, meaning a sum type is assumed. I guess those should be mapped into a set of sealed trait implementations, or a Scala enum.

@eikek
Copy link
Owner

eikek commented Mar 5, 2024

The plugin can unfortunately not cope with these anonymous objects. There is support for oneOf/allOf (using sealed traits) but only when the are separately defined. It should be doable to get it working, but I'm not sure when this will happen. So unfortunately this plugin won't be of good help here right now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants