Beispiel #1
0
    def get_paginated_response(self, paginator, response_schema):
        assert 'data' in response_schema[
            'properties'], "expected data field in response"
        assert response_schema['properties'][
            'data'].type == openapi.TYPE_ARRAY, "array expected for paged response"
        if not isinstance(paginator,
                          (pagination.JsonApiPageNumberPagination,
                           pagination.JsonApiLimitOffsetPagination)):
            return inspectors.NotHandled

        has_page = isinstance(paginator,
                              pagination.JsonApiPageNumberPagination)
        meta_schema = openapi.Schema(
            type=openapi.TYPE_OBJECT,
            properties=OrderedDict(pagination=openapi.Schema(
                type=openapi.TYPE_OBJECT,
                properties=OrderedDict(
                    filter_none((
                        ('count', openapi.Schema(type=openapi.TYPE_INTEGER)),
                        ('page', openapi.Schema(
                            type=openapi.TYPE_INTEGER) if has_page else None),
                        ('pages', openapi.Schema(
                            type=openapi.TYPE_INTEGER) if has_page else None),
                        ('limit', openapi.Schema(type=openapi.TYPE_INTEGER
                                                 ) if not has_page else None),
                        ('offset', openapi.Schema(type=openapi.TYPE_INTEGER
                                                  ) if not has_page else None),
                    ))),
            )))
        links_schema = openapi.Schema(
            type=openapi.TYPE_OBJECT,
            properties=OrderedDict((
                ('first',
                 openapi.Schema(type=openapi.TYPE_STRING,
                                format=openapi.FORMAT_URI)),
                ('next',
                 openapi.Schema(type=openapi.TYPE_STRING,
                                format=openapi.FORMAT_URI)),
                ('last',
                 openapi.Schema(type=openapi.TYPE_STRING,
                                format=openapi.FORMAT_URI)),
                ('prev',
                 openapi.Schema(type=openapi.TYPE_STRING,
                                format=openapi.FORMAT_URI)),
            )),
        )

        return openapi.Schema(
            type=openapi.TYPE_OBJECT,
            properties=filter_none(
                OrderedDict(
                    links=links_schema,
                    meta=meta_schema,
                    data=response_schema.properties['data'],
                    included=response_schema.properties.get('included'))))
Beispiel #2
0
    def build_serializer_schema(self,
                                serializer,
                                resource_name,
                                SwaggerType,
                                ChildSwaggerType,
                                use_references,
                                is_request=None):
        fields = json_api_utils.get_serializer_fields(serializer)
        is_post = self.method.lower() == 'post'

        id_field = self.extract_id_field(fields, serializer)
        if id_field is None and not (is_request and is_post):
            logging.warning(
                '{view}.{serializer} does not contain id field as every resource should'
                .format(view=self.view.__class__.__name__,
                        serializer=serializer.__class__.__name__))

        attributes, req_attrs = self.extract_attributes(
            id_field, fields, ChildSwaggerType, use_references, is_request)
        relationships, req_rels = self.extract_relationships(
            fields, ChildSwaggerType, use_references, is_request)
        links = self.extract_links(fields, ChildSwaggerType,
                                   use_references) if not is_request else None

        schema_fields = filter_none(
            OrderedDict(
                type=self.build_type_schema(resource_name),
                id=self.probe_field_inspectors(id_field, ChildSwaggerType,
                                               use_references)
                if id_field and not (self.strip_read_fields_from_request
                                     and is_request and is_post) else None,
                attributes=openapi.Schema(type=openapi.TYPE_OBJECT,
                                          properties=attributes,
                                          required=req_attrs)
                if attributes else None,
                relationships=openapi.Schema(type=openapi.TYPE_OBJECT,
                                             properties=relationships,
                                             required=req_rels)
                if relationships else None,
                links=openapi.Schema(type=openapi.TYPE_OBJECT,
                                     properties=links) if links else None))

        required_properties = None
        if self.is_request_or_unknown(is_request):
            required_properties = filter_none([
                'type',
                'id' if 'id' in schema_fields else None,
                'attributes' if req_attrs else None,
                'relationships' if req_rels else None,
            ])

        return SwaggerType(type=openapi.TYPE_OBJECT,
                           properties=schema_fields,
                           required=required_properties)
Beispiel #3
0
        def make_schema_definition():
            properties = OrderedDict()
            required = []
            for property_name, child in serializer.fields.items():
                prop_kwargs = {"read_only": bool(child.read_only) or None}
                prop_kwargs = filter_none(prop_kwargs)

                child_schema = self.probe_field_inspectors(
                    child, ChildSwaggerType, use_references, **prop_kwargs)
                properties[property_name] = child_schema

                if child.required and not getattr(child_schema, "read_only",
                                                  False):
                    required.append(property_name)

            result = SwaggerType(
                type=openapi.TYPE_OBJECT,
                properties=properties,
                required=required or None,
            )
            if not ref_name and "title" in result:
                # on an inline model, the title is derived from the field name
                # but is visually displayed like the model name, which is confusing
                # it is better to just remove title from inline models
                del result.title

            # Provide an option to add manual paremeters to a schema
            # for example, to add examples
            # self.add_manual_fields(serializer, result)
            return self.process_result(result, None, None)
    def get_operation(self, operation_keys=None):
        operation_keys = operation_keys or self.operation_keys

        consumes = self.get_consumes()
        produces = self.get_produces()

        body = self.get_request_body_parameters(consumes)
        query = self.get_query_parameters()
        parameters = body + query
        parameters = filter_none(parameters)
        parameters = self.add_manual_parameters(parameters)

        operation_id = self.get_operation_id(operation_keys)
        summary, description = self.get_summary_and_description()
        security = self.get_security()
        assert security is None or isinstance(security, list), \
            "security must be a list of security requirement objects"
        deprecated = self.is_deprecated()
        tags = self.get_tags(operation_keys)

        responses = self.get_responses()

        return openapi.Operation(
            operation_id=operation_id,
            description=force_real_str(description),
            summary=force_real_str(summary),
            responses=responses,
            parameters=parameters,
            consumes=consumes,
            produces=produces,
            tags=tags,
            security=security,
            deprecated=deprecated,
            **{'x-code-samples': self.overrides.get('code_examples')})
Beispiel #5
0
    def extract_links(self, fields, ChildSwaggerType, use_references):
        self_field_name = api_settings.URL_FIELD_NAME

        return filter_none(
            OrderedDict(self=openapi.Schema(
                type=openapi.TYPE_STRING, format=openapi.FORMAT_URI
            ) if self_field_name in fields and isinstance(
                fields[self_field_name], serializers.RelatedField) else None))
    def build_json_resource_schema(self,
                                   serializer,
                                   resource_name,
                                   SwaggerType,
                                   ChildSwaggerType,
                                   use_references,
                                   is_request=None):
        fields = json_api_utils.get_serializer_fields(serializer)

        id_ = fields.get('id')
        if id_ is None and self.method.lower() == 'get':
            logging.warning(
                '{view}.{serializer} does not contain id field as every resource should'
                .format(view=self.view.__class__.__name__,
                        serializer=serializer.__class__.__name__))
        if id_ is not None and id_.read_only and is_request:
            id_ = None

        attributes, req_attributes = self.extract_attributes(
            fields, ChildSwaggerType, use_references, is_request)
        relationships, req_relationships = self.extract_relationships(
            fields, ChildSwaggerType, use_references, is_request)
        links = self.extract_links(fields, ChildSwaggerType,
                                   use_references) if not is_request else None

        schema_fields = filter_none(
            OrderedDict(
                type=SwaggerType(type=openapi.TYPE_STRING,
                                 pattern=resource_name),
                id=self.probe_field_inspectors(
                    id_, ChildSwaggerType, use_references) if id_ else None,
                attributes=SwaggerType(type=openapi.TYPE_OBJECT,
                                       properties=attributes,
                                       required=req_attributes)
                if attributes else None,
                relationships=SwaggerType(type=openapi.TYPE_OBJECT,
                                          properties=relationships,
                                          required=req_relationships)
                if relationships else None,
                links=SwaggerType(type=openapi.TYPE_OBJECT, properties=links)
                if links else None))

        if self.is_request_or_unknown(is_request):
            required_properties = ['id', 'type'
                                   ] if 'id' in schema_fields else ['type']
        else:
            required_properties = None

        return SwaggerType(type=openapi.TYPE_OBJECT,
                           properties=schema_fields,
                           required=required_properties)
Beispiel #7
0
    def get_field_properties(serializer_class):
        """
        serializer_class의 field들의 schema들을 dictionary 타입으로 만들어 반환
        """
        properties = OrderedDict()

        for property_name, child in serializer_class().fields.items():
            prop_kwargs = {'read_only': bool(child.read_only) or None}
            prop_kwargs = filter_none(prop_kwargs)

            child_schema = SchemaGenerator._probe_field_inspectors(
                child, **prop_kwargs)
            properties[property_name] = child_schema

        return properties
Beispiel #8
0
    def get_required_fields(serializer_class):
        """
        serializer_class의 required field들의 list 반환
        """
        required = []
        for property_name, child in serializer_class().fields.items():
            prop_kwargs = {'read_only': bool(child.read_only) or None}
            prop_kwargs = filter_none(prop_kwargs)

            child_schema = SchemaGenerator._probe_field_inspectors(
                child, **prop_kwargs)

            if child.required and not getattr(child_schema, 'read_only',
                                              False):
                required.append(property_name)

        return required
Beispiel #9
0
    def get_operation(self, operation_keys):
        consumes = self.get_consumes()
        produces = self.get_produces()

        multipart = ['multipart/form-data', 'application/x-www-form-urlencoded']
        if 'file' in [param['type'] for param in self.get_request_body_parameters(multipart)]:
            # automatically set the media type to form data if there's a file
            # needed due to https://github.com/axnsan12/drf-yasg/issues/386
            consumes = multipart

        body = self.get_request_body_parameters(consumes)
        query = self.get_query_parameters()
        parameters = body + query
        parameters = filter_none(parameters)
        parameters = self.add_manual_parameters(parameters)
        if 'bindings' in self.request.query_params:
            operation_id = self.overrides.get('operation_id', '')
            if not operation_id:
                operation_id = operation_keys[-1]
        else:
            operation_id = self.get_operation_id(operation_keys)
        summary, description = self.get_summary_and_description()
        security = self.get_security()
        assert security is None or isinstance(security, list), "security must be a list of " \
                                                               "security requirement objects"
        deprecated = self.is_deprecated()
        tags = self.get_tags(operation_keys)

        responses = self.get_responses()
        if 'operation_summary' not in self.overrides:
            summary = self.get_summary(operation_keys)
        return openapi.Operation(
            operation_id=operation_id,
            description=force_real_str(description),
            summary=force_real_str(summary),
            responses=responses,
            parameters=parameters,
            consumes=consumes,
            produces=produces,
            tags=tags,
            security=security,
            deprecated=deprecated
        )
Beispiel #10
0
    def get_default_responses(self):
        if not is_json_api_response(self.get_renderer_classes()):
            return super().get_default_responses()

        method = self.method.lower()

        default_status = guess_response_status(method)
        default_schema = ''
        if method in ('get', 'post', 'put', 'patch'):
            default_serializer = self.get_default_response_serializer()

            default_schema = openapi.Schema(
                type=openapi.TYPE_OBJECT,
                properties=OrderedDict({
                    'data': self.get_default_response_data(default_serializer),
                    'included': self.get_default_response_included(default_serializer)
                })
            )

        return filter_none(OrderedDict({str(default_status): default_schema}))
Beispiel #11
0
    def get_operation(self, operation_keys):
        consumes = self.get_consumes()
        produces = self.get_produces()

        multipart = [
            'multipart/form-data', 'application/x-www-form-urlencoded'
        ]
        if self.method != 'GET':
            request_params = self.get_request_body_parameters(multipart)
            type_list = [param['type'] for param in request_params if param]
            if 'file' in type_list:
                # automatically set the media type to form data if there's a file
                # needed due to https://github.com/axnsan12/drf-yasg/issues/386
                consumes = multipart

        body = self.get_request_body_parameters(consumes)
        query = self.get_query_parameters()
        if self.method == 'GET':
            fields_paramenter = Parameter(
                name="fields",
                in_="query",
                description="A list of fields to include in the response.",
                required=False,
                type="string",
            )
            query.append(fields_paramenter)
            not_fields_paramenter = Parameter(
                name="exclude_fields",
                in_="query",
                description="A list of fields to exclude from the response.",
                required=False,
                type="string",
            )
            query.append(not_fields_paramenter)
        parameters = body + query
        parameters = filter_none(parameters)
        parameters = self.add_manual_parameters(parameters)
        if 'bindings' in self.request.query_params:
            operation_id = self.overrides.get('operation_id', '')
            if not operation_id:
                operation_id = operation_keys[-1]
        else:
            operation_id = self.get_operation_id(operation_keys)
        summary, description = self.get_summary_and_description()

        if "include_html" not in self.request.query_params:
            description = strip_tags(description)

        security = self.get_security()
        assert security is None or isinstance(security, list), "security must be a list of " \
                                                               "security requirement objects"
        deprecated = self.is_deprecated()
        tags = self.get_tags(operation_keys)

        responses = self.get_responses()
        if 'operation_summary' not in self.overrides:
            summary = self.get_summary(operation_keys)
        return openapi.Operation(operation_id=operation_id,
                                 description=force_real_str(description),
                                 summary=force_real_str(summary),
                                 responses=responses,
                                 parameters=parameters,
                                 consumes=consumes,
                                 produces=produces,
                                 tags=tags,
                                 security=security,
                                 deprecated=deprecated)
Beispiel #12
0
    def get_operation(self, operation_keys):
        consumes = self.get_consumes()
        produces = self.get_produces()

        multipart = [
            "multipart/form-data", "application/x-www-form-urlencoded"
        ]
        if self.method != "GET":
            contains_file_field = False
            serializer = self.get_request_serializer()

            for field_name, field in getattr(serializer, "fields", {}).items():
                if isinstance(field, serializers.FileField):
                    contains_file_field = True
                    break

            if contains_file_field:
                # automatically set the media type to form data if there's a file
                # needed due to https://github.com/axnsan12/drf-yasg/issues/386
                consumes = multipart

        body = self.get_request_body_parameters(consumes)
        query = self.get_query_parameters()
        if self.method == "GET":
            fields_paramenter = Parameter(
                name="fields",
                in_="query",
                description="A list of fields to include in the response.",
                required=False,
                type="string",
            )
            query.append(fields_paramenter)
            not_fields_paramenter = Parameter(
                name="exclude_fields",
                in_="query",
                description="A list of fields to exclude from the response.",
                required=False,
                type="string",
            )
            query.append(not_fields_paramenter)
        parameters = body + query
        parameters = filter_none(parameters)
        parameters = self.add_manual_parameters(parameters)
        if "bindings" in self.request.query_params:
            operation_id = self.overrides.get("operation_id", "")
            if not operation_id:
                operation_id = operation_keys[-1]
        else:
            operation_id = self.get_operation_id(operation_keys)
        summary, description = self.get_summary_and_description()

        if "include_html" not in self.request.query_params:
            description = strip_tags(description)

        security = self.get_security()
        assert security is None or isinstance(
            security, list), ("security must be a list of "
                              "security requirement objects")
        deprecated = self.is_deprecated()
        tags = self.get_tags(operation_keys)

        responses = self.get_responses()
        if "operation_summary" not in self.overrides:
            summary = self.get_summary(operation_keys)
        return openapi.Operation(
            operation_id=operation_id,
            description=force_real_str(description),
            summary=force_real_str(summary),
            responses=responses,
            parameters=parameters,
            consumes=consumes,
            produces=produces,
            tags=tags,
            security=security,
            deprecated=deprecated,
        )
Beispiel #13
0
    def extract_relationships(self,
                              fields,
                              ChildSwaggerType,
                              use_references,
                              is_request=None):
        relationships = OrderedDict()
        required_relationships = []
        for field_name, field in fields.items():
            self.maybe_fix_broken_parent_relation(field)

            if self.should_strip_from_schema(field, is_request):
                continue
            # Self url field
            if field_name == api_settings.URL_FIELD_NAME:
                continue
            # Skip fields without relations
            if not isinstance(
                    field, (relations.RelatedField, relations.ManyRelatedField,
                            serializers.Serializer)):
                continue

            # Produce swagger output
            relation_data_schema = openapi.Schema(**filter_none(
                OrderedDict(
                    type=openapi.TYPE_OBJECT,
                    properties=OrderedDict(
                        id=self.probe_field_inspectors(field, ChildSwaggerType,
                                                       use_references),
                        type=self.build_type_schema(
                            self.get_resource_name_from_related_id_field(
                                field_name, field),
                            read_only=field.read_only),
                    ),
                    required=['id', 'type'] if (self.is_request_or_unknown(
                        is_request)) and not field.read_only else None,
                )))

            if is_many_related_field(field):
                relation_data_schema = openapi.Schema(
                    type=openapi.TYPE_ARRAY, items=relation_data_schema)

            relation_links_schema = self.get_links_from_id_field(
                field_name, field)
            if relation_links_schema:
                relation_links_schema = openapi.Schema(
                    type=openapi.TYPE_OBJECT, properties=relation_links_schema)

            is_relation_required = self.is_request_or_unknown(
                is_request) and field.required and not field.read_only

            relationships[field_name] = openapi.Schema(**filter_none(
                OrderedDict(
                    type=openapi.TYPE_OBJECT,
                    properties=filter_none({
                        'data':
                        relation_data_schema,
                        'links':
                        relation_links_schema if self.
                        not_request_or_unknown(is_request) else None
                    }),
                    required=['data'] if is_relation_required else None,
                    read_only=field.read_only or None,
                    x_read_only=field.read_only or None,
                )))
            if is_relation_required:
                required_relationships.append(field_name)

        return relationships, (required_relationships or None)
Beispiel #14
0
 def build_type_schema(self, resource_name, read_only=None):
     return openapi.Schema(**filter_none(
         OrderedDict(type=openapi.TYPE_STRING,
                     pattern=resource_name,
                     read_only=read_only or None)))
    def extract_relationships(self,
                              fields,
                              ChildSwaggerType,
                              use_references,
                              is_request=None):
        relationships = OrderedDict()
        required_relationships = []
        for field_name, field in fields.items():
            many = False
            id_field = field

            if is_request and field.read_only:
                continue

            # Self url field
            if field_name == api_settings.URL_FIELD_NAME:
                continue

            # Skip fields without relations
            if not isinstance(
                    field, (relations.RelatedField, relations.ManyRelatedField,
                            serializers.Serializer)):
                continue

            # Unpack relation from many field
            if isinstance(id_field, serializers.ManyRelatedField):
                id_field = id_field.child_relation
                many = True

            resource_name = self.get_resource_name_from_id_field(
                field_name, id_field)

            # Pass swagger type evaluation to inspectors
            if getattr(id_field, 'pk_field', None):
                # a PrimaryKeyRelatedField can have a `pk_field` attribute which is a
                # serializer field that will convert the PK value
                swagger_id_field = self.probe_field_inspectors(
                    id_field.pk_field, ChildSwaggerType, use_references)
            else:
                swagger_id_field = self.probe_field_inspectors(
                    id_field, ChildSwaggerType, use_references)

            # Produce swagger output
            relation_data = openapi.Schema(**filter_none(
                OrderedDict(
                    type=openapi.TYPE_OBJECT,
                    properties={
                        'type':
                        openapi.Schema(**filter_none(
                            OrderedDict(type=openapi.TYPE_STRING,
                                        pattern=resource_name,
                                        read_only=field.read_only or None))),
                        'id':
                        swagger_id_field
                    },
                    required=['id', 'type'] if (self.is_request_or_unknown(
                        is_request)) and not field.read_only else None,
                )))

            if many:
                relation_data = openapi.Schema(type=openapi.TYPE_ARRAY,
                                               items=relation_data)

            relation_links = self.get_links_from_id_field(field_name, field)
            if relation_links:
                relation_links = openapi.Schema(type=openapi.TYPE_OBJECT,
                                                properties=relation_links)

            relationships[field_name] = openapi.Schema(**filter_none(
                OrderedDict(
                    type=openapi.TYPE_OBJECT,
                    properties=filter_none({
                        'data':
                        relation_data,
                        'links':
                        relation_links if self.
                        not_request_or_unknown(is_request) else None
                    }),
                    read_only=field.read_only or None,
                    x_read_only=field.read_only or None,
                )))
            if self.is_request_or_unknown(
                    is_request) and field.required and not field.read_only:
                required_relationships.append(field_name)

        return relationships, (required_relationships or None)