-
-
Notifications
You must be signed in to change notification settings - Fork 6.7k
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
[Python] handle oneOf/allOf/anyOf composed schemas in the Python client #2121
Conversation
18a6e0a
to
e72bd07
Compare
Hi @rienafairefr, Thank you very much for this PR! I have been doing some tests on your code. It seems to work on basic situations, but I still have some troubles with special/extended cases. The final class implementation still seems to miss some attributes of the base class when using multi-level inheritance. Also when referencing Python server comparisonPlease note that the Python server ( This implementation also avoids issues with It there a possibility to sync the client implementation with the server implementation regarding model definitions? Another final noteThe "child" (or "final") implementations in the Python models (either client or server) could give a proper default value to the
This would be very convenient for users of the API so they won't need to provide that field (which is mostly the same as the class name). Especially when they need to provide a new item to a Python function (i.e. REST operation) Example (stripped very much):BaseClass:
discriminator:
propertyName: implType
properties:
implType:
type: string
commonData:
type: string
SubClass:
discriminator:
propertyName: implType
allOf:
- $ref: BaseClass
- properties:
moreCommonData:
type: string
MyImpl:
allOf:
- $ref: SubClass
- properties:
implData:
type: string With best regards, |
Thanks for the review @tomghyselinck |
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.
Hi @rienafairefr,
I was a little bit confused about the python-flask
code regarding allOf
/oneOf
/anyOf
support.
I was wrong about that, I still need to add model conversion myself in the "controller" implementations.
def child_post(base_class): # noqa: E501
"""Create a new child
:param base_class: Child description
:type base_class: dict | bytes
:rtype: BaseClass
"""
# XXX - Generated by openapi-generator:
if connexion.request.is_json:
base_class = BaseClass.from_dict(connexion.request.get_json()) # noqa: E501
# XXX - Added by us:
if type(base_class) is BaseClass:
# XXX - We need the final implementation => resolve ourselves...
if base_class.impl_type == 'SomeChild':
base_class = SomeChild.from_dict(connexion.request.get_json()) # noqa: E501
if base_class.impl_type == 'OtherChild':
base_class = OtherChild.from_dict(connexion.request.get_json()) # noqa: E501
# Continue with the implementation...
We will probably need another PR for the server controllers properly supporting allOf
/oneOf
/anyOf
.
I made a review on the client implementation.
With best regards,
Tom.
Hi @rienafairefr, Are you aware of #2049 which should also fix the support for With best regards, |
Hi @tomghyselinck, I looked at #2049 but I don't think the cases of anyOf/oneOf are handled inthere :-| |
@rienafairefr can you describe what your code does for the case of the Dog class? How about the below proposal (store the children)?For the Dog class instance, store the below instances of child classes:
The Dog needs properties added from those child classes. |
One more use case that I'm not sure it this PR covers:
|
@rienafairefr the feature looks like it's almost there. Can you add tests to this PR that demonstrate instantiation and property access of Dog or other composed schemas? |
@spacether Json schemas encompass more stuff than just inheritance like in the oop world, so even though it's tempting, we can't really do only that. for allOf, yeah, we could have instances of the 'child' classes, and delegate to those. For anyOf or oneOf it would be possible too, but a bit more useless because anyOf or oneOf are instance of only one of the sub-classes. There is already a deserialization of Dog instances (e.g. test_deserialize_dict_str_dog ), but it's using the disciminator thing. The standard petstore spec doesn't have anyOf or oneOf. I'll add that to the |
@rienafairefr thank you for explaining how oop inheritance won't work here. Doesn't anyOf allow for one or more child schema matches? That's what this source describes: https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/ Adding those tests in the openapi python client sample sounds great! |
Hi @spacether, Sorry for the late reply. What do you mean with alternative to using |
Hi @rienafairefr and @tomghyselinck I want to make sure that this implementation covers the case that I described above for accessing all properties on a model instance that has composed schema. Looking at Dog and Cat:
A Dog instance should have the properties:
A Cat instance should have the properties:
Only className is required per allOf: Looking at our tests, I don't see any tests for instance properties on Dog and Cat instances.
What do you think? |
b045ab9
to
c66f4f5
Compare
oneOf/anyOf/allOf composed models have been added in the python-experimental generator in this diff: #4446 |
Thanks @spacether I'll test out he python-experimental generator, this PR can probably be closed |
@rienafairefr thank you for this PR. |
I think the |
PR checklist
./bin/
to update Petstore sample so that CIs can verify the change. (For instance, only need to run./bin/{LANG}-petstore.sh
,./bin/openapi3/{LANG}-petstore.sh
,./bin/security/{LANG}-petstore.sh
and./bin/openapi3/security/{LANG}-petstore.sh
if updating the {LANG} (e.g. php, ruby, python, etc) code generator or {LANG} client's mustache templates). Windows batch files can be found in.\bin\windows\
.master
,. Default:3.4.x
,4.0.x
master
.@wing328 @ci
Description of the PR
I saw that
allOf
/oneOf
/anyOf
was not yet fully handled in general, and in particular for my use case, in the Python client codegen. This PR is a tentative to handle deserialization of models that use oneOf/anyOf/allOf, by making use of theallOf
/oneOf
/anyOf
fields inCodegenModel
I've added a
composed_hierarchy
to the model, which is specified when the underlying openapi model uses oneOf/allOf/anyOf, if there is no discriminator field. When receiving some data, to deserialize it to the correct class, we try to match the incoming data to all possible subclassewith oneOf/anyOf, the instance that is returned will be of the correct subclass, and with allOf, we return an instance of the current model class.
The case of a more advanced schema (when more than one of
allOf
/oneOf
/anyOf
is used) is not handled.Caveat: cases of inline models might not be handled properly in some or all of the cases.