121121 Languages ,
122122 Network_Locations ,
123123 Note_Type ,
124+ NoteHistory ,
124125 Notes ,
125126 Notification_Webhooks ,
126127 Notifications ,
@@ -242,6 +243,19 @@ class PrefetchDojoModelViewSet(
242243 pass
243244
244245
246+ class DeprecationNoticeMixin :
247+
248+ deprecated : bool | None = None
249+ end_of_life_date : datetime | None = None
250+
251+ def finalize_response (self , request , response , * args , ** kwargs ):
252+ if self .deprecated is not None :
253+ response ["X-Deprecated" ] = self .deprecated
254+ if self .end_of_life_date is not None :
255+ response ["X-End-Of-Life-Date" ] = self .end_of_life_date .isoformat ()
256+ return super ().finalize_response (request , response , * args , ** kwargs )
257+
258+
245259# Authorization: authenticated users
246260class RoleViewSet (viewsets .ReadOnlyModelViewSet ):
247261 serializer_class = serializers .RoleSerializer
@@ -504,7 +518,7 @@ def generate_report(self, request, pk=None):
504518 request = serializers .AddNewNoteOptionSerializer ,
505519 responses = {status .HTTP_201_CREATED : serializers .NoteSerializer },
506520 )
507- @action (detail = True , methods = ["get" , "post" ], permission_classes = [IsAuthenticated , permissions .UserHasEngagementRelatedObjectPermission ])
521+ @action (detail = True , methods = ["get" , "post" ], permission_classes = [IsAuthenticated , permissions .UserHasEngagementNotePermission ])
508522 def notes (self , request , pk = None ):
509523 engagement = self .get_object ()
510524 if request .method == "POST" :
@@ -532,6 +546,10 @@ def notes(self, request, pk=None):
532546 note_type = note_type ,
533547 )
534548 note .save ()
549+ # Add an entry to the note history
550+ history = NoteHistory .objects .create (data = note .entry , time = note .date , current_editor = note .author )
551+ note .history .add (history )
552+ # Now add the note to the object
535553 engagement .notes .add (note )
536554 # Determine if we need to send any notifications for user mentioned
537555 process_tag_notifications (
@@ -1096,7 +1114,7 @@ def request_response(self, request, pk=None):
10961114 request = serializers .AddNewNoteOptionSerializer ,
10971115 responses = {status .HTTP_201_CREATED : serializers .NoteSerializer },
10981116 )
1099- @action (detail = True , methods = ["get" , "post" ], permission_classes = (IsAuthenticated , permissions .UserHasFindingRelatedObjectPermission ))
1117+ @action (detail = True , methods = ["get" , "post" ], permission_classes = (IsAuthenticated , permissions .UserHasFindingNotePermission ))
11001118 def notes (self , request , pk = None ):
11011119 finding = self .get_object ()
11021120 if request .method == "POST" :
@@ -1125,6 +1143,10 @@ def notes(self, request, pk=None):
11251143 note_type = note_type ,
11261144 )
11271145 note .save ()
1146+ # Add an entry to the note history
1147+ history = NoteHistory .objects .create (data = note .entry , time = note .date , current_editor = note .author )
1148+ note .history .add (history )
1149+ # Now add the note to the object
11281150 finding .last_reviewed = note .date
11291151 finding .last_reviewed_by = author
11301152 finding .save (update_fields = ["last_reviewed" , "last_reviewed_by" , "updated" ])
@@ -1226,7 +1248,7 @@ def download_file(self, request, file_id, pk=None):
12261248 request = serializers .FindingNoteSerializer ,
12271249 responses = {status .HTTP_204_NO_CONTENT : "" },
12281250 )
1229- @action (detail = True , methods = ["patch" ], permission_classes = (IsAuthenticated , permissions .UserHasFindingRelatedObjectPermission ))
1251+ @action (detail = True , methods = ["patch" ], permission_classes = (IsAuthenticated , permissions .UserHasFindingNotePermission ))
12301252 def remove_note (self , request , pk = None ):
12311253 """Remove Note From Finding Note"""
12321254 finding = self .get_object ()
@@ -2162,7 +2184,7 @@ def generate_report(self, request, pk=None):
21622184 request = serializers .AddNewNoteOptionSerializer ,
21632185 responses = {status .HTTP_201_CREATED : serializers .NoteSerializer },
21642186 )
2165- @action (detail = True , methods = ["get" , "post" ], permission_classes = (IsAuthenticated , permissions .UserHasTestRelatedObjectPermission ))
2187+ @action (detail = True , methods = ["get" , "post" ], permission_classes = (IsAuthenticated , permissions .UserHasTestNotePermission ))
21662188 def notes (self , request , pk = None ):
21672189 test = self .get_object ()
21682190 if request .method == "POST" :
@@ -2190,6 +2212,10 @@ def notes(self, request, pk=None):
21902212 note_type = note_type ,
21912213 )
21922214 note .save ()
2215+ # Add an entry to the note history
2216+ history = NoteHistory .objects .create (data = note .entry , time = note .date , current_editor = note .author )
2217+ note .history .add (history )
2218+ # Now add the note to the object
21932219 test .notes .add (note )
21942220 # Determine if we need to send any notifications for user mentioned
21952221 process_tag_notifications (
@@ -3173,7 +3199,10 @@ def get_queryset(self):
31733199class QuestionnaireQuestionViewSet (
31743200 viewsets .ReadOnlyModelViewSet ,
31753201 dojo_mixins .QuestionSubClassFieldsMixin ,
3202+ DeprecationNoticeMixin ,
31763203):
3204+ deprecated = True
3205+ end_of_life_date = datetime (2026 , 6 , 1 )
31773206 serializer_class = serializers .QuestionnaireQuestionSerializer
31783207 queryset = Question .objects .none ()
31793208 filter_backends = (DjangoFilterBackend ,)
@@ -3185,11 +3214,28 @@ class QuestionnaireQuestionViewSet(
31853214 def get_queryset (self ):
31863215 return Question .objects .all ().order_by ("id" )
31873216
3217+ @extend_schema (
3218+ deprecated = True ,
3219+ description = "This endpoint is deprecated and will be removed on 2026-06-01." ,
3220+ )
3221+ def list (self , request , * args , ** kwargs ):
3222+ return super ().list (request , * args , ** kwargs )
3223+
3224+ @extend_schema (
3225+ deprecated = True ,
3226+ description = "This endpoint is deprecated and will be removed on 2026-06-01." ,
3227+ )
3228+ def retrieve (self , request , * args , ** kwargs ):
3229+ return super ().retrieve (request , * args , ** kwargs )
3230+
31883231
31893232class QuestionnaireAnswerViewSet (
31903233 viewsets .ReadOnlyModelViewSet ,
31913234 dojo_mixins .AnswerSubClassFieldsMixin ,
3235+ DeprecationNoticeMixin ,
31923236):
3237+ deprecated = True
3238+ end_of_life_date = datetime (2026 , 6 , 1 )
31933239 serializer_class = serializers .QuestionnaireAnswerSerializer
31943240 queryset = Answer .objects .none ()
31953241 filter_backends = (DjangoFilterBackend ,)
@@ -3201,10 +3247,27 @@ class QuestionnaireAnswerViewSet(
32013247 def get_queryset (self ):
32023248 return Answer .objects .all ().order_by ("id" )
32033249
3250+ @extend_schema (
3251+ deprecated = True ,
3252+ description = "This endpoint is deprecated and will be removed on 2026-06-01." ,
3253+ )
3254+ def list (self , request , * args , ** kwargs ):
3255+ return super ().list (request , * args , ** kwargs )
3256+
3257+ @extend_schema (
3258+ deprecated = True ,
3259+ description = "This endpoint is deprecated and will be removed on 2026-06-01." ,
3260+ )
3261+ def retrieve (self , request , * args , ** kwargs ):
3262+ return super ().retrieve (request , * args , ** kwargs )
3263+
32043264
32053265class QuestionnaireGeneralSurveyViewSet (
32063266 viewsets .ReadOnlyModelViewSet ,
3267+ DeprecationNoticeMixin ,
32073268):
3269+ deprecated = True
3270+ end_of_life_date = datetime (2026 , 6 , 1 )
32083271 serializer_class = serializers .QuestionnaireGeneralSurveySerializer
32093272 queryset = General_Survey .objects .none ()
32103273 filter_backends = (DjangoFilterBackend ,)
@@ -3216,10 +3279,27 @@ class QuestionnaireGeneralSurveyViewSet(
32163279 def get_queryset (self ):
32173280 return General_Survey .objects .all ().order_by ("id" )
32183281
3282+ @extend_schema (
3283+ deprecated = True ,
3284+ description = "This endpoint is deprecated and will be removed on 2026-06-01." ,
3285+ )
3286+ def list (self , request , * args , ** kwargs ):
3287+ return super ().list (request , * args , ** kwargs )
3288+
3289+ @extend_schema (
3290+ deprecated = True ,
3291+ description = "This endpoint is deprecated and will be removed on 2026-06-01." ,
3292+ )
3293+ def retrieve (self , request , * args , ** kwargs ):
3294+ return super ().retrieve (request , * args , ** kwargs )
3295+
32193296
32203297class QuestionnaireEngagementSurveyViewSet (
32213298 viewsets .ReadOnlyModelViewSet ,
3299+ DeprecationNoticeMixin ,
32223300):
3301+ deprecated = True
3302+ end_of_life_date = datetime (2026 , 6 , 1 )
32233303 serializer_class = serializers .QuestionnaireEngagementSurveySerializer
32243304 queryset = Engagement_Survey .objects .none ()
32253305 filter_backends = (DjangoFilterBackend ,)
@@ -3232,13 +3312,29 @@ def get_queryset(self):
32323312 return Engagement_Survey .objects .all ().order_by ("id" )
32333313
32343314 @extend_schema (
3235- request = OpenApiTypes .NONE ,
3236- parameters = [
3237- OpenApiParameter (
3238- "engagement_id" , OpenApiTypes .INT , OpenApiParameter .PATH ,
3239- ),
3240- ],
3241- responses = {status .HTTP_200_OK : serializers .QuestionnaireAnsweredSurveySerializer },
3315+ deprecated = True ,
3316+ description = "This endpoint is deprecated and will be removed on 2026-06-01." ,
3317+ )
3318+ def list (self , request , * args , ** kwargs ):
3319+ return super ().list (request , * args , ** kwargs )
3320+
3321+ @extend_schema (
3322+ deprecated = True ,
3323+ description = "This endpoint is deprecated and will be removed on 2026-06-01." ,
3324+ )
3325+ def retrieve (self , request , * args , ** kwargs ):
3326+ return super ().retrieve (request , * args , ** kwargs )
3327+
3328+ @extend_schema (
3329+ deprecated = True ,
3330+ description = "This endpoint is deprecated and will be removed on 2026-06-01." ,
3331+ request = OpenApiTypes .NONE ,
3332+ parameters = [
3333+ OpenApiParameter (
3334+ "engagement_id" , OpenApiTypes .INT , OpenApiParameter .PATH ,
3335+ ),
3336+ ],
3337+ responses = {status .HTTP_200_OK : serializers .QuestionnaireAnsweredSurveySerializer },
32423338 )
32433339 @action (
32443340 detail = True , methods = ["post" ], url_path = r"link_engagement/(?P<engagement_id>\d+)" ,
@@ -3260,7 +3356,10 @@ class QuestionnaireAnsweredSurveyViewSet(
32603356 prefetch .PrefetchListMixin ,
32613357 prefetch .PrefetchRetrieveMixin ,
32623358 viewsets .ReadOnlyModelViewSet ,
3359+ DeprecationNoticeMixin ,
32633360):
3361+ deprecated = True
3362+ end_of_life_date = datetime (2026 , 6 , 1 )
32643363 serializer_class = serializers .QuestionnaireAnsweredSurveySerializer
32653364 queryset = Answered_Survey .objects .none ()
32663365 filter_backends = (DjangoFilterBackend ,)
@@ -3272,6 +3371,20 @@ class QuestionnaireAnsweredSurveyViewSet(
32723371 def get_queryset (self ):
32733372 return Answered_Survey .objects .all ().order_by ("id" )
32743373
3374+ @extend_schema (
3375+ deprecated = True ,
3376+ description = "This endpoint is deprecated and will be removed on 2026-06-01." ,
3377+ )
3378+ def list (self , request , * args , ** kwargs ):
3379+ return super ().list (request , * args , ** kwargs )
3380+
3381+ @extend_schema (
3382+ deprecated = True ,
3383+ description = "This endpoint is deprecated and will be removed on 2026-06-01." ,
3384+ )
3385+ def retrieve (self , request , * args , ** kwargs ):
3386+ return super ().retrieve (request , * args , ** kwargs )
3387+
32753388
32763389# Authorization: configuration
32773390class AnnouncementViewSet (
0 commit comments