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

fix: Fix bug parsing allOf in nested keys BNCH-20174 #29

Merged
merged 6 commits into from
Jan 14, 2021

Conversation

forest-benchling
Copy link

@forest-benchling forest-benchling commented Jan 13, 2021

Description

Our code was not correctly generating models where allOf was done on a nested key, such as NotFoundError:

    NotFoundError:
      type: object
      additionalProperties: false
      properties:
        error:
          allOf:
            - $ref: '#/components/schemas/BaseError'
            - properties:
                invalidId:
                  type: string
                type:
                  type: string
                  enum:
                    - invalid_request_error

The generated model has no error property.

This bug also affects ContainerContent, Folder, AssayRunSchema, AssayRequestSchema, and potentially more.

This PR tries to fix the bug by changing _property_from_data and build_model_property.

Test Plan

  • poetry run task check
  • poetry run task re
  • Made the changes in the openapi-specs submodule and rebuilt the client. Got these classes:
@attr.s(auto_attribs=True)
class NotFoundError:
    """  """

    error: Union[NotFoundErrorError, Unset] = UNSET
	...

@attr.s(auto_attribs=True)
class NotFoundErrorError:
    """  """

    invalid_id: Union[Unset, str] = UNSET
    type: Union[Unset, NotFoundErrorErrorType] = UNSET
    message: Union[Unset, str] = UNSET
    user_message: Union[Unset, str] = UNSET
    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
	...

class NotFoundErrorErrorType(str, Enum):
    INVALID_REQUEST_ERROR = "invalid_request_error"
  • Added automated test

@forest-benchling forest-benchling changed the title Fix allOf bug fix: Fix bug parsing allOf in nested keys BNCH-20174 Jan 13, 2021
@forest-benchling forest-benchling marked this pull request as ready for review January 13, 2021 21:26
@forest-benchling
Copy link
Author

cc @bowenwr

Copy link

@bowenwr bowenwr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@forest-benchling In scope of this how would you feel about fixing another issue with allOf that is blocking moving forward with the SDK?

Specifically that given this parameter in AaSequenceUpdate definition:

        fields:
          allOf:
            - $ref: "#/components/schemas/Fields"
            - description: >
                Fields to set on the AA sequence. Must correspond with the schema's field definitions.
                Every field should have its name as a key, mapping to an object with information about the value of
                the field.

We generate a field definition like:

    fields: Union[Unset, AaSequenceBaseRequestFields] = UNSET

Whereas we want to generate:

    fields: Union[Unset, Fields] = UNSET

The key to this is setting additionalProperties: false which would ideally be part of the allOf definition but setting it there has no effect in our current implementations.

If we corrected the spec to read:

    AaSequenceUpdate:
      allOf:
        - "$ref": "#/components/schemas/AaSequenceBaseRequest"
        - "$ref": "#/components/schemas/AaSequenceRequestAuthorIds"
        - "$ref": "#/components/schemas/AaSequenceRequestRegistryFields"
        - additionalProperties: false

I'd want it to work as specified above.

Copy link

@packyg packyg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGMT, though you should revert poetry.lock - we should keep in line with upstream dependencies.

@packyg
Copy link

packyg commented Jan 14, 2021

@bowenwr it seems like you are getting at 2 distinct issues:

  • Using allOf to tack on a description to an existing model
    • I think this would be do-able, but would need to think what the code would look like. Something like if we encounter an allOf and one element is a reference and the other(s) are inlined schemas that only have a description and/or example, we drop the inlined schemas and pull the reference up as if it was used directly
  • Adding additionalProperties support to allOf

I think the first point is less critical - we can comment out the description part for now, use the $ref without the allOf, and circle back to this before we start using the specs for documentation.

Either way, we should keep this PR's scope as-is and follow up in a separate PR for any more changes.

@packyg
Copy link

packyg commented Jan 14, 2021

After we merge this, we may want to do some massaging of the git history and squash this into the original allOf commit so there's just one commit for this feature, making it easier to keep the fork in sync with upstream.

Or we could do that next time we rebase from upstream and cut a main-v.0.7.4 (or whatever)

assert all(not p.required for p in model.optional_properties)

key_property = model.optional_properties[0]
assert sorted(p.name for p in key_property.required_properties) == ["DateTime", "Float", "String"]
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently fails with

>       assert sorted(p.name for p in key_property.required_properties) == ["DateTime", "Float", "String"]
E       AssertionError: assert ['Float'] == ['DateTime', ...at', 'String']
E         At index 0 diff: 'Float' != 'DateTime'
E         Right contains 2 more items, first extra item: 'Float'
E         Use -v to get the full diff

I can't figure out why.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@forest-benchling I think this is because you only resolved references on the parent model, not on the key_property.

This should work if you do

Suggested change
assert sorted(p.name for p in key_property.required_properties) == ["DateTime", "Float", "String"]
assert isinstance(key_property, ModelProperty)
key_property.resolve_references(components, schemas_holder)
assert sorted(p.name for p in key_property.required_properties) == ["DateTime", "Float", "String"]

When you actually run the code generator, all models are resolved in build_schemas but we don't use that code path here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wooo thanks @packyg

@bowenwr bowenwr self-requested a review January 14, 2021 16:11
@@ -268,9 +268,6 @@ def build_model_property(

for key, value in all_props.items():
prop_required = key in required_set
if not isinstance(value, oai.Reference) and value.allOf:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm.. I remember this being important for some reason..
I think removing this might inlines all references rather than referencing them as objects.
You might have more context at this point though.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dtkav Can you clarify what you mean that it might inline all references?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generated the benchling client with both branches, and I think this is actually fine.
I was just trying (but failing) to remember the context around the "resolved later" comment.

It look like your PR generates 40 additional models which seem to correspond to actual missing functionality. 👍

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a refactor upstream which I adapted the initial allOf implementation for. I think I shouldn't have included this there.
In that refactor, he changed the way property_from_data works such that it resolves references to models it has already built instead of returning a RefProperty that gets resolved later.

Copy link

@dtkav dtkav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@forest-benchling forest-benchling merged commit 0b9a99a into main-v.0.7.3 Jan 14, 2021
@forest-benchling forest-benchling deleted the forest-allof-2 branch January 14, 2021 23:53
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.

4 participants