diff --git a/docs/content/releases/os_upgrading/2.56.md b/docs/content/releases/os_upgrading/2.56.md new file mode 100644 index 0000000000..59d3171af4 --- /dev/null +++ b/docs/content/releases/os_upgrading/2.56.md @@ -0,0 +1,27 @@ +--- +title: 'Upgrading to DefectDojo Version 2.56.x' +toc_hide: true +weight: -20260215 +description: Deprecation of Questionnaire API Endpoints +--- + +## Deprecation: Questionnaire API Endpoints + +The following Questionnaire API endpoints are being deprecated and will be removed in DefectDojo 2.59.0 on June 1st, 2026: + +- `/api/v2/questionnaire_answered_questionnaires/` +- `/api/v2/questionnaire_answers/` +- `/api/v2/questionnaire_engagement_questionnaires/` +- `/api/v2/questionnaire_general_questionnaires/` +- `/api/v2/questionnaire_questions` + +### Required Actions + +Support for these endpoints will be fully removed in DefectDojo 2.59.0 (scheduled for June 1st, 2026). After this date, any requests to these endpoints will return a 404 Not Found error. + +### Timeline + +- **DefectDojo 2.56.x onwards**: Endpoints are deprecated with deprecation headers +- **DefectDojo 2.59.0 (June 1st, 2026)**: Endpoints will be removed entirely + +For more information, check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/2.56.0). diff --git a/docs/content/releases/os_upgrading/2.59.md b/docs/content/releases/os_upgrading/2.59.md new file mode 100644 index 0000000000..d5bb68b3ab --- /dev/null +++ b/docs/content/releases/os_upgrading/2.59.md @@ -0,0 +1,22 @@ +--- +title: 'Upgrading to DefectDojo Version 2.59.x' +toc_hide: true +weight: -20260602 +description: Removal of Questionnaire API Endpoints +--- + +## Removal: Questionnaire API Endpoints + +As announced in DefectDojo 2.56.0, the following Questionnaire API endpoints have been removed: + +- `/api/v2/questionnaire_answered_questionnaires/` +- `/api/v2/questionnaire_answers/` +- `/api/v2/questionnaire_engagement_questionnaires/` +- `/api/v2/questionnaire_general_questionnaires/` +- `/api/v2/questionnaire_questions` + +### Required Actions + +Any requests to these endpoints will now return a 404 Not Found error. + +For more information, check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/2.59.0). diff --git a/dojo/api_v2/views.py b/dojo/api_v2/views.py index 15ba41e3d0..e8790df5b4 100644 --- a/dojo/api_v2/views.py +++ b/dojo/api_v2/views.py @@ -242,6 +242,19 @@ class PrefetchDojoModelViewSet( pass +class DeprecationNoticeMixin: + + deprecated: bool | None = None + end_of_life_date: datetime | None = None + + def finalize_response(self, request, response, *args, **kwargs): + if self.deprecated is not None: + response["X-Deprecated"] = self.deprecated + if self.end_of_life_date is not None: + response["X-End-Of-Life-Date"] = self.end_of_life_date.isoformat() + return super().finalize_response(request, response, *args, **kwargs) + + # Authorization: authenticated users class RoleViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = serializers.RoleSerializer @@ -3171,7 +3184,10 @@ def get_queryset(self): class QuestionnaireQuestionViewSet( viewsets.ReadOnlyModelViewSet, dojo_mixins.QuestionSubClassFieldsMixin, + DeprecationNoticeMixin, ): + deprecated = True + end_of_life_date = datetime(2026, 6, 1) serializer_class = serializers.QuestionnaireQuestionSerializer queryset = Question.objects.none() filter_backends = (DjangoFilterBackend,) @@ -3183,11 +3199,28 @@ class QuestionnaireQuestionViewSet( def get_queryset(self): return Question.objects.all().order_by("id") + @extend_schema( + deprecated=True, + description="This endpoint is deprecated and will be removed on 2026-06-01.", + ) + def list(self, request, *args, **kwargs): + return super().list(request, *args, **kwargs) + + @extend_schema( + deprecated=True, + description="This endpoint is deprecated and will be removed on 2026-06-01.", + ) + def retrieve(self, request, *args, **kwargs): + return super().retrieve(request, *args, **kwargs) + class QuestionnaireAnswerViewSet( viewsets.ReadOnlyModelViewSet, dojo_mixins.AnswerSubClassFieldsMixin, + DeprecationNoticeMixin, ): + deprecated = True + end_of_life_date = datetime(2026, 6, 1) serializer_class = serializers.QuestionnaireAnswerSerializer queryset = Answer.objects.none() filter_backends = (DjangoFilterBackend,) @@ -3199,10 +3232,27 @@ class QuestionnaireAnswerViewSet( def get_queryset(self): return Answer.objects.all().order_by("id") + @extend_schema( + deprecated=True, + description="This endpoint is deprecated and will be removed on 2026-06-01.", + ) + def list(self, request, *args, **kwargs): + return super().list(request, *args, **kwargs) + + @extend_schema( + deprecated=True, + description="This endpoint is deprecated and will be removed on 2026-06-01.", + ) + def retrieve(self, request, *args, **kwargs): + return super().retrieve(request, *args, **kwargs) + class QuestionnaireGeneralSurveyViewSet( viewsets.ReadOnlyModelViewSet, + DeprecationNoticeMixin, ): + deprecated = True + end_of_life_date = datetime(2026, 6, 1) serializer_class = serializers.QuestionnaireGeneralSurveySerializer queryset = General_Survey.objects.none() filter_backends = (DjangoFilterBackend,) @@ -3214,10 +3264,27 @@ class QuestionnaireGeneralSurveyViewSet( def get_queryset(self): return General_Survey.objects.all().order_by("id") + @extend_schema( + deprecated=True, + description="This endpoint is deprecated and will be removed on 2026-06-01.", + ) + def list(self, request, *args, **kwargs): + return super().list(request, *args, **kwargs) + + @extend_schema( + deprecated=True, + description="This endpoint is deprecated and will be removed on 2026-06-01.", + ) + def retrieve(self, request, *args, **kwargs): + return super().retrieve(request, *args, **kwargs) + class QuestionnaireEngagementSurveyViewSet( viewsets.ReadOnlyModelViewSet, + DeprecationNoticeMixin, ): + deprecated = True + end_of_life_date = datetime(2026, 6, 1) serializer_class = serializers.QuestionnaireEngagementSurveySerializer queryset = Engagement_Survey.objects.none() filter_backends = (DjangoFilterBackend,) @@ -3230,13 +3297,29 @@ def get_queryset(self): return Engagement_Survey.objects.all().order_by("id") @extend_schema( - request=OpenApiTypes.NONE, - parameters=[ - OpenApiParameter( - "engagement_id", OpenApiTypes.INT, OpenApiParameter.PATH, - ), - ], - responses={status.HTTP_200_OK: serializers.QuestionnaireAnsweredSurveySerializer}, + deprecated=True, + description="This endpoint is deprecated and will be removed on 2026-06-01.", + ) + def list(self, request, *args, **kwargs): + return super().list(request, *args, **kwargs) + + @extend_schema( + deprecated=True, + description="This endpoint is deprecated and will be removed on 2026-06-01.", + ) + def retrieve(self, request, *args, **kwargs): + return super().retrieve(request, *args, **kwargs) + + @extend_schema( + deprecated=True, + description="This endpoint is deprecated and will be removed on 2026-06-01.", + request=OpenApiTypes.NONE, + parameters=[ + OpenApiParameter( + "engagement_id", OpenApiTypes.INT, OpenApiParameter.PATH, + ), + ], + responses={status.HTTP_200_OK: serializers.QuestionnaireAnsweredSurveySerializer}, ) @action( detail=True, methods=["post"], url_path=r"link_engagement/(?P\d+)", @@ -3258,7 +3341,10 @@ class QuestionnaireAnsweredSurveyViewSet( prefetch.PrefetchListMixin, prefetch.PrefetchRetrieveMixin, viewsets.ReadOnlyModelViewSet, + DeprecationNoticeMixin, ): + deprecated = True + end_of_life_date = datetime(2026, 6, 1) serializer_class = serializers.QuestionnaireAnsweredSurveySerializer queryset = Answered_Survey.objects.none() filter_backends = (DjangoFilterBackend,) @@ -3270,6 +3356,20 @@ class QuestionnaireAnsweredSurveyViewSet( def get_queryset(self): return Answered_Survey.objects.all().order_by("id") + @extend_schema( + deprecated=True, + description="This endpoint is deprecated and will be removed on 2026-06-01.", + ) + def list(self, request, *args, **kwargs): + return super().list(request, *args, **kwargs) + + @extend_schema( + deprecated=True, + description="This endpoint is deprecated and will be removed on 2026-06-01.", + ) + def retrieve(self, request, *args, **kwargs): + return super().retrieve(request, *args, **kwargs) + # Authorization: configuration class AnnouncementViewSet(