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
Expose API for building JSON schemas from Tapir Schemas #2873
Conversation
@kamilkloch could you take a look at the proposed API in the PR description? Would such way of obtaining JSON schemas be useful for use cases like the one you had? |
@kciesielski Thank you! Looking into it. |
We might want to expose a slightly modified API, similar to what Other remarks: val intListSchema = implicitly[Schema[List[Int]]]
val result = TapirSchemaToJsonSchema(List(intListSchema), markOptionsAsNullable = false)
println(result.keys) currently prints {
"$schema" : "http://json-schema.org/draft-04/schema#",
"type" : "array",
"items" : {
"type" : "integer"
}
} Also, for import sttp.apispec.circe._
case class Child(childName: String)
val childSchema = implicitly[Schema[Child]]
val result = TapirSchemaToJsonSchema(List(childSchema), markOptionsAsNullable = false)
println(result("Child").value.asJson.deepDropNullValues) the schema is missing
EDIT: |
Thanks a lot for your input @kamilkloch! What you call |
That might be a nice approach, yes :) Points to consider:
|
We might want to start with one metaSchema and consider support for switching them in the future, if there's demand. You mentioned
Sounds good! One more thought, regarding the hardest part, which is always naming things ;) As far as I understand the specification is maintained outside of the OpenAPI community, so |
Agreed on both (fixed meta schema for now and JsonSchema as a proposed name). Which meta schema - probably the one we are currently compatible with, I do not know which is that... 🤔 |
@kamilkloch Thanks again for your priceless input :)
case class Parent(innerChildField: Child)
case class Child(childNames: List[String])
val tSchema = implicitly[Schema[Parent]]
val jsonSchemas: JsonSchemas = JsonSchemas(
List(tSchema),
markOptionsAsNullable = true,
metaSchema = Some(MetaSchema202012), // default = None
schemaName = defaultSchemaName // default
)
import sttp.apispec.circe._
val schemasAsJson = jsonSchemas.schemas.values.asJson which gives [
{
"$schema" : "https://json-schema.org/draft/2020-12/schema",
"required" : [
"innerChildField"
],
"type" : "object",
"properties" : {
"innerChildField" : {
"$ref" : "#/components/schemas/Child"
}
}
},
{
"$schema" : "https://json-schema.org/draft/2020-12/schema",
"required" : [
"childName"
],
"type" : "object",
"properties" : {
"childName" : {
"type" : "string"
}
}
}
]
WDYT? |
@kciesielski Thank you for your work! I feel a bit akward only commenting on your code and not writing the code myself. That said:
I am not sure if:
{
"$schema":"http://json-schema.org/draft-04/schema#",
"type":"object",
"additionalProperties":false,
"properties":{
"innerChildField":{
"$ref":"#/definitions/Child"
}
},
"required":[
"innerChildField"
],
"definitions":{
"Child":{
"type":"object",
"additionalProperties":false,
"properties":{
"childName":{
"type":"string"
}
},
"required":[
"childName"
]
}
}
} Since the output (JSON schema) should be one Json object, I am now not sure if accepting EDIT: That is why I am not sure if
|
Thanks for clarifying, I think I misunderstood some crucial points, which seem much clearer now. Indeed we should just be fine with a single Tapir schema converter, but one which builds a structure of the type definition plus all referenced types in the |
Hm, it would probably be nice to strive for compatibility with Json meta schema, so that clients (IDE, for example), can use this schema for json input validation. Example - https://www.schemastore.org/json/ used within IDEs. Not sure how it will fly with OpenAPI standard. |
@kamilkloch Thanks again for your time spent looking at this! object Childhood {
case class Child(age: Int, height: Option[Int])
}
case class Parent(innerChildField: Child, childDetails: Childhood.Child)
case class Child(childName: String) // to illustrate unique name generation
val tSchema = implicitly[Schema[Parent]]
val jsonSchema: ASchema = JsonSchemas(
tSchema,
markOptionsAsNullable = true,
metaSchema = MetaSchemaDraft04, // default
schemaName = defaultSchemaName // default
)
import io.circe.Printer
import io.circe.syntax._
import sttp.apispec.circe._
val schemaAsJson = jsonSchema.asJson
println(Printer.spaces2.print(schemaAsJson.deepDropNullValues)) and this gives {
"$schema" : "https://json-schema.org/draft-04/schema#",
"required" : [
"innerChildField",
"childDetails"
],
"type" : "object",
"properties" : {
"innerChildField" : {
"$ref" : "#/$defs/Child"
},
"childDetails" : {
"$ref" : "#/$defs/Child1"
}
},
"$defs" : {
"Child" : {
"required" : [
"childName"
],
"type" : "object",
"properties" : {
"childName" : {
"type" : "string"
}
}
},
"Child1" : {
"required" : [
"age"
],
"type" : "object",
"properties" : {
"age" : {
"type" : "integer",
"format" : "int32"
},
"height" : {
"type" : [
"integer",
"null"
],
"format" : "int32"
}
}
}
}
} I checked some other cases with https://www.jsonschemavalidator.net/ and I couldn't generate a failing example so far. |
@kciesielski Wow, that looks great, thank you! Back to the most difficult problem ;): do want to keep the name |
I thought about it too, the name no longer sounds appropriate. Maybe we should get back to |
val schemaIds = keysToSchemas.map { case (k, v) => k -> ((keysToIds(k), v)) } | ||
|
||
val nestedKeyedSchemas = (schemaIds.values) | ||
// TODO proper handling of ref input schema |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thought: Should we just make the method return a ReferenceOr[ASchema]
and let the user handle this?
Great that you found the right API :) One thing missing - mention |
Solves #2838
This PR exposes a method which allows to convert a Tapir schema to
ASchema
(JSON schema). It uses similar mechanism to the internals ofSchemasForEndpoints
.Proposal for the new API:
to encode the schema:
which gives:
Checklist