openapi-core icon indicating copy to clipboard operation
openapi-core copied to clipboard

Errors are less verbose than they need to be

Open stephenfin opened this issue 4 years ago • 2 comments

Thanks for the excellent library. I have a request. I'm currently getting error messages like the following for an invalid schema:

Traceback (most recent call last):
  ...
  File ".../openapi_core/unmarshalling/schemas/unmarshallers.py", line 61, in validate
    value, self.schema.type, schema_errors=errors)
openapi_core.unmarshalling.schemas.exceptions.InvalidSchemaValue: Value [{'id': 1, ...}] not valid for schema of type SchemaType.ARRAY: (<ValidationError: 'None for not nullable'>,)

This isn't very helpful, and it took me a while to figure out what was happening (I was running into https://github.com/OAI/OpenAPI-Specification/issues/1368). I managed it by hacking on openapi-core in my venv and adding the following:

diff --git openapi_core/unmarshalling/schemas/unmarshallers.py openapi_core/unmarshalling/schemas/unmarshallers.py
index b60f9c3..9b4ddea 100644
--- openapi_core/unmarshalling/schemas/unmarshallers.py
+++ openapi_core/unmarshalling/schemas/unmarshallers.py
@@ -54,6 +54,7 @@ class PrimitiveTypeUnmarshaller(object):
             raise InvalidSchemaValue(value, self.schema.type)
 
     def validate(self, value):
+        self.validator.validate(value)
         errors_iter = self.validator.iter_errors(value)
         errors = tuple(errors_iter)
         if errors:

which gave me a far more helpful:

Traceback (most recent call last):
  ...
  File ".../openapi_core/unmarshalling/schemas/unmarshallers.py", line 57, in validate
    self.validator.validate(value)
  File ".../jsonschema/validators.py", line 353, in validate
    raise error
jsonschema.exceptions.ValidationError: None for not nullable

Failed validating 'nullable' in schema['items']['properties']['delegate']['allOf'][0]:
    {'nullable': False,
     'properties': {'email': {'format': 'email',
                              'minLength': 1,
                              'readOnly': True,
                              'title': 'Email address',
                              'type': 'string'},
                    'first_name': {'maxLength': 30,
                                   'readOnly': True,
                                   'title': 'First name',
                                   'type': 'string'},
                    'id': {'readOnly': True,
                           'title': 'ID',
                           'type': 'integer'},
                    'last_name': {'maxLength': 150,
                                  'readOnly': True,
                                  'title': 'Last name',
                                  'type': 'string'},
                    'url': {'format': 'uri',
                            'readOnly': True,
                            'title': 'URL',
                            'type': 'string'},
                    'username': {'maxLength': 150,
                                 'minLength': 1,
                                 'readOnly': True,
                                 'title': 'Username',
                                 'type': 'string'}},
     'type': 'object'}

On instance[0]['delegate']:
    None

I understand that you probably don't want to raise an exception immediately since you'd only see the first error, but would it be possible to opt-in to this behavior, or at least surface up the more verbose error message, assuming jsonschema provides a way to access this?

stephenfin avatar Apr 15 '20 23:04 stephenfin

Yes that's something that can be changed.

p1c2u avatar Apr 23 '20 10:04 p1c2u

I find this code now works (note it uses structlog to log messages):

def _openapi_log_errors(message, function_name, validation_errors):
    """Log all validation errors in a way that can be addressed."""
    for error_validation in validation_errors:
        if len(error_validation.schema_errors) == 0:
            # Catch when error does not have errors in schema_errors property.
            # This should never happen.
            logger.critical(
                event=message,
                value=error_validation.value
            )
        for schema_error in error_validation.schema_errors:
            schema_absolute_path = ".".join(schema_error.absolute_path)
            if len(schema_absolute_path) == 0:
                schema_absolute_path = '.'
            logger.error(
                event=message,
                function=function_name,
                schema_error=schema_error.message,
                schema_cause=schema_error.cause,
                schema_validator=schema_error.validator,
                schema_path=schema_absolute_path,
            )

And here we get the result from our validation:

        if len(request_validate_result.errors) > 0:
            _openapi_log_errors(
                message="openapi_request_validator failed",
                function_name=func.__name__,
                validation_errors=request_validate_result.errors
            )

So after some pokeng around I feel this bug is resolved.

osynge avatar Nov 01 '21 08:11 osynge