Beispiel #1
0
class EncodingObjectType(BaseOpenAPIObjectType[EncodingObject], result_class=EncodingObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'contentType': CommaDelimitedArrayType(
            item_type=t.StringType(), max_items=2, validators=(CONTENT_TYPE_VALIDATOR,)),
        'headers': t.MapType[HeaderObject](
            t.ReferenceType[HeaderObject](t.LazyType[HeaderObject](lambda: HeaderObjectType()))),
        'style': t.StringType(
            enum=(
                PARAMETER_STYLE.FORM,
                PARAMETER_STYLE.SPACE_DELIMITED,
                PARAMETER_STYLE.PIPE_DELIMITED,
                PARAMETER_STYLE.DEEP_OBJECT
            ),
            default=PARAMETER_STYLE.FORM
        ),
        'explode': t.BooleanType(),
        'allowReserved': t.BooleanType(
            enum=[False],
            messages={'enum': "`allowReserved` permanently unsupported"},
            default=False
        ),
        'x-parameterType': t.StringType(enum=PARAMETER_TYPE),
    }
Beispiel #2
0
class XmlObjectType(BaseOpenAPIObjectType[XmlObject], result_class=XmlObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'name': t.StringType(),
        'namespace': t.StringType(),
        'prefix': t.StringType(),
        'attribute': t.BooleanType(default=False),
        'wrapped': t.BooleanType(default=False)
    }
Beispiel #3
0
class HeaderObjectType(BaseParameterObjectType[HeaderObject],
                       result_class=HeaderObject):

    __slots__ = ()

    MESSAGES: ty.ClassVar[t.Messages] = {
        'deprecated': "Header '{0}' is deprecated"
    }

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'style':
        t.StringType(enum=(PARAMETER_STYLE.SIMPLE, ),
                     default=PARAMETER_STYLE.SIMPLE),
        'explode':
        t.BooleanType(default=False)
    }
Beispiel #4
0
class QueryParameterObjectType(NamedParameterObjectType[QueryParameterObject],
                               result_class=QueryParameterObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {  # type: ignore
        'in':
        t.StringType(enum=(PARAMETER_LOCATION.QUERY, )),
        'allowEmptyValue':
        t.BooleanType(default=False),
        'style':
        t.StringType(
            enum=(PARAMETER_STYLE.FORM, PARAMETER_STYLE.SPACE_DELIMITED,
                  PARAMETER_STYLE.PIPE_DELIMITED, PARAMETER_STYLE.DEEP_OBJECT),
            default=PARAMETER_STYLE.FORM)
    }

    REQUIRED: ty.ClassVar[t.Required] = {'in'}
Beispiel #5
0
class PathParameterObjectType(NamedParameterObjectType[PathParameterObject],
                              result_class=PathParameterObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {  # type: ignore
        'in':
        t.StringType(enum=(PARAMETER_LOCATION.PATH, )),
        'required':
        t.BooleanType(
            enum=[True],
            messages={'enum':
                      "For path parameter this property must be True"}),
        'style':
        t.StringType(enum=(PARAMETER_STYLE.SIMPLE, PARAMETER_STYLE.LABEL,
                           PARAMETER_STYLE.MATRIX),
                     default=PARAMETER_STYLE.SIMPLE)
    }

    REQUIRED: ty.ClassVar[t.Required] = {'in', 'required'}
Beispiel #6
0
class RequestBodyObjectType(BaseOpenAPIObjectType[RequestBodyObject], result_class=RequestBodyObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'description': t.StringType(),
        'content': t.MapType[MediaTypeObject](MediaTypeObjectType(), validators=(CONTENT_TYPE_VALIDATOR,)),
        'required': t.BooleanType(default=False)
    }

    REQUIRED: ty.ClassVar[t.Required] = {
        'content'
    }

    def convert(self, value: ty.Any, path: t.Path, *args: ty.Any, **context: ty.Any) -> ty.Optional[RequestBodyObject]:
        result: ty.Optional[RequestBodyObject] = super(RequestBodyObjectType, self).convert(value, path, **context)

        if result is None:
            return None

        result.path = path
        return result
Beispiel #7
0
class BaseParameterObjectType(BaseOpenAPIObjectType[T_base]):

    __slots__ = ()

    MESSAGES: ty.ClassVar[t.Messages] = {
        'deprecated':
        "Parameter '{0}' is deprecated",
        'mutually_exclusive_schema_content_keywords':
        "The keywords `schema` and `content` are mutually exclusive"
    }

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'description':
        t.StringType(),
        'required':
        t.BooleanType(default=False),
        'deprecated':
        t.BooleanType(default=False),
        'style':
        t.StringType(enum=PARAMETER_STYLE),
        'explode':
        t.BooleanType(),
        'allowReserved':
        t.BooleanType(
            enum=[False],
            messages={'enum': "`allowReserved` permanently unsupported"},
            default=False),
        'schema':
        t.ReferenceType[SchemaObject](SchemaObjectType()),
        'example':
        t.AnyType(),
        'examples':
        t.MapType[ExampleObject](t.ReferenceType[ExampleObject](
            ExampleObjectType())),
        'content':
        t.MapType['MediaTypeObject'](value_type=t.LazyType['MediaTypeObject'](
            lambda: MediaTypeObjectType()),
                                     min_values=1,
                                     max_values=1,
                                     validators=(CONTENT_TYPE_VALIDATOR, )),
        'x-parameterType':
        t.StringType(enum=PARAMETER_TYPE),
    }

    def convert(self, value: ty.Any, path: t.Path, *args: ty.Any,
                **context: ty.Any) -> ty.Optional[T_base]:
        result: ty.Optional[T_base] = super(BaseParameterObjectType,
                                            self).convert(
                                                value, path, **context)

        if result is None:
            return result

        if result.deprecated:
            warnings.warn(self.messages['deprecated'].format(path),
                          DeprecationWarning)

        result.path = path
        return result

    def validate_mutually_exclusive_schema_content_keywords(
            self, value: T_base, *args: ty.Any,
            **context: ty.Any) -> t.ValidationResult:
        if 'schema' in value and 'content' in value:
            return self.messages['mutually_exclusive_schema_content_keywords']

        return None
Beispiel #8
0
class SchemaObjectType(BaseOpenAPIObjectType[SchemaObject],
                       result_class=SchemaObject):

    __slots__ = ()

    MESSAGES: ty.ClassVar[t.Messages] = {
        'ambiguous_type':
        "The schema type is ambiguous",
        'deprecated':
        "The schema '{0}' is deprecated",
        'format':
        "Format is applicable only for primitive types",
        'items_required_for_type_array':
        "`items` must be specified for array type",
        'invalid_type_for_minimum':
        "`minimum` can only be used for number types",
        'invalid_type_for_maximum':
        "`maximum` can only be used for number types",
        'must_be_greater_than_minimum':
        "The value of `maximum` must be greater than or equal to the value of `minimum`",
        'exclusive_minimum_required_minimum':
        "When `exclusiveMinimum` is set, `minimum` is required",
        'exclusive_maximum_required_maximum':
        "When `exclusiveMaximum` is set, `maximum` is required",
        'invalid_type_for_multiple_of':
        "`multipleOf` can only be used for number types",
        'invalid_type_for_min_length':
        "`minLength` can only be used for string types",
        'invalid_type_for_max_length':
        "`maxLength` can only be used for string types",
        'must_be_greater_than_min_length':
        "The value of `maxLength` must be greater than or equal to the `minLength` value",
        'invalid_type_for_min_items':
        "`minItems` can only be used for array types",
        'invalid_type_for_max_items':
        "`maxItems` can only be used for array types",
        'must_be_greater_than_min_items':
        "The value of `maxItems` must be greater than or equal to the value of `minItems`",
        'invalid_type_for_unique_items':
        "`uniqueItems` can only be used for array types",
        'invalid_type_for_properties':
        "`properties` can only be used for object types",
        'invalid_type_for_additional_properties':
        "`additionalProperties` can only be used for object types",
        'invalid_type_for_required':
        "`required` can only be used for object types",
        'invalid_type_for_min_properties':
        "`minProperties` can only be used for object types",
        'invalid_type_for_max_properties':
        "`maxProperties` can only be used for object types",
        'must_be_greater_than_min_properties':
        "The value of `maxProperties` must be greater than or equal to `minProperties`",
        'improperly_discriminator_usage':
        "The `discriminator` can only be used with the keywords `anyOf` or `oneOf`",
        'read_only_and_write_only_are_mutually_exclusive':
        "`readOnly` and `writeOnly` are mutually exclusive and cannot be set simultaneously",
        'improperly_x_merge_usage':
        "The types of all subschemas in `allOf` must be the same when `x-merge` is true",
    }

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'type':
        t.StringType(enum=SCHEMA_TYPE),
        'format':
        t.StringType(),
        'title':
        t.StringType(),
        'description':
        t.StringType(),
        'default':
        t.AnyType(),
        'nullable':
        t.BooleanType(default=False),
        'enum':
        t.ArrayType(t.AnyType(), min_items=1, unique_items=True),
        'readOnly':
        t.BooleanType(default=False),
        'writeOnly':
        t.BooleanType(default=False),
        'xml':
        XmlObjectType(),
        'externalDocs':
        ExternalDocumentationObjectType(),
        'example':
        t.AnyType(),
        'deprecated':
        t.BooleanType(default=False),
        'multipleOf':
        t.NumberType(minimum=0, exclusive_minimum=True),
        'minimum':
        t.NumberType(),
        'maximum':
        t.NumberType(),
        'exclusiveMinimum':
        t.BooleanType(default=False),
        'exclusiveMaximum':
        t.BooleanType(default=False),
        'minLength':
        t.IntegerType(minimum=0, default=0),
        'maxLength':
        t.IntegerType(minimum=0),
        'pattern':
        t.RegexType(t.StringType(min_length=1)),
        'discriminator':
        t.LazyType[DiscriminatorObject](lambda: DiscriminatorObjectType()),
        'allOf':
        t.ArrayType[SchemaObject](t.ReferenceType[SchemaObject](
            t.LazyType[SchemaObject](lambda: SchemaObjectType())),
                                  min_items=1),
        'anyOf':
        t.ArrayType[SchemaObject](ReferenceSaver(t.ReferenceType[SchemaObject](
            t.LazyType[SchemaObject](lambda: SchemaObjectType()))),
                                  min_items=1),
        'oneOf':
        t.ArrayType[SchemaObject](ReferenceSaver(t.ReferenceType[SchemaObject](
            t.LazyType[SchemaObject](lambda: SchemaObjectType()))),
                                  min_items=1),
        'not':
        t.ArrayType[SchemaObject](t.ReferenceType[SchemaObject](
            t.LazyType[SchemaObject](lambda: SchemaObjectType())),
                                  min_items=1),
        'items':
        t.ReferenceType[SchemaObject](
            t.LazyType[SchemaObject](lambda: SchemaObjectType())),
        'minItems':
        t.IntegerType(minimum=0, default=0),
        'maxItems':
        t.IntegerType(minimum=0),
        'uniqueItems':
        t.BooleanType(default=False),
        'required':
        t.ArrayType[str](t.StringType(), min_items=1, unique_items=True),
        'properties':
        t.MapType[SchemaObject](t.ReferenceType[SchemaObject](
            t.LazyType[SchemaObject](lambda: SchemaObjectType()))),
        'minProperties':
        t.IntegerType(minimum=0, default=0),
        'maxProperties':
        t.IntegerType(minimum=0),
        'additionalProperties':
        t.OneOfType([
            t.BooleanType(), t.ReferenceType[SchemaObject](
                t.LazyType[SchemaObject](lambda: SchemaObjectType()))
        ],
                    default=True),
        'x-patternProperties':
        RegexMapType[SchemaObject](t.MapType[SchemaObject](
            t.LazyType[SchemaObject](lambda: SchemaObjectType()))),
        'x-merge':
        t.BooleanType(default=False),
    }

    def _convert(self, value: ty.Any, path: t.Path, *args: ty.Any,
                 **context: ty.Any) -> SchemaObject:
        result: SchemaObject = super(SchemaObjectType,
                                     self)._convert(value, path, **context)

        if result.type is None:
            inferred = []
            for schema_type, keywords in TYPE_SPECIFIC_KEYWORDS.items():
                if any(keyword for keyword in keywords if keyword in value):
                    inferred.append(schema_type)

            if len(inferred) > 1:
                raise t.SchemaError(
                    t.Error(path, self.messages['ambiguous_type']))

            elif inferred:
                result.properties['type'] = inferred[0]

        if result.deprecated:
            warnings.warn(self.messages['deprecated'].format(path),
                          DeprecationWarning)

        if result.required:
            result.properties['required'] = set(result.required)

        result.path = path
        return result

    def validate_format(self, value: SchemaObject, *args: ty.Any,
                        **context: ty.Any) -> t.ValidationResult:
        if value.format is not None and value.type not in PRIMITIVE_SCHEMA_TYPES:
            return self.messages['format']

        return None

    def validate_items(self, value: SchemaObject, *args: ty.Any,
                       **context: ty.Any) -> t.ValidationResult:
        if value.type == SCHEMA_TYPE.ARRAY and value.items_ is None:
            return self.messages['items_required_for_type_array']

        return None

    def validate_discriminator(self, value: SchemaObject, *args: ty.Any,
                               **context: ty.Any) -> t.ValidationResult:
        if value.discriminator is None:
            return None

        if value.all_of is not None or value.not_ is not None:
            return self.messages['improperly_discriminator_usage']

        return None

    def validate_minimum(self, value: SchemaObject, *args: ty.Any,
                         **context: ty.Any) -> t.ValidationResult:
        if value.minimum is None:
            return None

        if value.type not in (SCHEMA_TYPE.NUMBER, SCHEMA_TYPE.INTEGER):
            return self.messages['invalid_type_for_minimum']

        return None

    def validate_maximum(self, value: SchemaObject, *args: ty.Any,
                         **context: ty.Any) -> t.ValidationResult:
        if value.maximum is None:
            return None

        if value.type not in (SCHEMA_TYPE.NUMBER, SCHEMA_TYPE.INTEGER):
            return self.messages['invalid_type_for_maximum']

        if value.minimum is not None and value.maximum < value.minimum:
            return self.messages['must_be_greater_than_minimum']

        return None

    def validate_exclusive_minimum(self, value: SchemaObject,
                                   original: ty.Mapping, *args: ty.Any,
                                   **context: ty.Any) -> t.ValidationResult:
        if 'exclusiveMinimum' in original and value.minimum is None:
            return self.messages['exclusive_minimum_required_minimum']

        return None

    def validate_exclusive_maximum(self, value: SchemaObject,
                                   original: ty.Mapping,
                                   **context: ty.Any) -> t.ValidationResult:
        if 'exclusiveMaximum' in original and value.maximum is None:
            return self.messages['exclusive_maximum_required_maximum']

        return None

    def validate_multiple_of(self, value: SchemaObject, *args: ty.Any,
                             **context: ty.Any) -> t.ValidationResult:
        if value.multiple_of is not None and value.type not in (
                SCHEMA_TYPE.NUMBER, SCHEMA_TYPE.INTEGER):
            return self.messages['invalid_type_for_multiple_of']

        return None

    def validate_min_length(self, value: SchemaObject, original: ty.Mapping,
                            *args: ty.Any,
                            **context: ty.Any) -> t.ValidationResult:
        if 'minLength' in original and value.type != SCHEMA_TYPE.STRING:
            return self.messages['invalid_type_for_min_length']

        return None

    def validate_max_length(self, value: SchemaObject, *args: ty.Any,
                            **context: ty.Any) -> t.ValidationResult:
        if value.max_length is None:
            return None

        if value.type != SCHEMA_TYPE.STRING:
            return self.messages['invalid_type_for_max_length']

        if value.max_length < value.min_length:
            return self.messages['must_be_greater_than_min_length']

        return None

    def validate_min_items(self, value: SchemaObject, original: ty.Mapping,
                           *args: ty.Any,
                           **context: ty.Any) -> t.ValidationResult:
        if 'minItems' in original and value.type != SCHEMA_TYPE.ARRAY:
            return self.messages['invalid_type_for_min_items']

        return None

    def validate_max_items(self, value: SchemaObject, *args: ty.Any,
                           **context: ty.Any) -> t.ValidationResult:
        if value.max_items is None:
            return None

        if value.type != SCHEMA_TYPE.ARRAY:
            return self.messages['invalid_type_for_max_items']

        if value.max_items < value.min_items:
            return self.messages['must_be_greater_than_min_items']

        return None

    def validate_unique_items(self, value: SchemaObject, original: ty.Mapping,
                              *args: ty.Any,
                              **context: ty.Any) -> t.ValidationResult:
        if 'uniqueItems' in original and value.type != SCHEMA_TYPE.ARRAY:
            return self.messages['invalid_type_for_unique_items']

        return None

    def validate_properties(self, value: SchemaObject, original: ty.Mapping,
                            *args: ty.Any,
                            **context: ty.Any) -> t.ValidationResult:
        if 'properties' in original and value.type != SCHEMA_TYPE.OBJECT:
            return self.messages['invalid_type_for_properties']

        return None

    def validate_additional_properties(
            self, value: SchemaObject, original: ty.Mapping, *args: ty.Any,
            **context: ty.Any) -> t.ValidationResult:
        if 'additionalProperties' in original and value.type != SCHEMA_TYPE.OBJECT:
            return self.messages['invalid_type_for_additional_properties']

        return None

    def validate_required(self, value: SchemaObject, original: ty.Mapping,
                          *args: ty.Any,
                          **context: ty.Any) -> t.ValidationResult:
        if 'required' in original and value.type != SCHEMA_TYPE.OBJECT:
            return self.messages['invalid_type_for_required']

        return None

    def validate_min_properties(self, value: SchemaObject,
                                original: ty.Mapping, *args: ty.Any,
                                **context: ty.Any) -> t.ValidationResult:
        if 'minProperties' in original and value.type != SCHEMA_TYPE.OBJECT:
            return self.messages['invalid_type_for_min_properties']

        return None

    def validate_max_properties(self, value: SchemaObject, *args: ty.Any,
                                **context: ty.Any) -> t.ValidationResult:
        if value.max_properties is None:
            return None

        if value.type != SCHEMA_TYPE.OBJECT:
            return self.messages['invalid_type_for_max_properties']

        if value.max_properties < value.min_properties:
            return self.messages['must_be_greater_than_min_properties']

        return None

    def validate_read_only_write_only(self, value: SchemaObject, *args: ty.Any,
                                      **context: ty.Any) -> t.ValidationResult:
        if value.read_only and value.write_only:
            return self.messages[
                'read_only_and_write_only_are_mutually_exclusive']

        return None

    def validate_x_merge(self, value: SchemaObject, *args: ty.Any,
                         **context: ty.Any) -> t.ValidationResult:
        if not value.is_mergeable or value.all_of is None:
            return None

        subschema_types = [subschema.type for subschema in value.all_of]
        if not are_all_items_equal(
                subschema_types) or subschema_types[0] is None:
            return self.messages['improperly_x_merge_usage']

        return None
Beispiel #9
0
def _generate_boolean_type(schema: o.SchemaObject) -> t.BooleanType:
    return t.BooleanType(nullable=schema.nullable,
                         default=schema.default,
                         enum=schema.enum)
Beispiel #10
0
class OperationObjectType(BaseOpenAPIObjectType[OperationObject],
                          result_class=OperationObject):

    __slots__ = ()

    MESSAGES: ty.ClassVar[t.Messages] = {
        'deprecated': "Operation '{0}' is deprecated",
        'operation_id': "Duplicate of operation ID '{0}' was found at '{1}'"
    }

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'tags':
        t.ArrayType[str](t.StringType()),
        'summary':
        t.StringType(),
        'description':
        t.StringType(),
        'externalDocs':
        ExternalDocumentationObjectType(),
        'operationId':
        t.StringType(),
        'parameters':
        t.ArrayType[AnyParameterObject](
            t.ReferenceType[AnyParameterObject](ParameterPolymorphic),
            unique_items=True,
            unique_item_properties=['in', 'name']),
        'requestBody':
        t.ReferenceType[RequestBodyObject](RequestBodyObjectType()),
        'responses':
        ResponsesObjectType(),
        'callbacks':
        t.MapType[CallbackObject](t.ReferenceType[CallbackObject](
            CallbackObjectType())),
        'deprecated':
        t.BooleanType(default=False),
        'security':
        t.ArrayType[SecurityRequirementObject](
            SecurityRequirementObjectType(min_properties=1)),
        'servers':
        t.ArrayType[ServerObject](ServerObjectType())
    }

    REQUIRED: ty.ClassVar[t.Required] = {'responses'}

    def convert(self,
                value: ty.Any,
                path: t.Path,
                *args: ty.Any,
                operation_ids: ty.Optional[OperationIds] = None,
                **context: ty.Any) -> ty.Optional[OperationObject]:
        assert operation_ids is not None

        result: ty.Optional[OperationObject] = super(
            OperationObjectType, self).convert(value, path, **context)

        if result is None:
            return None

        if result.deprecated:
            warnings.warn(self.messages['deprecated'].format(path),
                          DeprecationWarning)

        operation_id = result.operation_id
        if operation_id is not None:
            if operation_id in operation_ids:
                raise t.SchemaError(
                    t.Error(
                        path / 'operationId',
                        self.messages['operation_id'].format(
                            operation_id, operation_ids[operation_id])))

            operation_ids[operation_id] = path / 'operationId'

        return result