Skip to content

Commit

Permalink
feat(validator): extract validator abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
sleistner committed Jun 21, 2019
1 parent 4fa8dbb commit bfc697e
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 95 deletions.
5 changes: 3 additions & 2 deletions lambda_handlers/validators/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .jsonschema import json_schema_validator # noqa
from .marshmallow import Validator as marshmallow # noqa
from .validator import Validator # noqa
from .jsonschema_validator import JSONSchemaValidator as jsonschema # noqa
from .marshmallow_validator import MarshmallowValidator as marshmallow # noqa
83 changes: 0 additions & 83 deletions lambda_handlers/validators/jsonschema.py

This file was deleted.

20 changes: 20 additions & 0 deletions lambda_handlers/validators/jsonschema_validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from typing import Any, Dict, List, Tuple
from collections import defaultdict

from jsonschema import Draft7Validator
from lambda_handlers.validators import Validator


class JSONSchemaValidator(Validator):

def validate(self, instance, schema) -> Tuple[Any, List[Any]]:
validator = Draft7Validator(schema)
errors = sorted(validator.iter_errors(instance), key=lambda error: error.path)
return instance, errors

def format_error(self, errors) -> List[Dict[str, Any]]:
path_errors: Dict[str, List[str]] = defaultdict(list)
for error in errors:
path_errors[error.path.pop()].append(error.message)

return [{path: messages} for path, messages in path_errors.items()]
17 changes: 17 additions & 0 deletions lambda_handlers/validators/marshmallow_validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import Any, Dict, List, Tuple

from marshmallow import ValidationError
from lambda_handlers.validators import Validator

NO_FIELD_NAME = 'errors'


class MarshmallowValidator(Validator):

def validate(self, instance, schema) -> Tuple[Any, List[Any]]:
result = schema.load(instance)
return result.data, result.errors

def format_error(self, errors) -> List[Dict[str, Any]]:
exception = ValidationError(errors)
return exception.normalized_messages(no_field_name=NO_FIELD_NAME).get(NO_FIELD_NAME, None)
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import marshmallow
from abc import ABC, abstractmethod
from typing import Any, Dict, List, Tuple

from lambda_handlers.errors import ValidationError


class Validator:
class Validator(ABC):

def __init__(self, path=None, query=None, body=None):
self._path_parameters_schema = path
self._query_string_parameters_schema = query
self._body_schema = body

def __call__(self, event, context):
errors = []
cumulative_errors = []

def _validate(key, schema):
result = schema.load(event.get(key, {}))
if result.errors:
errors.append(result.errors)
data, errors = self.on_validate(event.get(key, {}), schema)
if errors:
cumulative_errors.append(errors)
elif key in event:
event[key].update(result.data)
event[key].update(data)

if self._path_parameters_schema:
_validate('pathParameters', self._path_parameters_schema())
Expand All @@ -29,7 +30,14 @@ def _validate(key, schema):
if self._body_schema:
_validate('body', self._body_schema())

if errors:
exception = marshmallow.ValidationError(errors)
description = exception.normalized_messages(no_field_name='errors')['errors']
if cumulative_errors:
description = self.format_errors(cumulative_errors)
raise ValidationError(description)

@abstractmethod
def validate(self, instance: Any, schema: Any) -> Tuple[Any, List[Any]]:
pass

@abstractmethod
def format_errors(self, errors: List[Any]) -> List[Dict[str, Any]]:
pass

0 comments on commit bfc697e

Please sign in to comment.