Skip to content

fix(parser): indirect and recursive reference resolution (v2) #419

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

Conversation

p1-ra
Copy link
Contributor

@p1-ra p1-ra commented May 8, 2021

This PR aims to resolve indirect reference resolution and recursive reference resolution #338; It is a rework of #329

  • 806df1e : add support to indirect reference resolution, which is kind of strait forward thanks to the fact that models resolution are now based on their path
  • c7da89e : add support to recursive reference resolution, which is still a little bit tricky

The goal here was to avoid the use of any global object, but since we have to lazy resolve a model during its own construction we still need to somehow be able to reference it before it even exist. For that I'm using the following strategy:

First, I've update the Schemas model:
https://github.com/triaxtec/openapi-python-client/blob/c7da89e201c66caf123961afd76cd5a38b2fec88/openapi_python_client/parser/properties/schemas.py#L66-L71
Properties are now wrapped into an _Holder object which is mutable. The goal here is to be able to register a classes_by_reference toward an not existing Property model yet. That why it can contains an RecursiveReferenceInterupt which is an PropertyError that we will use later during the ModelProperty parsing to create a specific lazy reference Property.

To be able to detected recursive reference during schema update from OAI data, I'm tracking all already visited reference path:
https://github.com/triaxtec/openapi-python-client/blob/c7da89e201c66caf123961afd76cd5a38b2fec88/openapi_python_client/parser/properties/__init__.py#L629

https://github.com/triaxtec/openapi-python-client/blob/c7da89e201c66caf123961afd76cd5a38b2fec88/openapi_python_client/parser/properties/schemas.py#L121-L126

When an recursive reference is detected, we create a _Holder for it and return an RecursiveReferenceInterupt to the build loop
https://github.com/triaxtec/openapi-python-client/blob/c7da89e201c66caf123961afd76cd5a38b2fec88/openapi_python_client/parser/properties/__init__.py#L633-L642
Tiny trick here, I'm using the error to return the updated version of schema (which contains a reference to an not existing Property class); That not resolved items will be then re-processed at the end of the main build loop:
https://github.com/triaxtec/openapi-python-client/blob/c7da89e201c66caf123961afd76cd5a38b2fec88/openapi_python_client/parser/properties/__init__.py#L649-L653

Now come the part of the ModelProperty construction, during the creation of one of its property from a reference, If the underlaying holded data is an RecursiveReferenceInterupt, it will create an LazySelfReferenceProperty, passing to it the _Holder object which currently contains an RecursiveReferenceInterupt but will later on, after its re-processing will contain a valid ModelProperty
https://github.com/triaxtec/openapi-python-client/blob/c7da89e201c66caf123961afd76cd5a38b2fec88/openapi_python_client/parser/properties/__init__.py#L456-L486

The LazySelfReferenceProperty will then proxy the required method call to the ModelProperty contains in the _Holder object:
https://github.com/triaxtec/openapi-python-client/blob/c7da89e201c66caf123961afd76cd5a38b2fec88/openapi_python_client/parser/properties/__init__.py#L37-L87

This PR is not fully ready yet, I haven't updated the unit tests (but the golden records have been updated for the reference), I will wait for feedback regarding the used strategy before moving forward.

@p1-ra p1-ra force-pushed the fix/indirect-and-recursive-reference-resolution branch 2 times, most recently from ef4eebd to 20d3b15 Compare May 8, 2021 15:28
@p1-ra p1-ra force-pushed the fix/indirect-and-recursive-reference-resolution branch from 20d3b15 to 47adea9 Compare May 8, 2021 15:36
@p1-ra p1-ra force-pushed the fix/indirect-and-recursive-reference-resolution branch from 2df1eb0 to b445c09 Compare May 18, 2021 22:26
@p1-ra p1-ra force-pushed the fix/indirect-and-recursive-reference-resolution branch from b445c09 to 5980501 Compare May 18, 2021 22:31
@p1-ra
Copy link
Contributor Author

p1-ra commented Jun 9, 2021

Jfi, the current way to detect recursion has false positives and thus do not works as expected

@mtovts
Copy link
Contributor

mtovts commented Aug 10, 2021

@p1-ra Is there any work in this direction? I have OpenAPI where most of the schemas are referenced indirectly 😅

Why we cannot just create a model with empty properties for indirectly referenced schemas and fill it later when proceed by order?
I wrote hotfix to implement this. Most models are generated on it. But there are bugs with several models. I suppose the bugs are related to the generation strategy in _property_from_data(). I will investigate this a little more.

@dbanty What do you think about this way? Is it compatible with the other problems with references you mentonied in March?

@mtovts
Copy link
Contributor

mtovts commented Aug 10, 2021

-        return PropertyError(data=data, detail="Could not find reference in parsed models or enums"), schemas
+        return (
+            AnyProperty(
+                name=name,
+                required=required,
+                nullable=False,
+                default=None,
+                python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix),
+            ),
+            schemas,
+        )

That change in L422 parser/properties/init.py leads to generation of extra models for me that were absent before.

@dbanty
Copy link
Collaborator

dbanty commented Aug 14, 2021

@mtovts I was about to mention this PR in yours, but I see you already found it 😅. The bigger refactor I talked about previously is already done, it was a change that resulted in the Schemas class as it is today, decoupling generated class name from reference path.

This PR is definitely stale, I've neglected it a long time and now there are conflicts 😁 so if you make yours have no overlap with the existing forward-resolution support (either by taking out the old method or utilizing it) then I think yours replaces this one.

@dbanty
Copy link
Collaborator

dbanty commented Oct 17, 2021

If I understand correctly, this branch doesn't work as anticipated and there isn't a plan on picking it back up. So I'm going to close this for now. I still want to support better reference resolution overall, but my efforts are focused on a different, super-secret project right now 😬

@dbanty dbanty closed this Oct 17, 2021
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

Successfully merging this pull request may close these issues.

3 participants