예제 #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),
    }
예제 #2
0
class BaseSecuritySchemeObjectType(BaseOpenAPIObjectType[T_base]):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'type': t.StringType(enum=SECURITY_SCHEME_TYPE),
        'description': t.StringType()
    }

    REQUIRED: ty.ClassVar[t.Required] = {'type'}
예제 #3
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)
    }
예제 #4
0
class DiscriminatorObjectType(BaseOpenAPIObjectType[DiscriminatorObject],
                              result_class=DiscriminatorObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'propertyName': t.StringType(min_length=1),
        'mapping': t.MapType[str](t.StringType(min_length=1))
    }

    REQUIRED: ty.ClassVar[t.Required] = {'propertyName'}
예제 #5
0
class ExampleObjectType(BaseOpenAPIObjectType[ExampleObject],
                        result_class=ExampleObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'summary': t.StringType(),
        'description': t.StringType(),
        'value': t.AnyType(),
        'externalValue': t.StringType()
    }
예제 #6
0
class TagObjectType(BaseOpenAPIObjectType[TagObject], result_class=TagObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'name': t.StringType(),
        'description': t.StringType(),
        'externalDocs': ExternalDocumentationObjectType()
    }

    REQUIRED: ty.ClassVar[t.Required] = {'name'}
예제 #7
0
class LinkObjectType(BaseOpenAPIObjectType[LinkObject], result_class=LinkObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'operationRef': t.StringType(),
        'operationId': t.StringType(),
        'parameters': t.MapType(t.AnyType()),
        'requestBody': t.AnyType(),
        'description': t.StringType(),
        'server': ServerObjectType()
    }
예제 #8
0
class HttpSecuritySchemeObjectType(
        BaseSecuritySchemeObjectType[HttpSecuritySchemeObject],
        result_class=HttpSecuritySchemeObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'scheme': t.StringType(),
        'bearerFormat': t.StringType()
    }

    REQUIRED: ty.ClassVar[t.Required] = {'scheme'}
예제 #9
0
class ServerVariableObjectType(BaseOpenAPIObjectType[ServerVariableObject],
                               result_class=ServerVariableObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'enum': t.ArrayType[str](t.StringType()),
        'default': t.StringType(),
        'description': t.StringType()
    }

    REQUIRED: ty.ClassVar[t.Required] = {'default'}
예제 #10
0
class ServerObjectType(BaseOpenAPIObjectType[ServerObject], result_class=ServerObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'url': t.StringType(),
        'description': t.StringType(),
        'variables': t.MapType[ServerVariableObject](ServerVariableObjectType())
    }

    REQUIRED: ty.ClassVar[t.Required] = {
        'url'
    }
예제 #11
0
class InfoObjectType(BaseOpenAPIObjectType[InfoObject],
                     result_class=InfoObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'title': t.StringType(),
        'description': t.StringType(),
        'termsOfService': t.URIType(),
        'contact': ContactObjectType(),
        'license': LicenseObjectType(),
        'version': t.StringType()
    }

    REQUIRED: ty.ClassVar[t.Required] = {'title', 'version'}
예제 #12
0
class HeaderParameterObjectType(
        NamedParameterObjectType[HeaderParameterObject],
        result_class=HeaderParameterObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {  # type: ignore
        'in':
        t.StringType(enum=(PARAMETER_LOCATION.HEADER, )),
        'style':
        t.StringType(enum=(PARAMETER_STYLE.SIMPLE, ),
                     default=PARAMETER_STYLE.SIMPLE)
    }

    REQUIRED: ty.ClassVar[t.Required] = {'in'}
예제 #13
0
class APIKeySecuritySchemeObjectType(
        BaseSecuritySchemeObjectType[APIKeySecuritySchemeObject],
        result_class=APIKeySecuritySchemeObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'name':
        t.StringType(),
        'in':
        t.StringType(enum=(PARAMETER_LOCATION.QUERY, PARAMETER_LOCATION.HEADER,
                           PARAMETER_LOCATION.COOKIE))
    }

    REQUIRED: ty.ClassVar[t.Required] = {'name', 'in'}
예제 #14
0
class CookieParameterObjectType(
        NamedParameterObjectType[CookieParameterObject],
        result_class=CookieParameterObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {  # type: ignore
        'in':
        t.StringType(enum=(PARAMETER_LOCATION.COOKIE, )),
        'style':
        t.StringType(enum=(PARAMETER_STYLE.FORM, ),
                     default=PARAMETER_STYLE.FORM)
    }

    REQUIRED: ty.ClassVar[t.Required] = {'in'}
예제 #15
0
class OAuthFlowObjectType(BaseOpenAPIObjectType[OAuthFlowObject],
                          result_class=OAuthFlowObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'authorizationUrl': t.StringType(),
        'tokenUrl': t.StringType(),
        'refreshUrl': t.StringType(),
        'scopes': t.MapType[str](t.StringType())
    }

    REQUIRED: ty.ClassVar[t.Required] = {
        'authorizationUrl', 'tokenUrl', 'scopes'
    }
예제 #16
0
def _generate_string_type(schema: o.SchemaObject) -> t.StringType:
    return t.StringType(nullable=schema.nullable,
                        default=schema.default,
                        min_length=schema.min_length,
                        max_length=schema.max_length,
                        enum=schema.enum,
                        pattern=schema.pattern)
예제 #17
0
class ResponseObjectType(BaseOpenAPIObjectType[ResponseObject],
                         result_class=ResponseObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'description':
        t.StringType(),
        'headers':
        t.MapType[HeaderObject](t.ReferenceType[HeaderObject](
            HeaderObjectType())),
        'content':
        t.MapType[MediaTypeObject](MediaTypeObjectType(),
                                   validators=(CONTENT_TYPE_VALIDATOR, )),
        'links':
        t.MapType[LinkObject](t.ReferenceType[LinkObject](LinkObjectType()))
    }

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

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

        if result is None:
            return None

        result.path = path
        return result
예제 #18
0
class NamedParameterObjectType(BaseParameterObjectType[T_named]):

    PROPERTIES: ty.ClassVar[t.Properties] = {  # type: ignore
        'name': t.StringType()
    }

    REQUIRED: ty.ClassVar[t.Required] = {'name'}
예제 #19
0
    def _generate_part(
        self,
        schema: o.SchemaObject,
        property_encoding: ty.Optional[o.EncodingObject] = None
    ) -> PartAdapter:
        properties: ty.MutableMapping[str, t.AbstractConvertible] = {}
        allowed_content_types: ty.Optional[ty.Iterable[str]] = None
        headers = None
        if property_encoding is not None:
            allowed_content_types = property_encoding.content_type
            headers = property_encoding.headers

        default_content_type = schema.default_content_type
        if not allowed_content_types and default_content_type is not None:
            allowed_content_types = (default_content_type, )

        properties['contentType'] = t.StringType(
            pattern=o.CONTENT_TYPE_PATTERN,
            validators=(AllowedContentTypeValidator(allowed_content_types), ),
            messages={'pattern': "Invalid content type"})

        properties['headers'] = self.headers_factory.generate(headers or {})
        properties['content'] = self.type_factory.generate(schema)

        return PartAdapter(t.ObjectType(properties=properties,
                                        required={'headers', 'content'}),
                           default_content_type=default_content_type)
예제 #20
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'}
예제 #21
0
class ContactObjectType(BaseOpenAPIObjectType[ContactObject],
                        result_class=ContactObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'name': t.StringType(),
        'url': t.URIType(),
        'email': t.EmailType()
    }
예제 #22
0
class OpenIdConnectSecuritySchemeObjectType(
        BaseSecuritySchemeObjectType[OpenIdConnectSecuritySchemeObject],
        result_class=OpenIdConnectSecuritySchemeObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'openIdConnectUrl': t.StringType()
    }

    REQUIRED: ty.ClassVar[t.Required] = {'openIdConnectUrl'}
예제 #23
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'}
예제 #24
0
class LicenseObjectType(BaseOpenAPIObjectType[LicenseObject],
                        result_class=LicenseObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'name': t.StringType(),
        'url': t.URIType()
    }

    REQUIRED: ty.ClassVar[t.Required] = {'name'}
예제 #25
0
class ExternalDocumentationObjectType(
        BaseOpenAPIObjectType[ExternalDocumentationObject], result_class=ExternalDocumentationObject):

    __slots__ = ()

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'description': t.StringType(),
        'url': t.URIType()
    }

    REQUIRED: ty.ClassVar[t.Required] = {
        'url'
    }
예제 #26
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)
    }
예제 #27
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
예제 #28
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
예제 #29
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
예제 #30
0
class OpenAPIObjectType(BaseOpenAPIObjectType[OpenAPIObject],
                        result_class=OpenAPIObject):

    __slots__ = ()

    MESSAGES: ty.ClassVar[t.Messages] = {
        'undefined_security_scheme': "Undefined security scheme '{0}'"
    }

    PROPERTIES: ty.ClassVar[t.Properties] = {
        'openapi':
        t.StringType(pattern=re.compile(r'^3\.0\.[0-2]$'),
                     messages={'pattern': "Unsupported version of OpenAPI"}),
        'info':
        InfoObjectType(),
        'servers':
        t.ArrayType[ServerObject](ServerObjectType()),
        'paths':
        PathsObjectType(),
        'components':
        ComponentsObjectType(),
        'security':
        t.ArrayType[SecurityRequirementObject](
            SecurityRequirementObjectType(min_properties=1)),
        'tags':
        t.ArrayType[TagObject](TagObjectType(),
                               unique_items=True,
                               unique_item_properties=['name']),
        'externalDocs':
        ExternalDocumentationObjectType()
    }

    REQUIRED: ty.ClassVar[t.Required] = {'openapi', 'info', 'paths'}

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

        if result is None:
            return None

        security_schemes: ty.Mapping[str, AnySecuritySchemeObject] = {}
        components: ty.Optional[ComponentsObject] = result.components
        if components is not None and components.security_schemes:
            security_schemes = components.security_schemes

        errors: ty.List[t.Error] = []
        security: ty.Optional[
            ty.Sequence[SecurityRequirementObject]] = result.security
        if security:
            for i, security_requirement in enumerate(security):
                for scheme_name in security_requirement.additional_properties.keys(
                ):
                    if scheme_name not in security_schemes:
                        errors.append(
                            t.Error(
                                path / 'security' / i,
                                self.messages['undefined_security_scheme'].
                                format(scheme_name)))

        paths: PathsObject = result.paths
        for path_, path_item in paths.pattern_properties.items():
            for http_method in HTTP_METHODS:
                if http_method not in path_item:
                    continue

                operation: OperationObject = path_item[http_method]
                security = operation.security
                if security is None:
                    continue
                for i, security_requirement in enumerate(security):
                    for scheme_name in security_requirement.additional_properties.keys(
                    ):
                        if scheme_name not in security_schemes:
                            errors.append(
                                t.Error(
                                    path / 'paths' / path_ / http_method /
                                    'security' / i,
                                    self.messages['undefined_security_scheme'].
                                    format(scheme_name)))

        if errors:
            raise t.SchemaError(*errors)

        return result