Пример #1
0
def resolve(
    components: t.Dict[str, t.Dict[str, t.Any]],
    work_item: t.Dict[str, t.Any],
) -> model.OASType[t.Any]:
    if '$ref' in work_item:
        logger.opt(lazy=True).trace(
            'Following reference {ref}',
            ref=lambda: work_item['$ref'],
        )
        return resolve(
            components,
            ref.resolve(components, work_item['$ref']),
        )
    elif 'type' in work_item:
        oas_type = work_item['type']
        logger.opt(lazy=True).trace(
            'Resolving schema of type={type}',
            type=lambda: oas_type,
        )
        resolvers = {
            'number': functools.partial(_resolve_oas_number, float),
            'integer': functools.partial(_resolve_oas_number, int),
            'boolean': _resolve_oas_boolean,
            'string': _resolve_oas_string,
            'array': functools.partial(_resolve_oas_array, components),
            'object': functools.partial(_resolve_oas_object, components),
        }  # type:  t.Dict[str, t.Callable[[t.Dict[str, t.Any]], model.OASType[t.Any]]]
        return resolvers[oas_type](work_item)
    elif 'anyOf' in work_item:
        return _resolve_any_of(
            components=components,
            work_item=work_item,
        )
    elif 'oneOf' in work_item:
        return _resolve_one_of(
            components=components,
            work_item=work_item,
        )
    elif 'allOf' in work_item:
        return _resolve_all_of(
            components=components,
            work_item=work_item,
        )
    else:
        return _resolve_oas_any(work_item)
Пример #2
0
def _resolve_all_of(
    components: t.Dict[str, t.Dict[str, t.Any]],
    work_item: t.Dict[str, t.Any],
) -> model.OASType[t.Any]:
    # check what allOf stuff we build
    # - check if there is a conflict in definitions
    # - https://json-schema.org/understanding-json-schema/reference/combining.html#allof
    # pick the type and go with normal resolution along with
    # merging the resolved models
    mix_definition: t.List[t.Dict[str, t.Any]] = work_item.get('allOf', [])

    resolved_mix_def = list(
        map(
            lambda mx: ref.resolve(components, mx['$ref'])
            if '$ref' in mx else mx,
            mix_definition,
        ), )
    schema_types: t.Set[str] = set(
        filter(
            lambda mx: mx is not None,
            map(
                lambda mx: mx.get('type', None),  # type: ignore
                resolved_mix_def,
            ),
        ), )

    if len(schema_types) == 0:
        oas_type = 'any'
    elif len(schema_types) > 1:
        raise exceptions.OASConflict(
            f'allOf cannot combine more then one OAS type. '
            f'Detected those types [{", ".join(iter(map(repr, schema_types)))}]',
        )
    else:
        oas_type = list(schema_types)[0]

    logger.opt(lazy=True).trace(
        'allOf resolves into type: {oas_type}',
        oas_type=lambda: oas_type,
    )

    all_of_merged = all_of.merge(oas_type, {}, work_item)
    for sub_schema in resolved_mix_def:
        all_of_merged = all_of.merge(oas_type, all_of_merged, sub_schema)
    return resolve(components=components, work_item=all_of_merged)
Пример #3
0
def _resolve_parameters(
    components: t.Dict[str, t.Any],
    parameters: t.List[t.Dict[str, t.Any]],
) -> model.OASParameters:
    logger.opt(lazy=True).trace(
        'Resolving {count} of parameters',
        count=lambda: len(parameters),
    )

    resolved_parameters: t.List[model.OASParameter] = []

    for param in parameters:
        if '$ref' in param:
            logger.trace(
                'Parameter defined as $ref, following $ref={ref}',
                ref=param['$ref'],
            )
            param_def = parse_ref.resolve(components, param['$ref'])
        else:
            param_def = param

        param_type_to_cls: t.Mapping[str, t.Type[P]] = {
            'header': model.OASHeaderParameter,
            'path': model.OASPathParameter,
            'query': model.OASQueryParameter,
            'cookie': model.OASCookieParameter,
        }
        param_in = param_type_to_cls[param_def['in']]
        param_name = param_def['name']

        logger.trace(
            'Resolving param={param_name} defined in={param_in}',
            param_name=param_name,
            param_in=param_in,
        )

        resolved_parameters.append(
            _resolve_parameter(
                components=components,
                param_name=param_name,
                param_def=param_def,
                param_in=param_in,
            ), )

    return frozenset(resolved_parameters)
Пример #4
0
def _resolve_content(
    components: t.Dict[str, t.Dict[str, t.Any]],
    work_item: t.Dict[str, t.Any],
) -> model.OASContent:
    if '$ref' in work_item:
        return _resolve_content(
            components,
            parse_ref.resolve(components, work_item['$ref']),
        )
    elif 'content' in work_item:
        work_item = work_item['content']

        def _build_media_type(
            mime_type: str,
            media_type_def: t.Dict[str, t.Any],
        ) -> model.OASMediaType:
            # raw_example = media_type_def.get('example', None)
            # raw_examples = media_type_def.get('examples', {})
            # raw_encoding = media_type_def.get('encoding', None)

            # if not (raw_example and raw_examples) and schema.example is not None:
            #     raw_examples = [{mime_type: schema.example}]
            # elif raw_examples is None and raw_example is not None:
            #     raw_examples = [{mime_type: raw_example}]

            return model.OASMediaType(schema=parse_type.resolve(
                components,
                media_type_def['schema'],
            ), )

        return {
            model.MimeType(mime_type):
            _build_media_type(mime_type, work_item[mime_type])
            for mime_type in work_item
        }
    else:
        return {}
Пример #5
0
def test_spec_load_follow_ref(
    reference: str,
    expected_def: t.Dict[str, t.Any],
) -> None:
    components = {
        'requestBodies': {
            'Example': {
                'description': 'category to add to the system',
                'content': {
                    'application/json': {
                        'schema': {
                            '$ref': '#/components/schemas/Category',
                        },
                        'examples': {
                            'user': {
                                'summary': 'Category Example',
                                'externalValue':
                                'http://category-example.json',
                            },
                        },
                    },
                },
            },
        },
        'schemas': {
            'Category': {
                'type': 'object',
                'properties': {
                    'id': {
                        'type': 'integer',
                        'format': 'int64',
                    },
                    'name': {
                        'type': 'string',
                    },
                },
            },
            'UltraCategory': {
                '$ref': '#/components/schemas/Category',
            },
        },
        'headers': {
            'X-Rate-Limit': {
                'description': 'Rate limit for this API',
                'required': True,
                'schema': {
                    'type': 'integer',
                    'format': 'int32',
                },
            },
        },
        'parameters': {
            'limitParam': {
                'name': 'limit',
                'in': 'query',
                'description': 'max records to return',
                'required': True,
                'schema': {
                    'type': 'integer',
                    'format': 'int32',
                },
            },
        },
        'responses': {
            'NotFound': {
                'description': 'Entity not found.',
            },
        },
        'securitySchemes': {
            'apiKey': {
                'type': 'apiKey',
                'name': 'api_key',
                'in': 'header',
            },
        },
    }
    assert expected_def == ref.resolve(components, reference)
Пример #6
0
def test_spec_load_follow_ref_no_such_ref() -> None:
    with pytest.raises(KeyError):
        ref.resolve({}, '#/components/schemas/Dummy')
Пример #7
0
def _resolve_parameter(
    components: t.Dict[str, t.Dict[str, t.Any]],
    param_name: str,
    param_def: t.Dict[str, t.Any],
    param_in: t.Type[P],
) -> P:
    if '$ref' in param_def:
        return _resolve_parameter(
            components=components,
            param_name=param_name,
            param_def=parse_ref.resolve(components, param_def['$ref']),
            param_in=param_in,
        )
    else:
        # needed to determine proper content carried by the field
        # either schema or content will bet set, otherwise OAS is invalid
        schema = param_def.get('schema', None)
        style = model.OASParameterStyles[param_def.get(
            'style',
            param_in.default_style,
        )]
        content = param_def.get('content', None)

        # post processing fields
        explode = bool(param_def.get('explode', style.name == 'form'))
        required = bool(param_def.get('required', False))
        deprecated = bool(param_def.get('deprecated', False))
        example = param_def.get('example', None)

        # those fields are valid either for cookie or header
        allow_empty_value: t.Optional[
            bool] = None if 'style' in param_def else bool(
                param_def.get('allowEmptyValue', False), )

        final_schema: t.Union[t.Tuple[model.OASType[t.Any],
                                      model.OASParameterStyle, ],
                              model.OASContent]
        if content is not None:
            final_schema = _resolve_content(components, param_def)
        else:
            if param_in not in style.locations:
                raise ValueError(
                    f'Path param {param_name} has incompatible style {style.name}',
                )
            final_schema = (
                parse_type.resolve(components, schema),
                style,
            )

        if issubclass(param_in, model.OASHeaderParameter):
            if param_name in model.OASReservedHeaders:
                raise ValueError(
                    f'Header parameter name {param_name} is reserved thus invalid',
                )
            return model.OASHeaderParameter(
                name=model.OASParameterName(param_name),
                example=example,
                required=required,
                explode=explode,
                deprecated=deprecated,
                schema=final_schema,
            )
        elif issubclass(param_in, model.OASPathParameter):
            if not required:
                raise ValueError(
                    f'Path parameter {param_name} must have required set to True',
                )
            return model.OASPathParameter(
                name=model.OASParameterName(param_name),
                example=example,
                explode=explode,
                deprecated=deprecated,
                schema=final_schema,
            )
        elif issubclass(param_in, model.OASQueryParameter):
            allow_reserved = bool(param_def.get('allowReserved', False))
            return model.OASQueryParameter(
                name=model.OASParameterName(param_name),
                example=example,
                required=required,
                explode=explode,
                deprecated=deprecated,
                schema=final_schema,
                allow_empty_value=allow_empty_value,
                allow_reserved=allow_reserved,
            )
        elif issubclass(param_in, model.OASCookieParameter):
            return model.OASCookieParameter(
                name=model.OASParameterName(param_name),
                example=example,
                required=required,
                explode=explode,
                deprecated=deprecated,
                schema=final_schema,
            )
        else:
            raise ValueError(
                f'Cannot build parameter {param_name} '
                f'definition from {param_in}', )