diff --git a/geonode/api/urls.py b/geonode/api/urls.py index 984a2225294..c63a6c9c72f 100644 --- a/geonode/api/urls.py +++ b/geonode/api/urls.py @@ -18,7 +18,6 @@ ######################################################################### from tastypie.api import Api from dynamic_rest import routers -from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView from django.urls import path @@ -48,8 +47,5 @@ router = routers.DynamicRouter() urlpatterns = [ - path("schema/", SpectacularAPIView.as_view(), name="schema"), - path("schema/swagger-ui/", SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui"), - path("schema/redoc/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"), path("userinfo/", UserInfoView.as_view(), name="userinfo"), ] diff --git a/geonode/base/api/views.py b/geonode/base/api/views.py index 9b02091bbed..953c8724efa 100644 --- a/geonode/base/api/views.py +++ b/geonode/base/api/views.py @@ -37,7 +37,6 @@ from django.contrib.auth import get_user_model from django.http import HttpResponse -from drf_spectacular.utils import extend_schema from dynamic_rest.viewsets import DynamicModelViewSet, WithDynamicViewSetMixin from dynamic_rest.filters import DynamicFilterBackend, DynamicSortingFilter @@ -98,10 +97,8 @@ from .serializers import ( FavoriteSerializer, - PermSpecSerialiazer, GroupProfileSerializer, ResourceBaseSerializer, - ResourceBaseTypesSerializer, OwnerSerializer, HierarchicalKeywordSerializer, TopicCategorySerializer, @@ -145,35 +142,23 @@ def get_queryset(self): queryset = GroupProfile.objects.filter(id__in=[_g.id for _g in metadata_author_groups]) return queryset.order_by("title") - @extend_schema( - methods=["get"], - responses={200: UserSerializer(many=True)}, - description="API endpoint allowing to retrieve the Group members.", - ) @action(detail=True, methods=["get"]) def members(self, request, pk=None): + """API endpoint allowing to retrieve the Group members.""" group = self.get_object() members = get_user_model().objects.filter(id__in=group.member_queryset().values_list("user", flat=True)) return Response(UserSerializer(embed=True, many=True).to_representation(members)) - @extend_schema( - methods=["get"], - responses={200: UserSerializer(many=True)}, - description="API endpoint allowing to retrieve the Group managers.", - ) @action(detail=True, methods=["get"]) def managers(self, request, pk=None): + """API endpoint allowing to retrieve the Group managers.""" group = self.get_object() managers = group.get_managers() return Response(UserSerializer(embed=True, many=True).to_representation(managers)) - @extend_schema( - methods=["get"], - responses={200: ResourceBaseSerializer(many=True)}, - description="API endpoint allowing to retrieve the Group specific resources.", - ) @action(detail=True, methods=["get"]) def resources(self, request, pk=None): + """API endpoint allowing to retrieve the Group specific resources.""" group = self.get_object() resources = group.resources() paginator = GeoNodeApiPagination() @@ -335,38 +320,21 @@ def _filtered(self, request, filter): serializer = ResourceBaseSerializer(result_page, embed=True, many=True) return paginator.get_paginated_response({"resources": serializer.data}) - @extend_schema( - methods=["get"], - responses={200: ResourceBaseSerializer(many=True)}, - description="API endpoint allowing to retrieve the approved Resources.", - ) @action(detail=False, methods=["get"]) def approved(self, request, *args, **kwargs): + """API endpoint allowing to retrieve the approved Resources.""" return self._filtered(request, {"is_approved": True}) - @extend_schema( - methods=["get"], - responses={200: ResourceBaseSerializer(many=True)}, - description="API endpoint allowing to retrieve the published Resources.", - ) @action(detail=False, methods=["get"]) def published(self, request, *args, **kwargs): + """API endpoint allowing to retrieve the published Resources.""" return self._filtered(request, {"is_published": True}) - @extend_schema( - methods=["get"], - responses={200: ResourceBaseSerializer(many=True)}, - description="API endpoint allowing to retrieve the featured Resources.", - ) @action(detail=False, methods=["get"]) def featured(self, request, *args, **kwargs): + """API endpoint allowing to retrieve the featured Resources.""" return self._filtered(request, {"featured": True}) - @extend_schema( - methods=["get"], - responses={200: FavoriteSerializer(many=True)}, - description="API endpoint allowing to retrieve the favorite Resources.", - ) @action( detail=False, methods=["get"], @@ -375,6 +343,7 @@ def featured(self, request, *args, **kwargs): ], ) def favorites(self, request, pk=None, *args, **kwargs): + """API endpoint allowing to retrieve the favorite Resources.""" paginator = GeoNodeApiPagination() paginator.page_size = request.GET.get("page_size", 10) favorites = Favorite.objects.favorites_for_user(user=request.user) @@ -382,13 +351,9 @@ def favorites(self, request, pk=None, *args, **kwargs): serializer = FavoriteSerializer(result_page, embed=True, many=True) return paginator.get_paginated_response({"favorites": serializer.data}) - @extend_schema( - methods=["post", "delete"], - responses={200: FavoriteSerializer(many=True)}, - description="API endpoint allowing to retrieve the favorite Resources.", - ) @action(detail=True, methods=["post", "delete"], permission_classes=[IsAuthenticated]) def favorite(self, request, pk=None, *args, **kwargs): + """API endpoint allowing to add or remove a resource from favorites.""" resource = self.get_object() user = request.user @@ -407,10 +372,9 @@ def favorite(self, request, pk=None, *args, **kwargs): except Favorite.DoesNotExist: return Response({"message": "Resource not in favorites"}, status=404) - @extend_schema( - methods=["get"], - responses={200: ResourceBaseTypesSerializer()}, - description=""" + @action(detail=False, methods=["get"]) + def resource_types(self, request, *args, **kwargs): + """ Returns the list of available ResourceBase polymorphic_ctypes. the mapping looks like: @@ -436,10 +400,8 @@ def favorite(self, request, pk=None, *args, **kwargs): ] } ``` - """, - ) - @action(detail=False, methods=["get"]) - def resource_types(self, request, *args, **kwargs): + """ + def _to_compact_perms_list( allowed_perms: dict, resource_type: str, resource_subtype: str, compact_perms_labels: dict = {} ) -> list: @@ -514,11 +476,15 @@ def _to_compact_perms_list( ) return Response({"resource_types": resource_types}) - @extend_schema( + @action( + detail=True, + url_path="permissions", # noqa + url_name="perms-spec", methods=["get", "put", "patch", "delete"], - request=PermSpecSerialiazer(), - responses={200: None}, - description=""" + permission_classes=[IsAuthenticated], + ) + def resource_service_permissions(self, request, pk, *args, **kwargs): + """ Sets an object's the permission levels based on the perm_spec JSON. the mapping looks like: @@ -537,17 +503,8 @@ def _to_compact_perms_list( } } ``` - """, - ) - @action( - detail=True, - url_path="permissions", # noqa - url_name="perms-spec", - methods=["get", "put", "patch", "delete"], - permission_classes=[IsAuthenticated], - ) - def resource_service_permissions(self, request, pk, *args, **kwargs): - """Instructs the Async dispatcher to execute a 'DELETE' or 'UPDATE' on the permissions of a valid 'uuid' + + Instructs the Async dispatcher to execute a 'DELETE' or 'UPDATE' on the permissions of a valid 'uuid' - GET input_params: { id: "" @@ -673,11 +630,6 @@ def resource_service_permissions(self, request, pk, *args, **kwargs): logger.exception(e) return Response(status=status.HTTP_400_BAD_REQUEST, exception=e) - @extend_schema( - methods=["post"], - responses={200}, - description="API endpoint allowing to set the thumbnail url for an existing dataset.", - ) @action( detail=False, url_path=r"(?P\d+)/set_thumbnail_from_bbox", @@ -686,6 +638,7 @@ def resource_service_permissions(self, request, pk, *args, **kwargs): permission_classes=[IsAuthenticated, UserHasPerms(perms_dict={"default": {"POST": ["base.add_resourcebase"]}})], ) def set_thumbnail_from_bbox(self, request, resource_id, *args, **kwargs): + """API endpoint allowing to set the thumbnail url for an existing dataset.""" import traceback from django.utils.datastructures import MultiValueDictKeyError @@ -747,11 +700,6 @@ def set_thumbnail_from_bbox(self, request, resource_id, *args, **kwargs): logger.error(e) return Response(data={"message": e.args[0], "success": False}, status=500, exception=True) - @extend_schema( - methods=["post"], - responses={200}, - description="API endpoint allowing to delete a thumbnail for an existing dataset.", - ) @action( detail=False, url_path=r"(?P\d+)/delete_thumbnail", @@ -760,7 +708,7 @@ def set_thumbnail_from_bbox(self, request, resource_id, *args, **kwargs): permission_classes=[IsAuthenticated, UserHasPerms(perms_dict={"default": {"POST": ["base.add_resourcebase"]}})], ) def delete_thumbnail(self, request, resource_id, *args, **kwargs): - + """API endpoint allowing to delete a thumbnail for an existing dataset.""" try: resource = ResourceBase.objects.get(id=int(resource_id)) @@ -805,9 +753,6 @@ def delete_thumbnail(self, request, resource_id, *args, **kwargs): status=status.HTTP_500_INTERNAL_SERVER_ERROR, ) - @extend_schema( - methods=["post"], responses={200}, description="Instructs the Async dispatcher to execute a 'CREATE' operation." - ) @action( detail=False, url_path=r"create/(?P\w+)", @@ -904,11 +849,6 @@ def resource_service_create(self, request, resource_type: str = None, *args, **k logger.exception(e) return Response(status=status.HTTP_400_BAD_REQUEST, exception=e) - @extend_schema( - methods=["delete"], - responses={200}, - description="Instructs the Async dispatcher to execute a 'DELETE' operation over a valid 'uuid'.", - ) @action( detail=True, url_path="delete", # noqa @@ -988,11 +928,6 @@ def resource_service_delete(self, request, pk, *args, **kwargs): logger.exception(e) return Response(status=status.HTTP_400_BAD_REQUEST, exception=e) - @extend_schema( - methods=["put"], - responses={200}, - description="Instructs the Async dispatcher to execute a 'UPDATE' operation over a valid 'uuid'.", - ) @action( detail=True, url_path="update", # noqa @@ -1109,11 +1044,6 @@ def resource_service_update(self, request, pk, *args, **kwargs): logger.exception(e) return Response(status=status.HTTP_400_BAD_REQUEST, exception=e) - @extend_schema( - methods=["put"], - responses={200}, - description="Instructs the Async dispatcher to execute a 'COPY' operation over a valid 'uuid'.", - ) @action( detail=True, url_path="copy", # noqa @@ -1221,9 +1151,6 @@ def resource_service_copy(self, request, pk, *args, **kwargs): logger.exception(e) return Response(status=status.HTTP_400_BAD_REQUEST, exception=e) - @extend_schema( - methods=["put"], responses={200}, description="API endpoint allowing to set thumbnail of the Resource." - ) @action( detail=True, url_path="set_thumbnail", @@ -1233,6 +1160,7 @@ def resource_service_copy(self, request, pk, *args, **kwargs): parser_classes=[JSONParser, MultiPartParser], ) def set_thumbnail(self, request, pk, *args, **kwargs): + """API endpoint allowing to set thumbnail of the Resource.""" resource = get_object_or_404(ResourceBase, pk=pk) if not request.data.get("file"): @@ -1284,9 +1212,6 @@ def set_thumbnail(self, request, pk, *args, **kwargs): return Response({"thumbnail_url": resource.thumbnail_url}) return Response("Unable to set thumbnail", status=status.HTTP_400_BAD_REQUEST) - @extend_schema( - methods=["get", "put", "delete", "post"], description="Get/Update/Delete/Add extra metadata for resource" - ) @action( detail=True, methods=["get", "put", "delete", "post"], @@ -1295,6 +1220,7 @@ def set_thumbnail(self, request, pk, *args, **kwargs): url_name="extra-metadata", ) def extra_metadata(self, request, pk, *args, **kwargs): + """Get/Update/Delete/Add extra metadata for resource""" _obj = get_object_or_404(ResourceBase, pk=pk) if request.method == "GET": @@ -1396,7 +1322,6 @@ def _get_request_params(self, request, encode=False): logger.debug(e) return request.data - @extend_schema(methods=["get", "post", "delete"], description="Get Linked Resources") @action( detail=True, methods=["get", "post", "delete"], @@ -1405,6 +1330,7 @@ def _get_request_params(self, request, encode=False): url_name="linked_resources", ) def linked_resources(self, request, pk, *args, **kwargs): + """Get Linked Resources""" resource = self.get_object() if request.method in ("POST", "DELETE"): success_var = [] diff --git a/geonode/documents/api/views.py b/geonode/documents/api/views.py index 5d6d139f28f..48128764e40 100644 --- a/geonode/documents/api/views.py +++ b/geonode/documents/api/views.py @@ -17,7 +17,6 @@ # ######################################################################### -from drf_spectacular.utils import extend_schema from pathlib import Path from uuid import uuid4 from dynamic_rest.viewsets import DynamicModelViewSet @@ -30,7 +29,6 @@ from geonode.base.api.filters import DynamicSearchFilter, ExtentFilter, AdvertisedFilter from geonode.base.api.pagination import GeoNodeApiPagination from geonode.base.api.permissions import UserHasPerms -from geonode.base.api.serializers import ResourceBaseSerializer from geonode.base.api.views import base_linked_resources, ApiPresetsInitializer from geonode.base import enumerations from geonode.documents.api.exceptions import DocumentException @@ -133,11 +131,7 @@ def perform_create(self, serializer): logger.error(f"Error creating document {serializer.validated_data}", exc_info=e) raise e - @extend_schema( - methods=["get"], - responses={200: ResourceBaseSerializer(many=True)}, - description="API endpoint allowing to retrieve linked resources", - ) @action(detail=True, methods=["get"]) def linked_resources(self, request, pk=None, *args, **kwargs): + """API endpoint allowing to retrieve linked resources""" return base_linked_resources(self.get_object().get_real_instance(), request.user, request.GET) diff --git a/geonode/layers/api/views.py b/geonode/layers/api/views.py index f29777bd7de..31b3e44fd5e 100644 --- a/geonode/layers/api/views.py +++ b/geonode/layers/api/views.py @@ -16,8 +16,6 @@ # along with this program. If not, see . # ######################################################################### -from drf_spectacular.utils import extend_schema - from dynamic_rest.viewsets import DynamicModelViewSet from dynamic_rest.filters import DynamicFilterBackend, DynamicSortingFilter @@ -101,12 +99,6 @@ def partial_update(self, request, *args, **kwargs): return result - @extend_schema( - request=DatasetMetadataSerializer, - methods=["put"], - responses={200}, - description="API endpoint to upload metadata file.", - ) @action( detail=False, url_path=r"(?P\d+)/metadata", @@ -177,24 +169,16 @@ def metadata(self, request, pk=None, *args, **kwargs): if storage_manager: storage_manager.delete_retrieved_paths() - @extend_schema( - methods=["get"], - responses={200: SimpleMapLayerSerializer(many=True)}, - description="API endpoint allowing to retrieve the MapLayers list.", - ) @action(detail=True, methods=["get"]) def maplayers(self, request, pk=None, *args, **kwargs): + """API endpoint allowing to retrieve the MapLayers list.""" dataset = self.get_object() resources = dataset.maplayers return Response(SimpleMapLayerSerializer(many=True).to_representation(resources)) - @extend_schema( - methods=["get"], - responses={200: SimpleMapSerializer(many=True)}, - description="API endpoint allowing to retrieve maps using the dataset.", - ) @action(detail=True, methods=["get"]) def maps(self, request, pk=None, *args, **kwargs): + """API endpoint allowing to retrieve maps using the dataset.""" dataset = self.get_object() resources = dataset.maps return Response(SimpleMapSerializer(many=True).to_representation(resources)) diff --git a/geonode/maps/api/views.py b/geonode/maps/api/views.py index c3c69879f0b..0927f2bb3a3 100644 --- a/geonode/maps/api/views.py +++ b/geonode/maps/api/views.py @@ -20,7 +20,6 @@ from uuid import uuid4 from django.db import transaction -from drf_spectacular.utils import extend_schema from dynamic_rest.filters import DynamicFilterBackend, DynamicSortingFilter from dynamic_rest.viewsets import DynamicModelViewSet from rest_framework.decorators import action @@ -89,24 +88,16 @@ def create(self, request, *args, **kwargs): """ return super(MapViewSet, self).create(request, *args, **kwargs) - @extend_schema( - methods=["get"], - responses={200: MapLayerSerializer(many=True)}, - description="API endpoint allowing to retrieve the MapLayers list.", - ) @action(detail=True, methods=["get"]) def maplayers(self, request, pk=None, *args, **kwargs): + """API endpoint allowing to retrieve the MapLayers list.""" map = self.get_object() resources = map.maplayers return Response(MapLayerSerializer(embed=True, many=True).to_representation(resources)) - @extend_schema( - methods=["get"], - responses={200: DatasetSerializer(many=True)}, - description="API endpoint allowing to retrieve the local MapLayers.", - ) @action(detail=True, methods=["get"]) def datasets(self, request, pk=None, *args, **kwargs): + """API endpoint allowing to retrieve the local MapLayers.""" map = self.get_object() resources = map.datasets return Response(DatasetSerializer(embed=True, many=True).to_representation(resources)) diff --git a/geonode/people/api/views.py b/geonode/people/api/views.py index f1425420d1c..2cf48e35cc8 100644 --- a/geonode/people/api/views.py +++ b/geonode/people/api/views.py @@ -20,7 +20,6 @@ import logging from django.conf import settings from dynamic_rest.filters import DynamicFilterBackend, DynamicSortingFilter -from drf_spectacular.utils import extend_schema from dynamic_rest.viewsets import DynamicModelViewSet from rest_framework.response import Response from rest_framework.decorators import action @@ -98,13 +97,9 @@ def perform_destroy(self, instance): raise PermissionDenied(f"One or more validation rules are violated: {errors}") instance.delete() - @extend_schema( - methods=["get"], - responses={200: ResourceBaseSerializer(many=True)}, - description="API endpoint allowing to retrieve the Resources visible to the user.", - ) @action(detail=True, methods=["get"]) def resources(self, request, pk=None): + """API endpoint allowing to retrieve the Resources visible to the user.""" user = self.get_object() permitted = get_objects_for_user(user, "base.view_resourcebase") qs = ResourceBase.objects.all().filter(id__in=permitted).order_by("title") @@ -123,13 +118,9 @@ def resources(self, request, pk=None): serializer = ResourceBaseSerializer(result_page, embed=True, many=True, context={"request": request}) return paginator.get_paginated_response({"resources": serializer.data}) - @extend_schema( - methods=["get"], - responses={200: GroupProfileSerializer(many=True)}, - description="API endpoint allowing to retrieve the Groups the user is member of.", - ) @action(detail=True, methods=["get"]) def groups(self, request, pk=None): + """API endpoint allowing to retrieve the Groups the user is member of.""" user = self.get_object() qs_ids = GroupMember.objects.filter(user=user).values_list("group", flat=True) groups = GroupProfile.objects.filter(id__in=qs_ids) diff --git a/geonode/settings.py b/geonode/settings.py index 6e4ddc83a69..c26a85731b1 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -45,8 +45,6 @@ "fields.W340", "auth.W004", "urls.W002", - "drf_spectacular.W001", - "drf_spectacular.W002", ] # GeoNode Version @@ -497,7 +495,6 @@ "rest_framework", "rest_framework_gis", "dynamic_rest", - "drf_spectacular", # Theme "django_select2", "django_forms_bootstrap", @@ -553,7 +550,6 @@ "rest_framework.renderers.JSONRenderer", "dynamic_rest.renderers.DynamicBrowsableAPIRenderer", ], - "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", "EXCEPTION_HANDLER": "geonode.base.api.exceptions.geonode_exception_handler", } REST_FRAMEWORK_EXTENSIONS = { diff --git a/pyproject.toml b/pyproject.toml index fd2b304aeb0..4721383134d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,7 +94,6 @@ dependencies = [ "djangorestframework-gis==1.2.0", "djangorestframework-guardian==0.3.0", "drf-extensions==0.7.1", - "drf-spectacular==0.27.2", "geonode-dynamic-rest==2.3.0.1", "geonode-pinax-notifications==6.0.0.3",