Ejemplo n.º 1
0
def get_error_schema(errors=None):
    return build_object_type({
        "error": {
            "type": "string",
            "description":
            "Machine readable error indicating what went wrong.",
            "enum": errors,
        },
        "detail": {
            "oneOf": [
                {
                    "type": "string",
                    "format": "string",
                    "description":
                    "Human readable details about what went wrong.",
                },
                {
                    "type":
                    "object",
                    "format":
                    "object",
                    "description":
                    "Machine readable object about what went wrong.",
                },
            ]
        },
    })
Ejemplo n.º 2
0
def get_error_schema(errors=None):
    return build_object_type({
        'error': {
            'type': 'string',
            'description':
            'Machine readable error indicating what went wrong.',
            'enum': errors
        },
        'detail': {
            'oneOf': [{
                'type':
                'string',
                'format':
                'string',
                'description':
                'Human readable details about what went wrong.'
            }, {
                'type':
                'object',
                'format':
                'object',
                'description':
                'Machine readable object about what went wrong.'
            }]
        }
    })
Ejemplo n.º 3
0
    def _map_basic_serializer(self, serializer, direction):
        serializer = force_instance(serializer)
        required = set()
        properties = {}

        for field in serializer.fields.values():
            if isinstance(field, serializers.HiddenField):
                continue

            schema = self._map_serializer_field(field, direction)
            # skip field if there is no schema for the direction
            if not schema:
                continue

            if field.required or schema.get('readOnly'):
                required.add(field.field_name)

            self._map_field_validators(field, schema)

            properties[field.field_name] = safe_ref(schema)

        if spectacular_settings.COMPONENT_SPLIT_PATCH:
            if self.method == 'PATCH' and direction == 'request':
                required = []

        return build_object_type(
            properties=properties,
            required=required,
            description=inspect.getdoc(serializer),
        )
Ejemplo n.º 4
0
class UploadFileView(APIView):
    permission_classes = (IsAuthenticated, )
    parser_classes = (MultiPartParser, )

    @extend_schema(
        tags=['User files'],
        operation_id='upload_file',
        description=
        ('Uploads a file to Baserow by uploading the file contents directly. A '
         '`file` multipart is expected containing the file contents.'),
        request=build_object_type(),
        responses={
            200:
            UserFileSerializer,
            400:
            get_error_schema(
                ['ERROR_INVALID_FILE', 'ERROR_FILE_SIZE_TOO_LARGE'])
        })
    @transaction.atomic
    @map_exceptions({
        InvalidFileStreamError: ERROR_INVALID_FILE,
        FileSizeTooLargeError: ERROR_FILE_SIZE_TOO_LARGE
    })
    def post(self, request):
        """Uploads a file by uploading the contents directly."""

        if 'file' not in request.FILES:
            raise InvalidFileStreamError('No file was provided.')

        file = request.FILES.get('file')
        user_file = UserFileHandler().upload_user_file(request.user, file.name,
                                                       file)
        serializer = UserFileSerializer(user_file)
        return Response(serializer.data)
Ejemplo n.º 5
0
    def _get_request_body(self):
        # only unsafe methods can have a body
        if self.method not in ('PUT', 'PATCH', 'POST'):
            return None

        serializer = force_instance(self.get_request_serializer())

        request_body_required = False
        if is_list_serializer(serializer):
            if is_serializer(serializer.child):
                component = self.resolve_serializer(serializer.child,
                                                    'request')
                schema = build_array_type(component.ref)
            else:
                schema = build_array_type(
                    self._map_serializer_field(serializer.child, 'request'))
            request_body_required = True
        elif is_serializer(serializer):
            if self.method == 'PATCH':
                serializer.partial = True
            component = self.resolve_serializer(serializer, 'request')
            if not component.schema:
                # serializer is empty so skip content enumeration
                return None
            schema = component.ref
            # request body is only required if any required property is not read-only
            readonly_props = [
                p for p, s in component.schema.get('properties', {}).items()
                if s.get('readOnly')
            ]
            required_props = component.schema.get('required', [])
            request_body_required = any(req not in readonly_props
                                        for req in required_props)
        elif is_basic_type(serializer):
            schema = build_basic_type(serializer)
            if not schema:
                return None
        else:
            warn(
                f'could not resolve request body for {self.method} {self.path}. defaulting to generic '
                'free-form object. (maybe annotate a Serializer class?)')
            schema = build_object_type(
                additionalProperties={},
                description='Unspecified request body',
            )

        request_body = {
            'content': {
                media_type: build_media_type_object(
                    schema,
                    self._get_examples(serializer, 'request', media_type))
                for media_type in self.map_parsers()
            }
        }

        if request_body_required:
            request_body['required'] = request_body_required

        return request_body
Ejemplo n.º 6
0
    def _map_basic_serializer(self, serializer, direction):
        serializer = force_instance(serializer)
        required = set()
        properties = {}

        for field in serializer.fields.values():
            if isinstance(field, serializers.HiddenField):
                continue
            if field.field_name in get_override(serializer, 'exclude_fields',
                                                []):
                continue

            schema = self._map_serializer_field(field, direction)
            # skip field if there is no schema for the direction
            if not schema:
                continue

            add_to_required = (
                field.required or
                (schema.get('readOnly')
                 and not spectacular_settings.COMPONENT_NO_READ_ONLY_REQUIRED))
            if add_to_required:
                required.add(field.field_name)

            self._map_field_validators(field, schema)

            properties[field.field_name] = safe_ref(schema)

        if spectacular_settings.COMPONENT_SPLIT_PATCH:
            if self.method == 'PATCH' and direction == 'request':
                required = []

        return build_object_type(
            properties=properties,
            required=required,
            description=get_doc(serializer.__class__),
        )
Ejemplo n.º 7
0
from drf_spectacular.plumbing import build_object_type


group_user_schema = build_object_type({
    'order': {
        'type': 'int',
        'description': 'The order of the group, lowest first.',
        'example': 0
    },
    'id': {
        'type': 'int',
        'description': 'The unique identifier of the group.',
        'example': 1
    },
    'name': {
        'type': 'string',
        'description': 'The name given to the group.',
        'example': 'Bram\'s group'
    }
})
Ejemplo n.º 8
0
)
from drf_spectacular.settings import spectacular_settings
from drf_spectacular.types import OpenApiTypes


def build_standard_type(obj, **kwargs):
    """Build a basic type with optional add owns."""
    schema = build_basic_type(obj)
    schema.update(kwargs)
    return schema


GENERIC_ERROR = build_object_type(
    description=_("Generic API Error"),
    properties={
        "detail": build_standard_type(OpenApiTypes.STR),
        "code": build_standard_type(OpenApiTypes.STR),
    },
    required=["detail"],
)
VALIDATION_ERROR = build_object_type(
    description=_("Validation Error"),
    properties={
        "non_field_errors":
        build_array_type(build_standard_type(OpenApiTypes.STR)),
        "code": build_standard_type(OpenApiTypes.STR),
    },
    required=[],
    additionalProperties={},
)

Ejemplo n.º 9
0
from drf_spectacular.plumbing import build_object_type

create_user_response_schema = build_object_type({
    'user': {
        'type': 'object',
        'description': 'An object containing information related to the user.',
        'properties': {
            'first_name': {
                'type': 'string',
                'description': 'The first name of related user.'
            },
            'username': {
                'type':
                'string',
                'format':
                'email',
                'description':
                'The username of the related user. This is always '
                'an email address.'
            }
        }
    },
    'token': {
        'type': 'string'
    }
})
authenticate_user_schema = create_user_response_schema
Ejemplo n.º 10
0
from drf_spectacular.plumbing import build_object_type

group_user_schema = build_object_type({
    "order": {
        "type": "int",
        "description": "The order of the group, lowest first.",
        "example": 0,
    },
    "id": {
        "type": "int",
        "description": "The unique identifier of the group.",
        "example": 1,
    },
    "name": {
        "type": "string",
        "description": "The name given to the group.",
        "example": "Bram's group",
    },
})
Ejemplo n.º 11
0
def resolve_type_hint(hint) -> Any:
    """drf-spectacular library method modified as described above"""
    origin, args = _get_type_hint_origin(hint)
    excluded_fields = get_override(hint, "exclude_fields", [])

    if origin is None and is_basic_type(hint, allow_none=False):
        return build_basic_type(hint)
    elif origin is None and inspect.isclass(hint) and issubclass(hint, tuple):
        # a convoluted way to catch NamedTuple. suggestions welcome.
        if get_type_hints(hint):
            properties = {
                k: resolve_type_hint(v)
                for k, v in get_type_hints(hint).items()
            }
        else:
            properties = {
                k: build_basic_type(OpenApiTypes.ANY)
                for k in hint._fields
            }
        return build_object_type(properties=properties,
                                 required=properties.keys())
    elif origin is list or hint is list:
        return build_array_type(
            resolve_type_hint(args[0])
            if args else build_basic_type(OpenApiTypes.ANY))
    elif origin is tuple:
        return build_array_type(
            schema=build_basic_type(args[0]),
            max_length=len(args),
            min_length=len(args),
        )
    elif origin is dict or origin is defaultdict or origin is OrderedDict:
        schema = build_basic_type(OpenApiTypes.OBJECT)
        if args and args[1] is not typing.Any:
            schema["additionalProperties"] = resolve_type_hint(args[1])
        return schema
    elif origin is set:
        return build_array_type(resolve_type_hint(args[0]))
    elif origin is frozenset:
        return build_array_type(resolve_type_hint(args[0]))
    elif origin is Literal:
        # Literal only works for python >= 3.8 despite typing_extensions, because it
        # behaves slightly different w.r.t. __origin__
        schema = {"enum": list(args)}
        if all(type(args[0]) is type(choice) for choice in args):
            schema.update(build_basic_type(type(args[0])))
        return schema
    elif inspect.isclass(hint) and issubclass(hint, Enum):
        schema = {"enum": [item.value for item in hint]}
        mixin_base_types = [t for t in hint.__mro__ if is_basic_type(t)]
        if mixin_base_types:
            schema.update(build_basic_type(mixin_base_types[0]))
        return schema
    elif isinstance(hint, _TypedDictMeta):
        return build_object_type(
            properties={
                k: resolve_type_hint(v)
                for k, v in get_type_hints(hint).items()
                if k not in excluded_fields
            },
            description=inspect.cleandoc(hint.__doc__ or ""),
            required=[
                h for h in hint.__required_keys__ if h not in excluded_fields
            ],
        )
    elif origin is Union:
        type_args = [arg for arg in args
                     if arg is not type(None)]  # noqa: E721
        if len(type_args) > 1:
            schema = {"oneOf": [resolve_type_hint(arg) for arg in type_args]}
        else:
            schema = resolve_type_hint(type_args[0])
        if type(None) in args:
            schema["nullable"] = True
        return schema
    elif origin is collections.abc.Iterable:
        return build_array_type(resolve_type_hint(args[0]))
    elif isinstance(hint, typing._TypedDictMeta):
        raise UnableToProceedError(
            "Wrong TypedDict class, please use typing_extensions.TypedDict")
    else:
        raise UnableToProceedError()
Ejemplo n.º 12
0
FACET_RESPONSE = build_object_type(
    description=_("Faceted classification"),
    additionalProperties=build_object_type(
        properties={
            "doc_count": build_basic_type(int),
        },
        additionalProperties=build_object_type(
            properties={
                "meta": build_object_type(
                    properties={
                        "param": build_basic_type(str),
                        "missing": build_basic_type(str),
                        "missing_param": build_basic_type(str),
                    }
                ),
                "doc_count_error_upper_bound": build_basic_type(int),
                "sum_other_doc_count": build_basic_type(int),
                "buckets": build_array_type(
                    build_object_type(
                        properties={
                            "key": build_basic_type(str),
                            "doc_count": build_basic_type(int),
                        },
                    ),
                ),
            },
        ),
    ),
)
Ejemplo n.º 13
0
    """Build a basic type with optional add ons."""
    schema = build_basic_type(obj)
    schema.update(kwargs)
    return schema


GENERIC_ERROR = build_object_type(
    description=_("API error"),
    properties={
        "code": build_standard_type(OpenApiTypes.NUMBER),
        "message": build_standard_type(OpenApiTypes.STR),
        "status_code": build_standard_type(OpenApiTypes.NUMBER),
        "errors": build_array_type(
            build_object_type(
                properties={
                    "code": build_standard_type(OpenApiTypes.NUMBER),
                    "field": build_standard_type(OpenApiTypes.STR),
                    "message": build_standard_type(OpenApiTypes.STR),
                },
                required=["field", "message"],
            ),
        ),
    },
    required=["code", "message", "errors"],
)


def postprocess_schema_responses(result, generator, **kwargs):  # noqa: W0613
    """Workaround to set a default response for endpoints.

    Workaround suggested at
Ejemplo n.º 14
0
from drf_spectacular.plumbing import build_object_type

create_user_response_schema = build_object_type({
    "user": {
        "type": "object",
        "description": "An object containing information related to the user.",
        "properties": {
            "first_name": {
                "type": "string",
                "description": "The first name of related user.",
            },
            "username": {
                "type":
                "string",
                "format":
                "email",
                "description":
                "The username of the related user. This is always "
                "an email address.",
            },
        },
    },
    "token": {
        "type": "string"
    },
})
authenticate_user_schema = create_user_response_schema