-
-
Notifications
You must be signed in to change notification settings - Fork 6.4k
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
[Codegen] Convert "any type" to oneOf model #6051
[Codegen] Convert "any type" to oneOf model #6051
Conversation
@wing328 , @jimschubert, what do you think? What is the proper way to handle "any type"? This is just a draft PR to get feedback before I go any further. |
Thanks for the PR. Just want to clarify a little bit, oneOf means only (and exactly) one type/schema is matched. If the value is 2 for example, it will match both integer and number so it will fail the match. anyOf allows matching more than one schema defined in the anyOf schema list. Ref: https://swagger.io/docs/specification/data-models/data-types/ => "Any Type" cc @OpenAPITools/generator-core-team |
…odegen-any-type-to-oneof-schema-2
Good point, I am changing to anyOf. |
…odegen-any-type-to-oneof-schema-2
…s' attribute is not specified
@spacether for Python-experimental bug fix. |
// Per JSON schema specification, the array "items" attribute is optional. | ||
// When "items" is not specified, the elements of the array | ||
// may be anything at all. | ||
return prefix + "[bool, date, datetime, dict, float, int, list, str, none_type]" + fullSuffix; |
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.
@spacether , can you help review this? Is this the right way to fix the issue?
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.
So this will only happen if we are in an array, right?
Don't we instead need to cover the ModelUtils.isAnyTypeSchema(schema) case?
How about higher up at line 907 use:
Schema anyType = new Schema();
if (ModelUtils.isAnyTypeSchema(schema)):
return prefix + "bool, date, datetime, dict, float, int, list, str, none_type" + fullSufix;
} else if ((ModelUtils.isMapSchema(p) || "object".equals(p.getType())) &&
...
} else if (ModelUtils.isArraySchema(p))
ArraySchema ap = (ArraySchema) p;
Schema inner = ap.getItems();
if (inner == null) {
inner = anyType;
}
return prefix + "[" + getTypeString(inner, "", "") + "]" + fullSuffix;
...
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.
We also need the if (ModelUtils.isAnyTypeSchema(schema)):
in case we travel through an object(dict) to get there. In that case, our return would look like:
{str: (bool, date, datetime, dict, float, int, list, str, none_type)}
…s' attribute is not specified
cs.addAnyOfItem(new ArraySchema()); | ||
if (schema != null) { | ||
cs.setTitle(schema.getTitle()); | ||
cs.setDescription(schema.getDescription()); |
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.
Initially I thought I should create a single "AnyType" schema, but in reality there may be corner cases where constraints other than "type" have been specified:
title
pattern
required
enum
minimum
maximum
exclusiveMinimum
exclusiveMaximum
multipleOf
minLength
maxLength
minItems
maxItems
uniqueItems
minProperties
maxProperties
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.
But aren't those constraints specific to types? If you had minItems it would only apply to dict and array, right? My take is if they have any of those constraints then they should be fully explicit and list all types. We are just trying to cover this one super general yoy said it could be anything case. What do you think?
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.
That way that constraint question is the headache of specific generators. I agree with you about adding this model/schema once and then using it multiple places if the writers used it multiple places.
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.
But aren't those constraints specific to types? If you had minItems it would only apply to dict and array, right? My take is if they have any of those constraints then they should be fully explicit and list all types. We are just trying to cover this one super general yoy said it could be anything case. What do you think?
I am planning to handle these edge cases later. Right now just trying to make the simple case work, i.e. no OAS attribute whatsoever is defined in the OAS schema, it's really any type. Even this simple case is not so simple.
Also, the good news is most of these constraints are specific to a type. The only constraints that apply to all types are type, enum and const: https://tools.ietf.org/html/draft-handrews-json-schema-validation-02
Shouldn't it be oneOf? Why should it be anyOf? null can never be a Dict or List. |
Because 2 can be both Integer and Number. |
if (ModelUtils.isAnyTypeSchema(schema)) { | ||
// "Any type" means the payload can be any type, e.g. integer, number, object, array... | ||
return getAnyTypeModel(name, 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.
This code only covers the case when the user include AnyType as a model schema.
Below I see that you you also handle the Model property case, but that code does not create a schema/model that the property can refer to.
What if we have AnyTypeSchema in:
- a Model property
- a Model
- Inline in a response definition?
For example if AnyType is only in a model property AND/or an inline response definition there is no Model/Schema that it should be $ref connected to. In my opinion, we should make this model so it can be handled by all generators.
How about instead in the InlineModelResolver
- pre-processing the the openapi document to check for the locations that need to be fixed
- then insert qty 1 anyTypeModel into the openapi document
AnyType:
anyOf:
- type: "null"
- type: integer
- type: array ...
- link all references that require it to that $ref
Then we don't need any specific logic like you are adding in python-experimental.
Just want to clarify a bit. Currently,
Next version of OpenAPI Spec should align better with JSON schema: OAI/OpenAPI-Specification#1977 To model array of any type, please use the following instead: array_any_value:
items: {}
array_any_value_with_desc:
items:
description: inline any value
array_any_value_nullable:
items:
nullable: true
description: inline any value nullable (I've a pending PR for better support of any type: #6091) |
Doesn't this state that if "items" is present, then items must be an object? It does not actually state that items must be present.
|
But it says: |
Sorry, good catch, I missed that. So to expand "any type", it would have to be something like this: AnyType:
anyOf:
- type: 'null'
- type: integer
- type: number
- type: string
- type: object
- type: array
items:
$ref: '#components/schema/AnyType' |
Yes, I think that that will work. |
Actually, that statement was removed in the 3.1 specification, to be more inline with JSON schema. So it is no longer required to have |
cc @OpenAPITools/generator-core-team as it impacts default codegen |
Issue 1:
In the JSON schema and OAS specifications, the array "items" attribute is NOT required. However I found out some language generators assume incorrectly that "items" is required. I am fixing the issue for:
https://json-schema.org/understanding-json-schema/reference/array.html
Issue 2
An OAS document may have a schema that does not specify the "type" attribute. For example, below notice the "type" attribute has not been set in the "annotation" property.
This is equivalent to writing:
To help with the code generation, I am proposing that codegen converts the "any type" to a anyOf composed schema. The alternative is every language generator has its own way to handle "any type", but it seems more generic to do this conversion. Potentially this could be controlled by a codegen runtime property.
PR checklist
./bin/
(or Windows batch scripts under.\bin\windows
) to update Petstore samples related to your fix. This is important, as CI jobs will verify all generator outputs of your HEAD commit, and these must match the expectations made by your contribution. You only need to run./bin/{LANG}-petstore.sh
,./bin/openapi3/{LANG}-petstore.sh
if updating the code or mustache templates for a language ({LANG}
) (e.g. php, ruby, python, etc).master
,4.3.x
,5.0.x
. Default:master
.