Skip to content

Bug when a schema has a nullable AllOf ref #20

Closed

Description

@knivets

Hey! I have a weird issue that happens with refs that are nullable. I have the following schema:

"GroupWithRecentMessage": {
                "type": "object",
                "properties": {
                    "uid": {
                        "type": "string",
                        "format": "uuid",
                        "readOnly": true
                    },
                    "recent_message": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/Message"
                            }
                        ],
                        "readOnly": true,
                        "nullable": true
                    }
                }
            }

Essentially what I try to express is that recent_message can be either null or an object of type Message.

When I run a validator it raises an exception:

openapi_core.unmarshalling.schemas.exceptions.InvalidSchemaValue: Value {
'results': [{'uid': '018a8f79-5098-478b-a77e-2adb685a1ea5', 'recent_message': None}]
} not valid for schema of type object: (<ValidationError: 'None for not nullable'>

After debugging the issue I have been able to find the root cause. I think the main reason why it happens is that openapi-schema-validator adds a "nullable": false to each ref. So the actual schema that ends being used is the following:

{'nullable': False,
 'properties': {'recent_message': {'allOf': [{'$ref': '#/components/schemas/Message',
                                              'nullable': False, # <---- this is what triggers the wrong error
                                              'x-scope': ['',
                                                          '#/components/schemas/GroupWithRecentMessage']}],
                                   'nullable': True,
                                   'readOnly': True},
                'uid': {'format': 'uuid',
                        'nullable': False,
                        'readOnly': True,
                        'type': 'string'}},
 'required': ['recent_message',
              'uid'],
 'type': 'object'}

How can I fix this issue?

Thanks!

Activity

  1. ngnpope commented on Oct 11, 2021

    @ngnpope

    I have also bumped into this issue. I believe that these lines are incorrect and should be removed:

    https://github.com/p1c2u/openapi-schema-validator/blob/ab7222af0597ebc672f93cc7f7e089897aba1bec/openapi_schema_validator/validators.py#L68-L72

    This is for a number of reasons, all of which have affected me:

    • OpenAPI 3.0.x should ignore siblings of $ref (1)
    • OpenAPI 3.0.x needs special hacks for nullable with enum (1, 2, 3)
    • This breaks nullable set to True alongside allOf/anyOf/oneOf. (This is your case @knivets.)

    The comment # append defaults to trigger validator (i.e. nullable) doesn't seem to make sense as validation seems to work perfectly fine without it. Perhaps @p1c2u can shed some light on why this is/was necessary?

    This shouldn't be an issue with OpenAPI 3.1.x when it is supported as null is allowed as a type.

    I have used the following workaround which may help:

    from openapi_core.unmarshalling.schemas.enums import UnmarshalContext
    from openapi_core.unmarshalling.schemas.factories import SchemaUnmarshallersFactory
    from openapi_core.validation.response.validators import ResponseValidator
    from openapi_schema_validator import OAS30Validator
    
    class FixedOAS30Validator(OAS30Validator):
        def iter_errors(self, instance, _schema=None):
            # Use the implementation of .iter_errors() from the parent.
            return super(OAS30Validator, self).iter_errors(instance, _schema)
    
    class FixedSchemaUnmarshallersFactory(SchemaUnmarshallersFactory):
        def get_validator(self, schema):
            kwargs = {"resolver": self.resolver, "format_checker": self.format_checker}
            if self.context is not None:
                kwargs[self.CONTEXT_VALIDATION[self.context]] = True
            with schema.open() as schema_dict:
                return FixedOAS30Validator(schema_dict, **kwargs)
    
    class FixedResponseValidator(ResponseValidator):
        @property
        def schema_unmarshallers_factory(self):
            return FixedSchemaUnmarshallersFactory(
                self.spec.accessor.dereferencer.resolver_manager.resolver,
                self.format_checker,
                self.custom_formatters,
                context=UnmarshalContext.RESPONSE,
            )
  2. claeyswo commented on Feb 8, 2022

    @claeyswo

    We are experiencing the same issue. As the previous comment stated: Perhaps @p1c2u can shed some light on why this is/was necessary?

  3. p1c2u commented on Feb 10, 2022

    @p1c2u
    Collaborator

    That's exactly what comment says to trigger nullable validator.

    The comment # append defaults to trigger validator (i.e. nullable) doesn't seem to make sense as validation seems to work perfectly fine without it.

    It can work fine if you define nullable param explicitly.
    For schema without nullable defined (default is False) if you pass None value it won't raise validation error - which is incorrect. You can try to remove these lines and run tests.

  4. ngnpope commented on Feb 10, 2022

    @ngnpope

    Maybe the issue I was bumping into was related to #24 (solved by #26), but I haven't been able to check.

  5. p1c2u commented on Feb 10, 2022

    @p1c2u
    Collaborator

    Same here, can't reproduce the issue. @claeyswo which jsonschema version you use and do you have any example of the issue?

  6. claeyswo commented on Feb 14, 2022

    @claeyswo

    3.0, but by adding nullable to every item the problem was solved. By switching the nullable om some_id in the example below we get the desired effect.

    schema = {
        "$ref": "#/components/schemas/TestSchema",
        "components": {
            "schemas": {
                "IntegerString": {
                    "anyOf": [
                        {"type": "integer", "nullable": True},
                        {"type": "string", "nullable": True},
                    ],
                    "nullable": True,
                },
                "TestSchema": {
                    "type": "object",
                    "properties": {
                        "some_id": {
                            "$ref":  "#/components/schemas/IntegerString",
                            "nullable": True,
                        }
                    },
                },
            }
        },
    }
  7. ghandic commented on Jun 23, 2022

    @ghandic
    attribute:
        nullable: true
        $ref: '#/components/Example'
    
    attribute:
        oneOf:
            - type: "null"
            - $ref: '#/components/Example'
    

    Neither work, any update on a fix for this?

  8. added a commit that references this issue on Oct 14, 2022
  9. added 3 commits that reference this issue on Jan 5, 2023
    c5c2ac6
    d7261de
    e213079
  10. p1c2u commented on Jan 7, 2023

    @p1c2u
    Collaborator

    There were ambiguities in the semantics of nullable in OAS 3.0.0-2 More about the issue can be found in the following proposal. OAS 3.0.3 clarified the definition so it can be implemented properly. There shouldn't be any major backward compatibility issues.

  11. added a commit that references this issue on Jan 7, 2023
    5ce7d30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions