Migration to drf_spectacular (#4210)

main
Maria Khrustaleva 4 years ago committed by GitHub
parent dd4a78d8c9
commit d098e42c45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -40,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Different resources (tasks, projects) are not visible anymore for all CVAT instance users by default (<https://github.com/openvinotoolkit/cvat/pull/3788>) - Different resources (tasks, projects) are not visible anymore for all CVAT instance users by default (<https://github.com/openvinotoolkit/cvat/pull/3788>)
- API versioning scheme: using accept header versioning instead of namespace versioning (<https://github.com/openvinotoolkit/cvat/pull/4239>) - API versioning scheme: using accept header versioning instead of namespace versioning (<https://github.com/openvinotoolkit/cvat/pull/4239>)
- Replaced 'django_sendfile' with 'django_sendfile2' (<https://github.com/openvinotoolkit/cvat/pull/4267>) - Replaced 'django_sendfile' with 'django_sendfile2' (<https://github.com/openvinotoolkit/cvat/pull/4267>)
- Use drf-spectacular instead of drf-yasg for swagger documentation (<https://github.com/openvinotoolkit/cvat/pull/4210>)
### Deprecated ### Deprecated
- Job field "status" is not used in UI anymore, but it has not been removed from the database yet (<https://github.com/openvinotoolkit/cvat/pull/3788>) - Job field "status" is not used in UI anymore, but it has not been removed from the database yet (<https://github.com/openvinotoolkit/cvat/pull/3788>)

@ -43,6 +43,10 @@ class StatusChoice(str, Enum):
def choices(cls): def choices(cls):
return tuple((x.value, x.name) for x in cls) return tuple((x.value, x.name) for x in cls)
@classmethod
def list(cls):
return list(map(lambda x: x.value, cls))
def __str__(self): def __str__(self):
return self.value return self.value

@ -17,6 +17,8 @@ from cvat.apps.engine.cloud_provider import get_cloud_storage_instance, Credenti
from cvat.apps.engine.log import slogger from cvat.apps.engine.log import slogger
from cvat.apps.engine.utils import parse_specific_attributes from cvat.apps.engine.utils import parse_specific_attributes
from drf_spectacular.utils import OpenApiExample, extend_schema_serializer
class BasicUserSerializer(serializers.ModelSerializer): class BasicUserSerializer(serializers.ModelSerializer):
def validate(self, data): def validate(self, data):
if hasattr(self, 'initial_data'): if hasattr(self, 'initial_data'):
@ -849,7 +851,7 @@ class ManifestSerializer(serializers.ModelSerializer):
def to_representation(self, instance): def to_representation(self, instance):
return instance.filename if instance else instance return instance.filename if instance else instance
class BaseCloudStorageSerializer(serializers.ModelSerializer): class CloudStorageReadSerializer(serializers.ModelSerializer):
owner = BasicUserSerializer(required=False) owner = BasicUserSerializer(required=False)
manifests = ManifestSerializer(many=True, default=[]) manifests = ManifestSerializer(many=True, default=[])
class Meta: class Meta:
@ -857,7 +859,70 @@ class BaseCloudStorageSerializer(serializers.ModelSerializer):
exclude = ['credentials'] exclude = ['credentials']
read_only_fields = ('created_date', 'updated_date', 'owner', 'organization') read_only_fields = ('created_date', 'updated_date', 'owner', 'organization')
class CloudStorageSerializer(serializers.ModelSerializer): @extend_schema_serializer(
examples=[
OpenApiExample(
'Create AWS S3 cloud storage with credentials',
description='',
value={
'provider_type': models.CloudProviderChoice.AWS_S3,
'resource': 'somebucket',
'display_name': 'Bucket',
'credentials_type': models.CredentialsTypeChoice.KEY_SECRET_KEY_PAIR,
'specific_attributes': 'region=eu-central-1',
'description': 'Some description',
'manifests': [
'manifest.jsonl'
],
},
request_only=True,
),
OpenApiExample(
'Create AWS S3 cloud storage without credentials',
value={
'provider_type': models.CloudProviderChoice.AWS_S3,
'resource': 'somebucket',
'display_name': 'Bucket',
'credentials_type': models.CredentialsTypeChoice.ANONYMOUS_ACCESS,
'manifests': [
'manifest.jsonl'
],
},
request_only=True,
),
OpenApiExample(
'Create Azure cloud storage',
value={
'provider_type': models.CloudProviderChoice.AZURE_CONTAINER,
'resource': 'sonecontainer',
'display_name': 'Container',
'credentials_type': models.CredentialsTypeChoice.ACCOUNT_NAME_TOKEN_PAIR,
'account_name': 'someaccount',
'session_token': 'xxx',
'manifests': [
'manifest.jsonl'
],
},
request_only=True,
),
OpenApiExample(
'Create GCS',
value={
'provider_type': models.CloudProviderChoice.GOOGLE_CLOUD_STORAGE,
'resource': 'somebucket',
'display_name': 'Bucket',
'credentials_type': models.CredentialsTypeChoice.KEY_FILE_PATH,
'key_file': 'file',
'manifests': [
'manifest.jsonl'
],
},
request_only=True,
)
]
)
class CloudStorageWriteSerializer(serializers.ModelSerializer):
owner = BasicUserSerializer(required=False) owner = BasicUserSerializer(required=False)
session_token = serializers.CharField(max_length=440, allow_blank=True, required=False) session_token = serializers.CharField(max_length=440, allow_blank=True, required=False)
key = serializers.CharField(max_length=20, allow_blank=True, required=False) key = serializers.CharField(max_length=20, allow_blank=True, required=False)

@ -6,42 +6,12 @@
from django.urls import path, include from django.urls import path, include
from . import views from . import views
from rest_framework import routers from rest_framework import routers
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from django.views.generic import RedirectView from django.views.generic import RedirectView
from django.conf import settings from django.conf import settings
from cvat.apps.restrictions.views import RestrictionsViewSet from cvat.apps.restrictions.views import RestrictionsViewSet
from cvat.apps.iam.decorators import login_required
schema_view = get_schema_view(
openapi.Info(
title="CVAT REST API",
default_version='v1',
description="REST API for Computer Vision Annotation Tool (CVAT)",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="nikita.manovich@intel.com"),
license=openapi.License(name="MIT License"),
),
public=True,
permission_classes=(permissions.IsAuthenticated,),
)
# drf-yasg component doesn't handle correctly URL_FORMAT_OVERRIDE and
# send requests with ?format=openapi suffix instead of ?scheme=openapi.
# We map the required parameter explicitly and add it into query arguments
# on the server side.
def wrap_swagger(view):
@login_required
def _map_format_to_schema(request, scheme=None):
if 'format' in request.GET:
request.GET = request.GET.copy()
format_alias = settings.REST_FRAMEWORK['URL_FORMAT_OVERRIDE']
request.GET[format_alias] = request.GET['format']
return view(request, format=scheme)
return _map_format_to_schema from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
router = routers.DefaultRouter(trailing_slash=False) router = routers.DefaultRouter(trailing_slash=False)
router.register('projects', views.ProjectViewSet) router.register('projects', views.ProjectViewSet)
@ -60,12 +30,9 @@ urlpatterns = [
query_string=True)), query_string=True)),
# documentation for API # documentation for API
path('api/swagger<str:scheme>', wrap_swagger( path('api/schema/', SpectacularAPIView.as_view(api_version='2.0'), name='schema'),
schema_view.without_ui(cache_timeout=0)), name='schema-json'), path('api/swagger/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger'),
path('api/swagger/', wrap_swagger( path('api/docs/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
schema_view.with_ui('swagger', cache_timeout=0)), name='schema-swagger-ui'),
path('api/docs/', wrap_swagger(
schema_view.with_ui('redoc', cache_timeout=0)), name='schema-redoc'),
# entry point for API # entry point for API
path('api/', include('cvat.apps.iam.urls')), path('api/', include('cvat.apps.iam.urls')),

File diff suppressed because it is too large Load Diff

@ -2,3 +2,4 @@
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
from .schema import *

@ -6,12 +6,18 @@ import coreapi
from rest_framework.filters import BaseFilterBackend from rest_framework.filters import BaseFilterBackend
class OrganizationFilterBackend(BaseFilterBackend): class OrganizationFilterBackend(BaseFilterBackend):
organization_slug = 'org'
organization_slug_description = 'Organization unique slug'
organization_id = 'org_id'
organization_id_description = 'Organization identifier'
def get_schema_fields(self, view): def get_schema_fields(self, view):
return [ return [
coreapi.Field(name='org', location='query', required=False, # NOTE: in coreapi.Field 'type', 'description' and 'example' are now deprecated, in favor of 'schema'.
type='string', description='Organization unique slug'), coreapi.Field(name=self.organization_slug, location='query', required=False,
coreapi.Field(name='org_id', location='query', required=False, type='string', description=self.organization_slug_description),
type='string', description='Organization identifier'), coreapi.Field(name=self.organization_id, location='query', required=False,
type='string', description=self.organization_id_description),
] ]
def filter_queryset(self, request, queryset, view): def filter_queryset(self, request, queryset, view):

@ -0,0 +1,44 @@
# Copyright (C) 2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
from drf_spectacular.extensions import OpenApiFilterExtension, OpenApiAuthenticationExtension
from drf_spectacular.plumbing import build_parameter_type
from drf_spectacular.utils import OpenApiParameter
# https://drf-spectacular.readthedocs.io/en/latest/customization.html?highlight=OpenApiFilterExtension#step-5-extensions
class OrganizationFilterExtension(OpenApiFilterExtension):
"""Describe OrganizationFilterBackend filter"""
target_class = 'cvat.apps.iam.filters.OrganizationFilterBackend'
priority = 1
def get_schema_operation_parameters(self, auto_schema, *args, **kwargs):
"""Describe query parameters"""
return [
build_parameter_type(
name=self.target.organization_slug,
required=False,
location=OpenApiParameter.QUERY,
description=self.target.organization_slug_description,
schema={'type': 'string'},
),
build_parameter_type(
name=self.target.organization_id,
required=False,
location=OpenApiParameter.QUERY,
description=self.target.organization_id_description,
schema={'type': 'string'},
)
]
class SignatureAuthenticationScheme(OpenApiAuthenticationExtension):
target_class = 'cvat.apps.iam.authentication.SignatureAuthentication'
name = 'SignatureAuthentication' # name used in the schema
def get_security_definition(self, auto_schema):
return {
'type': 'apiKey',
'in': 'query',
'name': 'sign',
}

@ -4,7 +4,7 @@
from django.core.exceptions import BadRequest from django.core.exceptions import BadRequest
from django.utils.functional import SimpleLazyObject from django.utils.functional import SimpleLazyObject
from rest_framework import views from rest_framework import views, serializers
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from django.conf import settings from django.conf import settings
from rest_framework.response import Response from rest_framework.response import Response
@ -12,9 +12,9 @@ from rest_auth.registration.views import RegisterView
from allauth.account import app_settings as allauth_settings from allauth.account import app_settings as allauth_settings
from furl import furl from furl import furl
from django.utils.decorators import method_decorator from drf_spectacular.types import OpenApiTypes
from drf_yasg.utils import swagger_auto_schema from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer, extend_schema_view
from drf_yasg import openapi
from .authentication import Signer from .authentication import Signer
@ -79,25 +79,19 @@ class ContextMiddleware:
return self.get_response(request) return self.get_response(request)
@method_decorator(name='post', decorator=swagger_auto_schema( @extend_schema_view(post=extend_schema(
request_body=openapi.Schema( summary='This method signs URL for access to the server',
type=openapi.TYPE_OBJECT, description='Signed URL contains a token which authenticates a user on the server.'
required=[ 'Signed URL is valid during 30 seconds since signing.',
'url' request=inline_serializer(
], name='Signing',
properties={ fields={
'url': openapi.Schema(type=openapi.TYPE_STRING) 'url': serializers.CharField(),
} }
), ),
responses={'200': openapi.Response(description='text URL')} responses={'200': OpenApiResponse(response=OpenApiTypes.STR, description='text URL')}, tags=['auth'], versions=['2.0']))
))
class SigningView(views.APIView): class SigningView(views.APIView):
"""
This method signs URL for access to the server.
Signed URL contains a token which authenticates a user on the server.
Signed URL is valid during 30 seconds since signing.
"""
def post(self, request): def post(self, request):
url = request.data.get('url') url = request.data.get('url')
if not url: if not url:

@ -25,5 +25,5 @@ router.register('requests', views.RequestViewSet, basename='request')
# GET /api/lambda/requests/<int:rid> - get status of the request # GET /api/lambda/requests/<int:rid> - get status of the request
# DEL /api/lambda/requests/<int:rid> - cancel a request (don't delete) # DEL /api/lambda/requests/<int:rid> - cancel a request (don't delete)
urlpatterns = [ urlpatterns = [
path('api/lambda/', include((router.urls, 'cvat'), namespace='v1')) path('api/lambda/', include(router.urls))
] ]

@ -17,6 +17,9 @@ from cvat.apps.engine.models import Task as TaskModel
from cvat.apps.engine.serializers import LabeledDataSerializer from cvat.apps.engine.serializers import LabeledDataSerializer
from cvat.apps.engine.models import ShapeType, SourceType from cvat.apps.engine.models import ShapeType, SourceType
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse, OpenApiParameter
from drf_spectacular.types import OpenApiTypes
class LambdaType(Enum): class LambdaType(Enum):
DETECTOR = "detector" DETECTOR = "detector"
INTERACTOR = "interactor" INTERACTOR = "interactor"
@ -228,7 +231,6 @@ class LambdaFunction:
return base64.b64encode(image[0].getvalue()).decode('utf-8') return base64.b64encode(image[0].getvalue()).decode('utf-8')
class LambdaQueue: class LambdaQueue:
def _get_queue(self): def _get_queue(self):
QUEUE_NAME = "low" QUEUE_NAME = "low"
@ -286,7 +288,6 @@ class LambdaQueue:
return LambdaJob(job) return LambdaJob(job)
class LambdaJob: class LambdaJob:
def __init__(self, job): def __init__(self, job):
self.job = job self.job = job
@ -552,10 +553,19 @@ def return_response(success_code=status.HTTP_200_OK):
return func_wrapper return func_wrapper
return wrap_response return wrap_response
@extend_schema_view(retrieve=extend_schema(
summary='Method returns the information about the function',
responses={
'200': OpenApiResponse(response=OpenApiTypes.OBJECT, description='Information about the function'),
},
tags=['lambda'], versions=['2.0']))
@extend_schema_view(list=extend_schema(
summary='Method returns a list of functions', tags=['lambda'], versions=['2.0']))
class FunctionViewSet(viewsets.ViewSet): class FunctionViewSet(viewsets.ViewSet):
lookup_value_regex = '[a-zA-Z0-9_.-]+' lookup_value_regex = '[a-zA-Z0-9_.-]+'
lookup_field = 'func_id' lookup_field = 'func_id'
iam_organization_field = None iam_organization_field = None
serializer_class = None
@return_response() @return_response()
def list(self, request): def list(self, request):
@ -585,8 +595,24 @@ class FunctionViewSet(viewsets.ViewSet):
return lambda_func.invoke(db_task, request.data) return lambda_func.invoke(db_task, request.data)
@extend_schema_view(retrieve=extend_schema(
summary='Method returns the status of the request',
parameters=[
# specify correct type
OpenApiParameter('id', location=OpenApiParameter.PATH, type=OpenApiTypes.INT,
description='Request id'),
],
tags=['lambda'], versions=['2.0']))
@extend_schema_view(list=extend_schema(
summary='Method returns a list of requests', tags=['lambda'], versions=['2.0']))
#TODO
@extend_schema_view(create=extend_schema(
summary='Method calls the function', tags=['lambda'], versions=['2.0']))
@extend_schema_view(delete=extend_schema(
summary='Method cancels the request', tags=['lambda'], versions=['2.0']))
class RequestViewSet(viewsets.ViewSet): class RequestViewSet(viewsets.ViewSet):
iam_organization_field = None iam_organization_field = None
serializer_class = None
@return_response() @return_response()
def list(self, request): def list(self, request):

@ -10,4 +10,4 @@ router.register('organizations', OrganizationViewSet)
router.register('invitations', InvitationViewSet) router.register('invitations', InvitationViewSet)
router.register('memberships', MembershipViewSet) router.register('memberships', MembershipViewSet)
urlpatterns = router.urls urlpatterns = router.urls

@ -7,6 +7,8 @@ from rest_framework.permissions import SAFE_METHODS
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django_filters import rest_framework as filters from django_filters import rest_framework as filters
from drf_spectacular.utils import OpenApiResponse, extend_schema, extend_schema_view
from cvat.apps.iam.permissions import ( from cvat.apps.iam.permissions import (
InvitationPermission, MembershipPermission, OrganizationPermission) InvitationPermission, MembershipPermission, OrganizationPermission)
from .models import Invitation, Membership, Organization from .models import Invitation, Membership, Organization
@ -16,6 +18,37 @@ from .serializers import (
MembershipReadSerializer, MembershipWriteSerializer, MembershipReadSerializer, MembershipWriteSerializer,
OrganizationReadSerializer, OrganizationWriteSerializer) OrganizationReadSerializer, OrganizationWriteSerializer)
@extend_schema_view(retrieve=extend_schema(
summary='Method returns details of an organization',
responses={
'200': OrganizationReadSerializer,
}, tags=['organizations'], versions=['2.0']))
@extend_schema_view(list=extend_schema(
summary='Method returns a paginated list of organizatins according to query parameters',
responses={
'200': OrganizationReadSerializer(many=True),
}, tags=['organizations'], versions=['2.0']))
@extend_schema_view(update=extend_schema(
summary='Method updates an organization by id',
responses={
'200': OrganizationWriteSerializer,
}, tags=['organizations'], versions=['2.0']))
@extend_schema_view(partial_update=extend_schema(
summary='Methods does a partial update of chosen fields in an organization',
responses={
'200': OrganizationWriteSerializer,
}, tags=['organizations'], versions=['2.0']))
@extend_schema_view(create=extend_schema(
summary='Method creates an organization',
responses={
'201': OrganizationWriteSerializer,
}, tags=['organizations'], versions=['2.0']))
@extend_schema_view(destroy=extend_schema(
summary='Method deletes an organization',
responses={
'204': OpenApiResponse(description='The organization has been deleted'),
}, tags=['organizations'], versions=['2.0']))
class OrganizationViewSet(viewsets.ModelViewSet): class OrganizationViewSet(viewsets.ModelViewSet):
queryset = Organization.objects.all() queryset = Organization.objects.all()
ordering = ['-id'] ordering = ['-id']
@ -46,7 +79,31 @@ class MembershipFilter(filters.FilterSet):
class Meta: class Meta:
model = Membership model = Membership
fields = ("user", ) fields = ("user", )
@extend_schema_view(retrieve=extend_schema(
summary='Method returns details of a membership',
responses={
'200': MembershipReadSerializer,
}, tags=['memberships'], versions=['2.0']))
@extend_schema_view(list=extend_schema(
summary='Method returns a paginated list of memberships according to query parameters',
responses={
'200': MembershipReadSerializer(many=True),
}, tags=['memberships'], versions=['2.0']))
@extend_schema_view(update=extend_schema(
summary='Method updates a membership by id',
responses={
'200': MembershipWriteSerializer,
}, tags=['memberships'], versions=['2.0']))
@extend_schema_view(partial_update=extend_schema(
summary='Methods does a partial update of chosen fields in a membership',
responses={
'200': MembershipWriteSerializer,
}, tags=['memberships'], versions=['2.0']))
@extend_schema_view(destroy=extend_schema(
summary='Method deletes a membership',
responses={
'204': OpenApiResponse(description='The membership has been deleted'),
}, tags=['memberships'], versions=['2.0']))
class MembershipViewSet(mixins.RetrieveModelMixin, mixins.DestroyModelMixin, class MembershipViewSet(mixins.RetrieveModelMixin, mixins.DestroyModelMixin,
mixins.ListModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet): mixins.ListModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet):
queryset = Membership.objects.all() queryset = Membership.objects.all()
@ -66,6 +123,37 @@ class MembershipViewSet(mixins.RetrieveModelMixin, mixins.DestroyModelMixin,
permission = MembershipPermission(self.request, self) permission = MembershipPermission(self.request, self)
return permission.filter(queryset) return permission.filter(queryset)
# TODO
@extend_schema_view(retrieve=extend_schema(
summary='Method returns details of an invitation',
responses={
'200': InvitationReadSerializer,
}, tags=['invitations'], versions=['2.0']))
@extend_schema_view(list=extend_schema(
summary='Method returns a paginated list of invitations according to query parameters',
responses={
'200': InvitationReadSerializer(many=True),
}, tags=['invitations'], versions=['2.0']))
@extend_schema_view(update=extend_schema(
summary='Method updates an invitation by id',
responses={
'200': InvitationWriteSerializer,
}, tags=['invitations'], versions=['2.0']))
@extend_schema_view(partial_update=extend_schema(
summary='Methods does a partial update of chosen fields in an invitation',
responses={
'200': InvitationWriteSerializer,
}, tags=['invitations'], versions=['2.0']))
@extend_schema_view(create=extend_schema(
summary='Method creates an invitation',
responses={
'201': InvitationWriteSerializer,
}, tags=['invitations'], versions=['2.0']))
@extend_schema_view(destroy=extend_schema(
summary='Method deletes an invitation',
responses={
'204': OpenApiResponse(description='The invitation has been deleted'),
}, tags=['invitations'], versions=['2.0']))
class InvitationViewSet(viewsets.ModelViewSet): class InvitationViewSet(viewsets.ModelViewSet):
queryset = Invitation.objects.all() queryset = Invitation.objects.all()
ordering = ['-created_date'] ordering = ['-created_date']

@ -8,7 +8,8 @@ from rest_framework.decorators import action
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.renderers import TemplateHTMLRenderer from rest_framework.renderers import TemplateHTMLRenderer
from drf_yasg.utils import swagger_auto_schema from drf_spectacular.utils import OpenApiResponse, extend_schema
from cvat.apps.restrictions.serializers import UserAgreementSerializer from cvat.apps.restrictions.serializers import UserAgreementSerializer
@ -24,10 +25,9 @@ class RestrictionsViewSet(viewsets.ViewSet):
pass pass
@staticmethod @staticmethod
@swagger_auto_schema( @extend_schema(summary='Method provides user agreements that the user must accept to register',
method='get', responses={'200': UserAgreementSerializer},
operation_summary='Method provides user agreements that the user must accept to register', tags=['restrictions'], versions=['2.0'])
responses={'200': UserAgreementSerializer})
@action(detail=False, methods=['GET'], serializer_class=UserAgreementSerializer, url_path='user-agreements') @action(detail=False, methods=['GET'], serializer_class=UserAgreementSerializer, url_path='user-agreements')
def user_agreements(request): def user_agreements(request):
user_agreements = settings.RESTRICTIONS['user_agreements'] user_agreements = settings.RESTRICTIONS['user_agreements']
@ -36,6 +36,9 @@ class RestrictionsViewSet(viewsets.ViewSet):
return Response(data=serializer.data) return Response(data=serializer.data)
@staticmethod @staticmethod
@extend_schema(summary='Method provides CVAT terms of use',
responses={'200': OpenApiResponse(description='CVAT terms of use')},
tags=['restrictions'], versions=['2.0'])
@action(detail=False, methods=['GET'], renderer_classes=(TemplateHTMLRenderer,), @action(detail=False, methods=['GET'], renderer_classes=(TemplateHTMLRenderer,),
url_path='terms-of-use') url_path='terms-of-use')
def terms_of_use(request): def terms_of_use(request):

@ -28,7 +28,7 @@ django-filter==2.4.0
Markdown==3.2.2 Markdown==3.2.2
djangorestframework==3.12.4 djangorestframework==3.12.4
Pygments==2.7.4 Pygments==2.7.4
drf-yasg==1.20.0 drf-spectacular==0.21.2
Shapely==1.7.1 Shapely==1.7.1
pdf2image==1.14.0 pdf2image==1.14.0
django-rest-auth[with_social]==0.9.5 django-rest-auth[with_social]==0.9.5

@ -112,7 +112,7 @@ INSTALLED_APPS = [
'rest_framework', 'rest_framework',
'rest_framework.authtoken', 'rest_framework.authtoken',
'django_filters', 'django_filters',
'drf_yasg', 'drf_spectacular',
'rest_auth', 'rest_auth',
'django.contrib.sites', 'django.contrib.sites',
'allauth', 'allauth',
@ -177,6 +177,7 @@ REST_FRAMEWORK = {
'anon': '100/minute', 'anon': '100/minute',
}, },
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata', 'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
} }
REST_AUTH_REGISTER_SERIALIZERS = { REST_AUTH_REGISTER_SERIALIZERS = {
@ -492,7 +493,44 @@ TUS_DEFAULT_CHUNK_SIZE = 104857600 # 100 mb
# How django uses X-Forwarded-Proto - https://docs.djangoproject.com/en/2.2/ref/settings/#secure-proxy-ssl-header # How django uses X-Forwarded-Proto - https://docs.djangoproject.com/en/2.2/ref/settings/#secure-proxy-ssl-header
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Django-sendfile requires to set SENDFILE_ROOT # Django-sendfile requires to set SENDFILE_ROOT
# https://github.com/moggers87/django-sendfile2 # https://github.com/moggers87/django-sendfile2
SENDFILE_ROOT = BASE_DIR SENDFILE_ROOT = BASE_DIR
SPECTACULAR_SETTINGS = {
'TITLE': 'CVAT REST API',
'DESCRIPTION': 'REST API for Computer Vision Annotation Tool (CVAT)',
# Statically set schema version. May also be an empty string. When used together with
# view versioning, will become '0.0.0 (v2)' for 'v2' versioned requests.
# Set VERSION to None if only the request version should be rendered.
'VERSION': None,
'CONTACT': {
'name': 'Nikita Manovich',
'url': 'https://github.com/nmanovic',
'email': 'nikita.manovich@intel.com',
},
'LICENSE': {
'name': 'MIT License',
'url': 'https://en.wikipedia.org/wiki/MIT_License',
},
'SERVE_PUBLIC': True,
'SCHEMA_COERCE_PATH_PK_SUFFIX': True,
'SCHEMA_PATH_PREFIX': '/api',
'SCHEMA_PATH_PREFIX_TRIM': False,
'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAuthenticated'],
# https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/
'SWAGGER_UI_SETTINGS': {
'deepLinking': True,
'displayOperationId': True,
'displayRequestDuration': True,
'filter': True,
'showExtensions': True,
},
'TOS': 'https://www.google.com/policies/terms/',
'EXTERNAL_DOCS': {
'description': 'CVAT documentation',
'url': 'https://openvinotoolkit.github.io/cvat/docs/',
},
# OTHER SETTINGS
# https://drf-spectacular.readthedocs.io/en/latest/settings.html
}

Loading…
Cancel
Save