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

feat: Implement discriminators knowledge in code generation BNCH-30443 #87

Merged
merged 9 commits into from
Sep 25, 2021

Conversation

GitOnUp
Copy link

@GitOnUp GitOnUp commented Sep 20, 2021

The bulk of logic for this change is in https://github.com/benchling/benchling-sdk/pull/180.

This change adds knowledge of type mappings in the union property type for usage downstream.

@GitOnUp GitOnUp requested a review from packyg September 20, 2021 16:38
Comment on lines 12 to 22
{% if property.discriminator_property != None %}
discriminator_value: str = data.get("{{property.discriminator_property}}")
if discriminator_value is not None:
{% for discriminated_by, inner_property in property.discriminator_mappings.items() %}
if discriminator_value == "{{discriminated_by}}":
{% from "property_templates/" + inner_property.template import construct %}
{{ construct(inner_property, "data", initial_value="UNSET") | indent(8) }}
return {{ property.python_name }}
{% endfor %}

{% endif %}
Copy link
Author

Choose a reason for hiding this comment

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

I originally wrote this to fall back nicely to the brute force approach if a discriminator is accidentally missing, but I think it's probably more correct to have that be enforced by the openapi linter and raise an error at the end of this if chain here if we haven't returned.

Copy link
Author

Choose a reason for hiding this comment

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

Likewise, William had pointed out that we might want to have a default value for types for forward compatibility, but the existing mechanism doesn't do that, so we have to decide whether to add it for non-discriminated cases as well. I'm still thinking this through a little bit, but given that the UnionProperty type can also be used for "simple" types like str vs int (which can never be discriminated, if I'm reading the OpenAPI docs correctly), best case is the logic gets strange based on the types that can be unioned here.

I'm tempted to take a stand and say "if you want to support forward compatibility, use discriminators" and implement the "Unknown" case for that only.

Copy link

Choose a reason for hiding this comment

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

I'll need to return to this tomorrow after I've thought about it some, too.

But I think forward compatibility is the key consideration here. And the problems get only more acute the deeper a prop is embedded in the model hierarchy.

We could enforce your rule on discriminators for forward compatibility internally, but that might be a hard sell if we're trying to upstream this.

Copy link

Choose a reason for hiding this comment

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

best case is the logic gets strange based on the types that can be unioned here.

Can't we detect this from the value we get (as opposed to the spec)? Like if it's a primitive (i.e. just not a dict or list when serialized), then we don't do any deserialization - the raw value is the deserialzed value. If it is a dict, then we do the existing brute force approach (since in this case it'd be models union'ed with primitives for some reason and we can discriminate)

@@ -9,6 +9,17 @@ def _parse_{{ property.python_name }}(data: {{ property.get_type_string(json=Tru
if isinstance(data, Unset):
return data
{% endif %}
{% if property.discriminator_property != None %}
Copy link
Author

Choose a reason for hiding this comment

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

This results in a chain that looks like this for RegisteredEntitiesList, which has a discriminator:

                discriminator_value: str = data.get("entityType")
                if discriminator_value is not None:
                    if discriminator_value == "dna_sequence":
                        entities_item = DnaSequence.from_dict(data)

                        return entities_item
                    if discriminator_value == "custom_entity":
                        entities_item = CustomEntity.from_dict(data)

                        return entities_item
                    if discriminator_value == "aa_sequence":
                        entities_item = AaSequence.from_dict(data)

                        return entities_item
                    if discriminator_value == "mixture":
                        entities_item = Mixture.from_dict(data)

                        return entities_item
                    if discriminator_value == "dna_oligo":
                        entities_item = DnaOligo.from_dict(data)

                        return entities_item
                    if discriminator_value == "rna_oligo":
                        entities_item = RnaOligo.from_dict(data)

                        return entities_item

@GitOnUp GitOnUp marked this pull request as ready for review September 24, 2021 16:37
@GitOnUp GitOnUp changed the title Discriminator support feat: Implement discriminators knowledge in code generation BNCH-30443 Sep 24, 2021
@@ -423,13 +427,22 @@ def build_union_property(
*, data: oai.Schema, name: str, required: bool, schemas: Schemas, parent_name: str
) -> Tuple[Union[UnionProperty, PropertyError], Schemas]:
sub_properties: List[Property] = []
inverted_mappings = {}
for k, v in (data.discriminator.mapping if data.discriminator else {}).items():
inverted_mappings[Reference.from_ref(v).class_name] = k
Copy link
Author

Choose a reason for hiding this comment

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

AD: Check for existence, error if so, but we might want to support that case in the future (several mapped names can reference the same type, e.g. for deprecation purposes)

…g/openapi-python-client into george/discriminators
@GitOnUp GitOnUp merged commit 6c40881 into benchling-sdk-m1-fixes-01282021 Sep 25, 2021
@eli-bl eli-bl deleted the george/discriminators branch November 26, 2024 22:45
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.

2 participants