| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 | # -*- coding: utf-8 -*-
import typing
from hapic.exception import OutputValidationException
from hapic.exception import ConfigurationException
class RequestParameters(object):
    def __init__(
        self,
        path_parameters,
        query_parameters,
        body_parameters,
        form_parameters,
        header_parameters,
        files_parameters,
    ):
        self.path_parameters = path_parameters
        self.query_parameters = query_parameters
        self.body_parameters = body_parameters
        self.form_parameters = form_parameters
        self.header_parameters = header_parameters
        self.files_parameters = files_parameters
class ProcessValidationError(object):
    def __init__(
        self,
        message: str,
        details: dict,
    ) -> None:
        self.message = message
        self.details = details
class ProcessorInterface(object):
    def __init__(self):
        self._schema = None
    @property
    def schema(self):
        if not self._schema:
            raise ConfigurationException('Schema not set for processor {}'.format(str(self)))
        return self._schema
    @schema.setter
    def schema(self, schema):
        self._schema = schema
    def process(self, value):
        raise NotImplementedError
    def get_validation_error(
        self,
        request_context: RequestParameters,
    ) -> ProcessValidationError:
        raise NotImplementedError
class Processor(ProcessorInterface):
    @classmethod
    def clean_data(cls, data: typing.Any) -> dict:
        # Fixes #22: Schemas make not validation if None is given
        if data is None:
            return {}
        return data
class InputProcessor(Processor):
    pass
class OutputProcessor(Processor):
    pass
class MarshmallowOutputProcessor(OutputProcessor):
    def process(self, data: typing.Any):
        clean_data = self.clean_data(data)
        dump_data = self.schema.dump(clean_data).data
        self.validate(dump_data)
        return dump_data
    def validate(self, data: typing.Any) -> None:
        clean_data = self.clean_data(data)
        errors = self.schema.load(clean_data).errors
        if errors:
            raise OutputValidationException(
                'Error when validate input: {}'.format(
                    str(errors),
                )
            )
    def get_validation_error(self, data: dict) -> ProcessValidationError:
        clean_data = self.clean_data(data)
        dump_data = self.schema.dump(clean_data).data
        errors = self.schema.load(dump_data).errors
        return ProcessValidationError(
            message='Validation error of output data',
            details=errors,
        )
class MarshmallowInputProcessor(InputProcessor):
    def process(self, data: dict):
        clean_data = self.clean_data(data)
        unmarshall = self.schema.load(clean_data)
        if unmarshall.errors:
            raise OutputValidationException(
                'Error when validate ouput: {}'.format(
                    str(unmarshall.errors),
                )
            )
        return unmarshall.data
    def get_validation_error(self, data: dict) -> ProcessValidationError:
        clean_data = self.clean_data(data)
        marshmallow_errors = self.schema.load(clean_data).errors
        return ProcessValidationError(
            message='Validation error of input data',
            details=marshmallow_errors,
        )
class MarshmallowInputFilesProcessor(MarshmallowInputProcessor):
    def process(self, data: dict):
        clean_data = self.clean_data(data)
        unmarshall = self.schema.load(clean_data)
        additional_errors = self._get_files_errors(unmarshall.data)
        if unmarshall.errors:
            raise OutputValidationException(
                'Error when validate ouput: {}'.format(
                    str(unmarshall.errors),
                )
            )
        if additional_errors:
            raise OutputValidationException(
                'Error when validate ouput: {}'.format(
                    str(additional_errors),
                )
            )
        return unmarshall.data
    def get_validation_error(self, data: dict) -> ProcessValidationError:
        clean_data = self.clean_data(data)
        unmarshall = self.schema.load(clean_data)
        marshmallow_errors = unmarshall.errors
        additional_errors = self._get_files_errors(unmarshall.data)
        if marshmallow_errors:
            return ProcessValidationError(
                message='Validation error of input data',
                details=marshmallow_errors,
            )
        if additional_errors:
            return ProcessValidationError(
                message='Validation error of input data',
                details=additional_errors,
            )
    def _get_files_errors(self, validated_data: dict) -> typing.Dict[str, str]:
        """
        Additional check of data
        :param validated_data: previously validated data by marshmallow schema
        :return: list of error if any
        """
        errors = {}
        for field_name, field in self.schema.fields.items():
            # Actually just check if value not empty
            # TODO BS 20171102: Think about case where test file content is more complicated
            if field.required and (field_name not in validated_data or not validated_data[field_name]):
                errors.setdefault(field_name, []).append('Missing data for required field')
        return errors
 |