From 2ef2223fe544e9f7478b7b5968dca29dc5510cbd Mon Sep 17 00:00:00 2001 From: Lucas Welscher Date: Sun, 28 Apr 2024 13:58:49 +0200 Subject: [PATCH 1/6] `Development`: Add architecture test for endpoint naming conventions --- .../config/lti/CustomLti13Configurer.java | 2 +- .../web/rest/AeolusTemplateResource.java | 6 +- .../web/rest/ApollonDiagramResource.java | 2 +- .../artemis/web/rest/CompetencyResource.java | 8 +-- .../artemis/web/rest/LectureResource.java | 2 +- .../web/rest/ModelingSubmissionResource.java | 4 +- .../web/rest/QuizExerciseResource.java | 8 +-- .../artemis/web/rest/StudentExamResource.java | 2 +- .../www1/artemis/web/rest/TeamResource.java | 2 +- .../web/rest/TextSubmissionResource.java | 4 +- .../admin/AdminBuildJobQueueResource.java | 2 +- .../admin/AdminLtiConfigurationResource.java | 2 +- .../metis/conversation/ChannelResource.java | 2 +- .../conversation/ConversationResource.java | 4 +- .../metis/conversation/GroupChatResource.java | 2 +- .../conversation/OneToOneChatResource.java | 2 +- .../rest/open/PublicOAuth2JWKSResource.java | 2 +- ...grammingExerciseParticipationResource.java | 6 +- .../ProgrammingExerciseResource.java | 2 +- .../PushNotificationResource.java | 2 +- ...grammingExerciseParticipationResource.java | 36 +++++----- .../repository/TestRepositoryResource.java | 20 +++--- .../AbstractArchitectureTest.java | 6 ++ .../architecture/ArchitectureTest.java | 3 +- .../ResourceArchitectureTest.java | 72 +++++++++++++++++++ 25 files changed, 140 insertions(+), 63 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java b/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java index 72ddb0c83f99..842feda9681c 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java +++ b/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java @@ -32,7 +32,7 @@ public class CustomLti13Configurer extends Lti13Configurer { private static final String LOGIN_INITIATION_PATH = "/initiate-login"; /** Base path for LTI 1.3 API endpoints. */ - public static final String LTI13_BASE_PATH = "/api/public/lti13"; + public static final String LTI13_BASE_PATH = "api/public/lti13"; /** Full path for LTI 1.3 login. */ public static final String LTI13_LOGIN_PATH = LTI13_BASE_PATH + LOGIN_PATH; diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/AeolusTemplateResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/AeolusTemplateResource.java index db450c5234be..6fd9e7312ca7 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/AeolusTemplateResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/AeolusTemplateResource.java @@ -32,7 +32,7 @@ */ @Profile("aeolus | localci") @RestController -@RequestMapping("api/aeolus") +@RequestMapping("api/aeolus/") public class AeolusTemplateResource { private static final Logger log = LoggerFactory.getLogger(AeolusTemplateResource.class); @@ -64,7 +64,7 @@ public AeolusTemplateResource(AeolusTemplateService aeolusTemplateService, Build * @param testCoverage Whether the test coverage template should be used * @return The requested file, or 404 if the file doesn't exist */ - @GetMapping({ "/templates/{language}/{projectType}", "/templates/{language}" }) + @GetMapping({ "templates/{language}/{projectType}", "templates/{language}" }) @EnforceAtLeastEditor public ResponseEntity getAeolusTemplate(@PathVariable ProgrammingLanguage language, @PathVariable Optional projectType, @RequestParam(value = "staticAnalysis", defaultValue = "false") boolean staticAnalysis, @@ -91,7 +91,7 @@ public ResponseEntity getAeolusTemplate(@PathVariable ProgrammingLanguag * @param testCoverage Whether the test coverage template should be used * @return The requested file, or 404 if the file doesn't exist */ - @GetMapping({ "/templateScripts/{language}/{projectType}", "/templateScripts/{language}" }) + @GetMapping({ "templateScripts/{language}/{projectType}", "templateScripts/{language}" }) @EnforceAtLeastEditor public ResponseEntity getAeolusTemplateScript(@PathVariable ProgrammingLanguage language, @PathVariable Optional projectType, @RequestParam(value = "staticAnalysis", defaultValue = "false") boolean staticAnalysis, diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/ApollonDiagramResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/ApollonDiagramResource.java index e591923d3e0b..9c3f9277695c 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/ApollonDiagramResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/ApollonDiagramResource.java @@ -118,7 +118,7 @@ public ResponseEntity updateApollonDiagram(@RequestBody ApollonD * @param diagramId the id of the diagram * @return the ResponseEntity with status 200 (OK) and with body the title of the diagram or 404 Not Found if no diagram with that id exists */ - @GetMapping(value = "/apollon-diagrams/{diagramId}/title") + @GetMapping("apollon-diagrams/{diagramId}/title") @EnforceAtLeastStudent public ResponseEntity getDiagramTitle(@PathVariable Long diagramId) { final var title = apollonDiagramRepository.getDiagramTitle(diagramId); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/CompetencyResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/CompetencyResource.java index 34030b6e22f3..c15c79ff5b79 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/CompetencyResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/CompetencyResource.java @@ -262,7 +262,7 @@ public ResponseEntity createCompetency(@PathVariable long courseId, * @return the ResponseEntity with status 201 (Created) and body the created competencies * @throws URISyntaxException if the Location URI syntax is incorrect */ - @PostMapping("/courses/{courseId}/competencies/bulk") + @PostMapping("courses/{courseId}/competencies/bulk") @EnforceAtLeastInstructor public ResponseEntity> createCompetencies(@PathVariable Long courseId, @RequestBody List competencies) throws URISyntaxException { log.debug("REST request to create Competencies : {}", competencies); @@ -314,7 +314,7 @@ public ResponseEntity importCompetency(@PathVariable long courseId, * @return the ResponseEntity with status 201 (Created) and with body containing the imported competencies * @throws URISyntaxException if the Location URI syntax is incorrect */ - @PostMapping("/courses/{courseId}/competencies/import/bulk") + @PostMapping("courses/{courseId}/competencies/import/bulk") @EnforceAtLeastEditor public ResponseEntity> importCompetencies(@PathVariable long courseId, @RequestBody List competenciesToImport, @RequestParam(defaultValue = "false") boolean importRelations) throws URISyntaxException { @@ -347,7 +347,7 @@ public ResponseEntity> importCompetencies(@P * @return the ResponseEntity with status 201 (Created) and with body containing the imported competencies (and relations) * @throws URISyntaxException if the Location URI syntax is incorrect */ - @PostMapping("/courses/{courseId}/competencies/import-all/{sourceCourseId}") + @PostMapping("courses/{courseId}/competencies/import-all/{sourceCourseId}") @EnforceAtLeastInstructor public ResponseEntity> importAllCompetenciesFromCourse(@PathVariable long courseId, @PathVariable long sourceCourseId, @RequestParam(defaultValue = "false") boolean importRelations) throws URISyntaxException { @@ -591,7 +591,7 @@ public ResponseEntity removePrerequisite(@PathVariable long competencyId, * @param courseDescription the text description of the course * @return the ResponseEntity with status 200 (OK) and body the genrated competencies */ - @PostMapping("/courses/{courseId}/competencies/generate-from-description") + @PostMapping("courses/{courseId}/competencies/generate-from-description") @EnforceAtLeastEditor public ResponseEntity> generateCompetenciesFromCourseDescription(@PathVariable Long courseId, @RequestBody String courseDescription) { var irisService = irisCompetencyGenerationSessionService.orElseThrow(); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/LectureResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/LectureResource.java index 1ff9d496b894..e92abba0cee5 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/LectureResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/LectureResource.java @@ -169,7 +169,7 @@ public ResponseEntity> getAllLecturesOnPage(SearchT * @param courseId the courseId of the course for which all lectures should be returned * @return the ResponseEntity with status 200 (OK) and the list of lectures in body */ - @GetMapping(value = "/courses/{courseId}/lectures") + @GetMapping("courses/{courseId}/lectures") @EnforceAtLeastEditor public ResponseEntity> getLecturesForCourse(@PathVariable Long courseId, @RequestParam(required = false, defaultValue = "false") boolean withLectureUnits) { log.debug("REST request to get all Lectures for the course with id : {}", courseId); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingSubmissionResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingSubmissionResource.java index 96e838ce1cc8..c2997f1eb6bb 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingSubmissionResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingSubmissionResource.java @@ -171,7 +171,7 @@ private ResponseEntity handleModelingSubmission(Long exercis @ResponseStatus(HttpStatus.OK) @ApiResponses({ @ApiResponse(code = 200, message = GET_200_SUBMISSIONS_REASON, response = ModelingSubmission.class, responseContainer = "List"), @ApiResponse(code = 403, message = ErrorConstants.REQ_403_REASON), @ApiResponse(code = 404, message = ErrorConstants.REQ_404_REASON), }) - @GetMapping(value = "/exercises/{exerciseId}/modeling-submissions") + @GetMapping(value = "exercises/{exerciseId}/modeling-submissions") @EnforceAtLeastTutor public ResponseEntity> getAllModelingSubmissions(@PathVariable Long exerciseId, @RequestParam(defaultValue = "false") boolean submittedOnly, @RequestParam(defaultValue = "false") boolean assessedByTutor, @RequestParam(value = "correction-round", defaultValue = "0") int correctionRound) { @@ -255,7 +255,7 @@ public ResponseEntity getModelingSubmission(@PathVariable Lo * @param correctionRound correctionRound for which submissions without a result should be returned * @return the ResponseEntity with status 200 (OK) and a modeling submission without assessment in body */ - @GetMapping(value = "/exercises/{exerciseId}/modeling-submission-without-assessment") + @GetMapping(value = "exercises/{exerciseId}/modeling-submission-without-assessment") @EnforceAtLeastTutor public ResponseEntity getModelingSubmissionWithoutAssessment(@PathVariable Long exerciseId, @RequestParam(value = "lock", defaultValue = "false") boolean lockSubmission, @RequestParam(value = "correction-round", defaultValue = "0") int correctionRound) { diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java index c0055638afe5..8555fd1fd809 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java @@ -179,7 +179,7 @@ public QuizExerciseResource(QuizExerciseService quizExerciseService, QuizMessagi * @return the ResponseEntity with status 201 (Created) and with body the new quizExercise, or with status 400 (Bad Request) if the quizExercise has already an ID * @throws URISyntaxException if the Location URI syntax is incorrect */ - @PostMapping(value = "/quiz-exercises", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PostMapping(value = "quiz-exercises", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @EnforceAtLeastEditor public ResponseEntity createQuizExercise(@RequestPart("exercise") QuizExercise quizExercise, @RequestPart(value = "files", required = false) List files) throws URISyntaxException, IOException { @@ -222,7 +222,7 @@ public ResponseEntity createQuizExercise(@RequestPart("exercise") * @return the ResponseEntity with status 200 (OK) and with body the updated quizExercise, or with status 400 (Bad Request) if the quizExercise is not valid, or with status 500 * (Internal Server Error) if the quizExercise couldn't be updated */ - @PutMapping(value = "/quiz-exercises/{exerciseId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PutMapping(value = "quiz-exercises/{exerciseId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @EnforceAtLeastEditor public ResponseEntity updateQuizExercise(@PathVariable Long exerciseId, @RequestPart("exercise") QuizExercise quizExercise, @RequestPart(value = "files", required = false) List files, @RequestParam(value = "notificationText", required = false) String notificationText) @@ -283,7 +283,7 @@ public ResponseEntity updateQuizExercise(@PathVariable Long exerci * @param courseId id of the course of which all exercises should be fetched * @return the ResponseEntity with status 200 (OK) and the list of quiz exercises in body */ - @GetMapping(value = "/courses/{courseId}/quiz-exercises") + @GetMapping(value = "courses/{courseId}/quiz-exercises") @EnforceAtLeastTutor public ResponseEntity> getQuizExercisesForCourse(@PathVariable Long courseId) { log.info("REST request to get all quiz exercises for the course with id : {}", courseId); @@ -640,7 +640,7 @@ public ResponseEntity deleteQuizExercise(@PathVariable Long quizExerciseId * @return the ResponseEntity with status 200 (OK) and with body the re-evaluated quizExercise, or with status 400 (Bad Request) if the quizExercise is not valid, or with * status 500 (Internal Server Error) if the quizExercise couldn't be re-evaluated */ - @PutMapping(value = "/quiz-exercises/{quizExerciseId}/re-evaluate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @PutMapping(value = "quiz-exercises/{quizExerciseId}/re-evaluate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @EnforceAtLeastInstructor public ResponseEntity reEvaluateQuizExercise(@PathVariable Long quizExerciseId, @RequestPart("exercise") QuizExercise quizExercise, @RequestPart(value = "files", required = false) List files) throws IOException { diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/StudentExamResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/StudentExamResource.java index 12e80f4e894f..73133896d839 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/StudentExamResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/StudentExamResource.java @@ -685,7 +685,7 @@ public ResponseEntity deleteTestRun(@PathVariable Long courseId, @PathVari * @param examId the exam to which the student exam belongs to * @return ResponseEntity containing the list of generated participations */ - @PostMapping(value = "/courses/{courseId}/exams/{examId}/student-exams/start-exercises") + @PostMapping(value = "courses/{courseId}/exams/{examId}/student-exams/start-exercises") @EnforceAtLeastInstructor public ResponseEntity startExercises(@PathVariable Long courseId, @PathVariable Long examId) { long start = System.nanoTime(); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/TeamResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/TeamResource.java index 6d816ccdce3b..494b4b377e5d 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/TeamResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/TeamResource.java @@ -444,7 +444,7 @@ public ResponseEntity> importTeamsFromSourceExercise(@PathVariable lo * @param teamShortName short name of the team (all teams with the short name in the course are seen as the same team) * @return Course with exercises and participations (and latest submissions) for the team */ - @GetMapping(value = "/courses/{courseId}/teams/{teamShortName}/with-exercises-and-participations") + @GetMapping(value = "courses/{courseId}/teams/{teamShortName}/with-exercises-and-participations") @EnforceAtLeastStudent public ResponseEntity getCourseWithExercisesAndParticipationsForTeam(@PathVariable Long courseId, @PathVariable String teamShortName) { log.debug("REST request to get Course {} with exercises and participations for Team with short name {}", courseId, teamShortName); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/TextSubmissionResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/TextSubmissionResource.java index 22afc6bab93d..94a7f42e57ca 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/TextSubmissionResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/TextSubmissionResource.java @@ -187,7 +187,7 @@ public ResponseEntity getTextSubmissionWithResults(@PathVariable * @param assessedByTutor mark if only assessed Submissions should be returned * @return the ResponseEntity with status 200 (OK) and the list of textSubmissions in body */ - @GetMapping(value = "/exercises/{exerciseId}/text-submissions") + @GetMapping(value = "exercises/{exerciseId}/text-submissions") @EnforceAtLeastTutor public ResponseEntity> getAllTextSubmissions(@PathVariable Long exerciseId, @RequestParam(defaultValue = "false") boolean submittedOnly, @RequestParam(defaultValue = "false") boolean assessedByTutor, @RequestParam(value = "correction-round", defaultValue = "0") int correctionRound) { @@ -205,7 +205,7 @@ public ResponseEntity> getAllTextSubmissions(@PathVariable Long * @param lockSubmission optional value to define if the submission should be locked and has the value of false if not set manually * @return the ResponseEntity with status 200 (OK) and the list of textSubmissions in body */ - @GetMapping(value = "/exercises/{exerciseId}/text-submission-without-assessment") + @GetMapping(value = "exercises/{exerciseId}/text-submission-without-assessment") @EnforceAtLeastTutor public ResponseEntity getTextSubmissionWithoutAssessment(@PathVariable Long exerciseId, @RequestParam(value = "head", defaultValue = "false") boolean skipAssessmentOrderOptimization, diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/admin/AdminBuildJobQueueResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/admin/AdminBuildJobQueueResource.java index 50a74b0d8128..d276acd3a248 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/admin/AdminBuildJobQueueResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/admin/AdminBuildJobQueueResource.java @@ -123,7 +123,7 @@ public ResponseEntity cancelAllRunningBuildJobs() { * @param agentName the name of the agent * @return the ResponseEntity with the result of the cancellation */ - @DeleteMapping("/cancel-all-running-jobs-for-agent") + @DeleteMapping("cancel-all-running-jobs-for-agent") @EnforceAdmin public ResponseEntity cancelAllRunningBuildJobsForAgent(@RequestParam String agentName) { log.debug("REST request to cancel all running build jobs for agent {}", agentName); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/admin/AdminLtiConfigurationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/admin/AdminLtiConfigurationResource.java index 706e9715f775..0d7d7a54b1f1 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/admin/AdminLtiConfigurationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/admin/AdminLtiConfigurationResource.java @@ -144,7 +144,7 @@ public ResponseEntity addLtiPlatformConfiguration(@RequestBody LtiPlatform * @param registrationToken Optional token for the registration process. * @return a {@link ResponseEntity} with status 200 (OK) if the dynamic registration process was successful. */ - @PostMapping("/lti13/dynamic-registration") + @PostMapping("lti13/dynamic-registration") @EnforceAdmin public ResponseEntity lti13DynamicRegistration(@RequestParam(name = "openid_configuration") String openIdConfiguration, @RequestParam(name = "registration_token", required = false) String registrationToken) { diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ChannelResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ChannelResource.java index d22a8a4437e4..2012c949047c 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ChannelResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ChannelResource.java @@ -55,7 +55,7 @@ @Profile(PROFILE_CORE) @RestController -@RequestMapping("api/courses") +@RequestMapping("api/courses/") public class ChannelResource extends ConversationManagementResource { private static final Logger log = LoggerFactory.getLogger(ChannelResource.class); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java index 2954f4627e5d..7072079ba2bc 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java @@ -47,7 +47,7 @@ @Profile(PROFILE_CORE) @RestController -@RequestMapping("api/courses") +@RequestMapping("api/courses/") public class ConversationResource extends ConversationManagementResource { private static final Logger log = LoggerFactory.getLogger(ConversationResource.class); @@ -135,7 +135,7 @@ public ResponseEntity updateIsHidden(@PathVariable Long courseId, @PathVar * @param isMuted the new muted status * @return ResponseEntity with status 200 (Ok) */ - @PostMapping("/{courseId}/conversations/{conversationId}/muted") + @PostMapping("{courseId}/conversations/{conversationId}/muted") @EnforceAtLeastStudent public ResponseEntity updateIsMuted(@PathVariable Long courseId, @PathVariable Long conversationId, @RequestParam boolean isMuted) { checkMessagingOrCommunicationEnabledElseThrow(courseId); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/GroupChatResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/GroupChatResource.java index 260267d7cc3c..a26d3ceb4546 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/GroupChatResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/GroupChatResource.java @@ -38,7 +38,7 @@ @Profile(PROFILE_CORE) @RestController -@RequestMapping("api/courses") +@RequestMapping("api/courses/") public class GroupChatResource extends ConversationManagementResource { private static final Logger log = LoggerFactory.getLogger(GroupChatResource.class); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/OneToOneChatResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/OneToOneChatResource.java index 0e4e8ba6eee0..cd34a2276e9d 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/OneToOneChatResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/OneToOneChatResource.java @@ -32,7 +32,7 @@ @Profile(PROFILE_CORE) @RestController -@RequestMapping("api/courses") +@RequestMapping("api/courses/") public class OneToOneChatResource extends ConversationManagementResource { private static final Logger log = LoggerFactory.getLogger(OneToOneChatResource.class); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicOAuth2JWKSResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicOAuth2JWKSResource.java index 2a9ca6594835..1d8ff193c4dd 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicOAuth2JWKSResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicOAuth2JWKSResource.java @@ -26,7 +26,7 @@ public PublicOAuth2JWKSResource(OAuth2JWKSService jwksService) { this.jwksService = jwksService; } - @GetMapping("/.well-known/jwks.json") + @GetMapping(".well-known/jwks.json") @EnforceNothing @ManualConfig public ResponseEntity getJwkSet() { diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseParticipationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseParticipationResource.java index d9ab8fce0870..3c25b02549bd 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseParticipationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseParticipationResource.java @@ -111,7 +111,7 @@ public ResponseEntity getParticipationW * @param participationId for which to retrieve the student participation with all results and feedbacks. * @return the ResponseEntity with status 200 (OK) and the participation with all results and feedbacks in the body. */ - @GetMapping("/programming-exercise-participations/{participationId}/student-participation-with-all-results") + @GetMapping("programming-exercise-participations/{participationId}/student-participation-with-all-results") @EnforceAtLeastStudent public ResponseEntity getParticipationWithAllResultsForStudentParticipation(@PathVariable Long participationId) { ProgrammingExerciseStudentParticipation participation = programmingExerciseStudentParticipationRepository.findByIdWithAllResultsAndRelatedSubmissions(participationId) @@ -132,7 +132,7 @@ public ResponseEntity getParticipationW * @return the ResponseEntity with status 200 (OK) and the latest result with feedbacks in its body, 404 if the participation can't be found or 403 if the user is not allowed * to access the participation. */ - @GetMapping(value = "/programming-exercise-participations/{participationId}/latest-result-with-feedbacks") + @GetMapping(value = "programming-exercise-participations/{participationId}/latest-result-with-feedbacks") @EnforceAtLeastStudent public ResponseEntity getLatestResultWithFeedbacksForProgrammingExerciseParticipation(@PathVariable Long participationId, @RequestParam(defaultValue = "false") boolean withSubmission) { @@ -150,7 +150,7 @@ public ResponseEntity getLatestResultWithFeedbacksForProgrammingExercise * @param participationId of the participation to check. * @return the ResponseEntity with status 200 (OK) with true if there is a result, otherwise false. */ - @GetMapping(value = "/programming-exercise-participations/{participationId}/has-result") + @GetMapping(value = "programming-exercise-participations/{participationId}/has-result") @EnforceAtLeastStudent public ResponseEntity checkIfParticipationHashResult(@PathVariable Long participationId) { boolean hasResult = resultRepository.existsByParticipationId(participationId); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java index d44ad4499a6b..fc605c149ec9 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java @@ -667,7 +667,7 @@ public ResponseEntity> getAllExercisesO * @param programmingLanguage Filters for only exercises with this language * @return The desired page, sorted and matching the given query */ - @GetMapping("/programming-exercises/with-sca") + @GetMapping("programming-exercises/with-sca") @EnforceAtLeastEditor public ResponseEntity> getAllExercisesWithSCAOnPage(SearchTermPageableSearchDTO search, @RequestParam(defaultValue = "true") boolean isCourseFilter, @RequestParam(defaultValue = "true") boolean isExamFilter, diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/push_notification/PushNotificationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/push_notification/PushNotificationResource.java index 33ddf6dc6b8e..d3e6a655d884 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/push_notification/PushNotificationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/push_notification/PushNotificationResource.java @@ -39,7 +39,7 @@ */ @Profile(PROFILE_CORE) @RestController -@RequestMapping("api/push_notification") +@RequestMapping("api/push_notification/") public class PushNotificationResource { private static final Logger log = LoggerFactory.getLogger(PushNotificationResource.class); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/repository/RepositoryProgrammingExerciseParticipationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/repository/RepositoryProgrammingExerciseParticipationResource.java index da47ce54a1a9..207f6d52d0ed 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/repository/RepositoryProgrammingExerciseParticipationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/repository/RepositoryProgrammingExerciseParticipationResource.java @@ -185,7 +185,7 @@ else if (participation instanceof ProgrammingExerciseStudentParticipation studen } @Override - @GetMapping(value = "/repository/{participationId}/files", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "repository/{participationId}/files", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastStudent public ResponseEntity> getFiles(@PathVariable Long participationId) { return super.getFiles(participationId); @@ -197,7 +197,7 @@ public ResponseEntity> getFiles(@PathVariable Long partici * @param participationId the participationId of the repository we want to get the files from * @return a map with the file path as key and the file type as value */ - @GetMapping(value = "/repository/{participationId}/files-plagiarism-view", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "repository/{participationId}/files-plagiarism-view", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastStudent public ResponseEntity> getFilesForPlagiarismView(@PathVariable Long participationId) { log.debug("REST request to files for plagiarism view for domainId : {}", participationId); @@ -218,7 +218,7 @@ public ResponseEntity> getFilesForPlagiarismView(@PathVari * @param repositoryType the type of the repository (template, solution, tests) * @return a map with the file path as key and the file content as value */ - @GetMapping(value = "/repository/{participationId}/files-content/{commitId}", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "repository/{participationId}/files-content/{commitId}", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastStudent public ResponseEntity> getFilesAtCommit(@PathVariable long participationId, @PathVariable String commitId, @RequestAttribute(required = false) RepositoryType repositoryType) { @@ -251,7 +251,7 @@ public ResponseEntity> getFilesAtCommit(@PathVariable long p * @param participationId participation of the student * @return the ResponseEntity with status 200 (OK) and a map of files with the information if they were changed/are new. */ - @GetMapping(value = "/repository/{participationId}/files-change", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "repository/{participationId}/files-change", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastTutor public ResponseEntity> getFilesWithInformationAboutChange(@PathVariable Long participationId) { return super.executeAndCheckForExceptions(() -> { @@ -266,7 +266,7 @@ public ResponseEntity> getFilesWithInformationAboutChange(@ } @Override - @GetMapping(value = "/repository/{participationId}/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + @GetMapping(value = "repository/{participationId}/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @EnforceAtLeastStudent public ResponseEntity getFile(@PathVariable Long participationId, @RequestParam("file") String filename) { return super.getFile(participationId, filename); @@ -279,7 +279,7 @@ public ResponseEntity getFile(@PathVariable Long participationId, @Reque * @param filename the name of the file to retrieve * @return the file with the given filename */ - @GetMapping(value = "/repository/{participationId}/file-plagiarism-view", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + @GetMapping(value = "repository/{participationId}/file-plagiarism-view", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @EnforceAtLeastStudent public ResponseEntity getFileForPlagiarismView(@PathVariable Long participationId, @RequestParam("file") String filename) { log.debug("REST request to file {} for plagiarism view for domainId : {}", filename, participationId); @@ -298,7 +298,7 @@ public ResponseEntity getFileForPlagiarismView(@PathVariable Long partic * @param participationId participation of the student/template/solution * @return the ResponseEntity with status 200 (OK) and a map of files with their content */ - @GetMapping(value = "/repository/{participationId}/files-content", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "repository/{participationId}/files-content", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastTutor public ResponseEntity> getFilesWithContent(@PathVariable Long participationId) { return super.executeAndCheckForExceptions(() -> { @@ -314,7 +314,7 @@ public ResponseEntity> getFilesWithContent(@PathVariable Lon * @param participationId participation of the student/template/solution * @return the ResponseEntity with status 200 (OK) and a set of file names */ - @GetMapping(value = "/repository/{participationId}/file-names", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "repository/{participationId}/file-names", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastTutor public ResponseEntity> getFileNames(@PathVariable Long participationId) { return super.executeAndCheckForExceptions(() -> { @@ -327,7 +327,7 @@ public ResponseEntity> getFileNames(@PathVariable Long participation } @Override - @PostMapping(value = "/repository/{participationId}/file", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "repository/{participationId}/file", produces = MediaType.APPLICATION_JSON_VALUE) @FeatureToggle(Feature.ProgrammingExercises) @EnforceAtLeastStudent public ResponseEntity createFile(@PathVariable Long participationId, @RequestParam("file") String filePath, HttpServletRequest request) { @@ -335,7 +335,7 @@ public ResponseEntity createFile(@PathVariable Long participationId, @Requ } @Override - @PostMapping(value = "/repository/{participationId}/folder", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "repository/{participationId}/folder", produces = MediaType.APPLICATION_JSON_VALUE) @FeatureToggle(Feature.ProgrammingExercises) @EnforceAtLeastStudent public ResponseEntity createFolder(@PathVariable Long participationId, @RequestParam("folder") String folderPath, HttpServletRequest request) { @@ -343,7 +343,7 @@ public ResponseEntity createFolder(@PathVariable Long participationId, @Re } @Override - @PostMapping(value = "/repository/{participationId}/rename-file", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "repository/{participationId}/rename-file", produces = MediaType.APPLICATION_JSON_VALUE) @FeatureToggle(Feature.ProgrammingExercises) @EnforceAtLeastStudent public ResponseEntity renameFile(@PathVariable Long participationId, @RequestBody FileMove fileMove) { @@ -351,14 +351,14 @@ public ResponseEntity renameFile(@PathVariable Long participationId, @Requ } @Override - @DeleteMapping(value = "/repository/{participationId}/file", produces = MediaType.APPLICATION_JSON_VALUE) + @DeleteMapping(value = "repository/{participationId}/file", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastStudent public ResponseEntity deleteFile(@PathVariable Long participationId, @RequestParam("file") String filename) { return super.deleteFile(participationId, filename); } @Override - @GetMapping(value = "/repository/{participationId}/pull", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "repository/{participationId}/pull", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastStudent public ResponseEntity pullChanges(@PathVariable Long participationId) { return super.pullChanges(participationId); @@ -372,7 +372,7 @@ public ResponseEntity pullChanges(@PathVariable Long participationId) { * @param commit whether to commit after updating the files * @return {Map} file submissions or the appropriate http error */ - @PutMapping(value = "/repository/{participationId}/files") + @PutMapping(value = "repository/{participationId}/files") @EnforceAtLeastStudent public ResponseEntity> updateParticipationFiles(@PathVariable("participationId") Long participationId, @RequestBody List submissions, @RequestParam(defaultValue = "false") boolean commit) { @@ -427,7 +427,7 @@ public ResponseEntity> updateParticipationFiles(@PathVariabl * participation OR the buildAndTestAfterDueDate is set and the repository is now locked. */ @Override - @PostMapping(value = "/repository/{participationId}/commit", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "repository/{participationId}/commit", produces = MediaType.APPLICATION_JSON_VALUE) @FeatureToggle(Feature.ProgrammingExercises) @EnforceAtLeastStudent public ResponseEntity commitChanges(@PathVariable Long participationId) { @@ -435,7 +435,7 @@ public ResponseEntity commitChanges(@PathVariable Long participationId) { } @Override - @PostMapping(value = "/repository/{participationId}/reset", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "repository/{participationId}/reset", produces = MediaType.APPLICATION_JSON_VALUE) @FeatureToggle(Feature.ProgrammingExercises) @EnforceAtLeastStudent public ResponseEntity resetToLastCommit(@PathVariable Long participationId) { @@ -443,7 +443,7 @@ public ResponseEntity resetToLastCommit(@PathVariable Long participationId } @Override - @GetMapping(value = "/repository/{participationId}", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "repository/{participationId}", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastStudent public ResponseEntity getStatus(@PathVariable Long participationId) throws GitAPIException { return super.getStatus(participationId); @@ -458,7 +458,7 @@ public ResponseEntity getStatus(@PathVariable Long particip * @return the ResponseEntity with status 200 (OK) and with body the result, or with status 404 (Not Found) */ // TODO: rename to participation/{participationId}/buildlogs - @GetMapping(value = "/repository/{participationId}/buildlogs", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "repository/{participationId}/buildlogs", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastStudent public ResponseEntity> getBuildLogs(@PathVariable Long participationId, @RequestParam(name = "resultId") Optional resultId) { log.debug("REST request to get build log : {}", participationId); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/repository/TestRepositoryResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/repository/TestRepositoryResource.java index 8c96c47cfa7f..6340a1679951 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/repository/TestRepositoryResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/repository/TestRepositoryResource.java @@ -97,21 +97,21 @@ String getOrRetrieveBranchOfDomainObject(Long exerciseId) { } @Override - @GetMapping(value = "/test-repository/{exerciseId}/files", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "test-repository/{exerciseId}/files", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastTutor public ResponseEntity> getFiles(@PathVariable Long exerciseId) { return super.getFiles(exerciseId); } @Override - @GetMapping(value = "/test-repository/{exerciseId}/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + @GetMapping(value = "test-repository/{exerciseId}/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @EnforceAtLeastTutor public ResponseEntity getFile(@PathVariable Long exerciseId, @RequestParam("file") String filename) { return super.getFile(exerciseId, filename); } @Override - @PostMapping(value = "/test-repository/{exerciseId}/file", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "test-repository/{exerciseId}/file", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastTutor @FeatureToggle(Feature.ProgrammingExercises) public ResponseEntity createFile(@PathVariable Long exerciseId, @RequestParam("file") String filePath, HttpServletRequest request) { @@ -119,7 +119,7 @@ public ResponseEntity createFile(@PathVariable Long exerciseId, @RequestPa } @Override - @PostMapping(value = "/test-repository/{exerciseId}/folder", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "test-repository/{exerciseId}/folder", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastTutor @FeatureToggle(Feature.ProgrammingExercises) public ResponseEntity createFolder(@PathVariable Long exerciseId, @RequestParam("folder") String folderPath, HttpServletRequest request) { @@ -127,7 +127,7 @@ public ResponseEntity createFolder(@PathVariable Long exerciseId, @Request } @Override - @PostMapping(value = "/test-repository/{exerciseId}/rename-file", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "test-repository/{exerciseId}/rename-file", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastTutor @FeatureToggle(Feature.ProgrammingExercises) public ResponseEntity renameFile(@PathVariable Long exerciseId, @RequestBody FileMove fileMove) { @@ -135,7 +135,7 @@ public ResponseEntity renameFile(@PathVariable Long exerciseId, @RequestBo } @Override - @DeleteMapping(value = "/test-repository/{exerciseId}/file", produces = MediaType.APPLICATION_JSON_VALUE) + @DeleteMapping(value = "test-repository/{exerciseId}/file", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastTutor @FeatureToggle(Feature.ProgrammingExercises) public ResponseEntity deleteFile(@PathVariable Long exerciseId, @RequestParam("file") String filename) { @@ -143,14 +143,14 @@ public ResponseEntity deleteFile(@PathVariable Long exerciseId, @RequestPa } @Override - @GetMapping(value = "/test-repository/{exerciseId}/pull", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "test-repository/{exerciseId}/pull", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastTutor public ResponseEntity pullChanges(@PathVariable Long exerciseId) { return super.pullChanges(exerciseId); } @Override - @PostMapping(value = "/test-repository/{exerciseId}/commit", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "test-repository/{exerciseId}/commit", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastTutor @FeatureToggle(Feature.ProgrammingExercises) public ResponseEntity commitChanges(@PathVariable Long exerciseId) { @@ -158,7 +158,7 @@ public ResponseEntity commitChanges(@PathVariable Long exerciseId) { } @Override - @PostMapping(value = "/test-repository/{exerciseId}/reset", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = "test-repository/{exerciseId}/reset", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastTutor @FeatureToggle(Feature.ProgrammingExercises) public ResponseEntity resetToLastCommit(@PathVariable Long exerciseId) { @@ -166,7 +166,7 @@ public ResponseEntity resetToLastCommit(@PathVariable Long exerciseId) { } @Override - @GetMapping(value = "/test-repository/{exerciseId}", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "test-repository/{exerciseId}", produces = MediaType.APPLICATION_JSON_VALUE) @EnforceAtLeastTutor public ResponseEntity getStatus(@PathVariable Long exerciseId) throws GitAPIException { return super.getStatus(exerciseId); diff --git a/src/test/java/de/tum/in/www1/artemis/architecture/AbstractArchitectureTest.java b/src/test/java/de/tum/in/www1/artemis/architecture/AbstractArchitectureTest.java index fe378cb6271f..915cbdf7d9b0 100644 --- a/src/test/java/de/tum/in/www1/artemis/architecture/AbstractArchitectureTest.java +++ b/src/test/java/de/tum/in/www1/artemis/architecture/AbstractArchitectureTest.java @@ -4,6 +4,7 @@ import static com.tngtech.archunit.base.DescribedPredicate.not; import static com.tngtech.archunit.base.DescribedPredicate.or; import static com.tngtech.archunit.core.domain.JavaClass.Predicates.belongToAnyOf; +import static com.tngtech.archunit.core.domain.properties.HasType.Predicates.rawType; import static com.tngtech.archunit.lang.SimpleConditionEvent.violated; import static org.assertj.core.api.Assertions.assertThat; @@ -22,6 +23,7 @@ import com.tngtech.archunit.core.domain.JavaCodeUnit; import com.tngtech.archunit.core.domain.JavaMethod; import com.tngtech.archunit.core.domain.JavaParameter; +import com.tngtech.archunit.core.domain.properties.HasAnnotations; import com.tngtech.archunit.core.importer.ClassFileImporter; import com.tngtech.archunit.core.importer.ImportOption; import com.tngtech.archunit.lang.ArchCondition; @@ -102,6 +104,10 @@ protected DescribedPredicate declaredClassSimpleName(Strin return equalTo(name).as("Declared in class with simple name " + name).onResultOf(unit -> unit.getOwner().getSimpleName()); } + protected > JavaAnnotation findJavaAnnotation(T item, Class annotationClass) { + return item.getAnnotations().stream().filter(rawType(annotationClass)).findAny().orElseThrow(); + } + protected ArchCondition haveAllParametersAnnotatedWithUnless(DescribedPredicate> annotationPredicate, DescribedPredicate exception) { return new ArchCondition<>("have all parameters annotated with " + annotationPredicate.getDescription()) { diff --git a/src/test/java/de/tum/in/www1/artemis/architecture/ArchitectureTest.java b/src/test/java/de/tum/in/www1/artemis/architecture/ArchitectureTest.java index 86226ad5aaf9..7caa62221c12 100644 --- a/src/test/java/de/tum/in/www1/artemis/architecture/ArchitectureTest.java +++ b/src/test/java/de/tum/in/www1/artemis/architecture/ArchitectureTest.java @@ -14,7 +14,6 @@ import static com.tngtech.archunit.core.domain.JavaCodeUnit.Predicates.constructor; import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameMatching; import static com.tngtech.archunit.core.domain.properties.HasOwner.Predicates.With.owner; -import static com.tngtech.archunit.core.domain.properties.HasType.Predicates.rawType; import static com.tngtech.archunit.lang.SimpleConditionEvent.violated; import static com.tngtech.archunit.lang.conditions.ArchPredicates.are; import static com.tngtech.archunit.lang.conditions.ArchPredicates.have; @@ -253,7 +252,7 @@ private > ArchCondition useJsonIncludeNonEmpty() @Override public void check(T item, ConditionEvents events) { - var annotation = item.getAnnotations().stream().filter(rawType(JsonInclude.class)).findAny().orElseThrow(); + var annotation = findJavaAnnotation(item, JsonInclude.class); var valueProperty = annotation.tryGetExplicitlyDeclaredProperty("value"); if (valueProperty.isEmpty()) { // @JsonInclude() is ok since it allows explicitly including properties diff --git a/src/test/java/de/tum/in/www1/artemis/architecture/ResourceArchitectureTest.java b/src/test/java/de/tum/in/www1/artemis/architecture/ResourceArchitectureTest.java index 3ef7b9410ea3..11d28d6527ee 100644 --- a/src/test/java/de/tum/in/www1/artemis/architecture/ResourceArchitectureTest.java +++ b/src/test/java/de/tum/in/www1/artemis/architecture/ResourceArchitectureTest.java @@ -1,15 +1,30 @@ package de.tum.in.www1.artemis.architecture; +import static com.tngtech.archunit.lang.ConditionEvent.createMessage; +import static com.tngtech.archunit.lang.SimpleConditionEvent.violated; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods; +import java.lang.annotation.Annotation; +import java.util.Set; + import org.junit.jupiter.api.Test; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; +import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaClasses; +import com.tngtech.archunit.core.domain.JavaMethod; +import com.tngtech.archunit.lang.ArchCondition; import com.tngtech.archunit.lang.ArchRule; +import com.tngtech.archunit.lang.ConditionEvents; import de.tum.in.www1.artemis.web.rest.ogparser.LinkPreviewResource; @@ -37,4 +52,61 @@ void allPublicMethodsShouldReturnResponseEntity() { JavaClasses classes = classesExcept(allClasses, LinkPreviewResource.class); rule.check(classes); } + + private static final Set> annotationClasses = Set.of(GetMapping.class, PatchMapping.class, PostMapping.class, PutMapping.class, DeleteMapping.class, + RequestMapping.class); + + @Test + void shouldCorrectlyUseRequestMappingAnnotations() { + classes().that().areAnnotatedWith(RequestMapping.class).should(haveCorrectRequestMappingPathForClasses()).check(productionClasses); + for (var annotation : annotationClasses) { + methods().that().areAnnotatedWith(annotation).should(haveCorrectRequestMappingPathForMethods(annotation)).allowEmptyShould(true).check(productionClasses); + } + } + + private ArchCondition haveCorrectRequestMappingPathForClasses() { + return new ArchCondition<>("correctly use @RequestMapping") { + + @Override + public void check(JavaClass javaClass, ConditionEvents conditionEvents) { + var annotation = findJavaAnnotation(javaClass, RequestMapping.class); + var valueProperty = annotation.tryGetExplicitlyDeclaredProperty("value"); + if (valueProperty.isEmpty()) { + conditionEvents.add(violated(javaClass, createMessage(javaClass, "RequestMapping should declare a path value."))); + return; + } + String[] values = ((String[]) valueProperty.get()); + for (String value : values) { + if (value.startsWith("/")) { + conditionEvents.add(violated(javaClass, createMessage(javaClass, "The @RequestMapping path value should not start with /"))); + } + if (!value.endsWith("/")) { + conditionEvents.add(violated(javaClass, createMessage(javaClass, "The @RequestMapping path value should always end with /"))); + } + } + } + }; + } + + private ArchCondition haveCorrectRequestMappingPathForMethods(Class annotationClass) { + return new ArchCondition<>("correctly use @RequestMapping") { + + @Override + public void check(JavaMethod javaMethod, ConditionEvents conditionEvents) { + var annotation = findJavaAnnotation(javaMethod, annotationClass); + var valueProperty = annotation.tryGetExplicitlyDeclaredProperty("value"); + if (valueProperty.isEmpty()) { + conditionEvents.add(violated(javaMethod, createMessage(javaMethod, "RequestMapping should declare a path value."))); + return; + } + String value = ((String[]) valueProperty.get())[0]; + if (value.startsWith("/")) { + conditionEvents.add(violated(javaMethod, createMessage(javaMethod, "The @RequestMapping path value should not start with /"))); + } + if (value.endsWith("/")) { + conditionEvents.add(violated(javaMethod, createMessage(javaMethod, "The @RequestMapping path value should not end with /"))); + } + } + }; + } } From dbbadd6b0d07ff3c01fa0cff10fcd21719695ad2 Mon Sep 17 00:00:00 2001 From: Lucas Welscher Date: Sun, 28 Apr 2024 18:01:34 +0200 Subject: [PATCH 2/6] fix lti server test --- .../config/lti/CustomLti13Configurer.java | 3 ++- .../artemis/Lti13LaunchIntegrationTest.java | 18 ++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java b/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java index 842feda9681c..eca154ca13cb 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java +++ b/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java @@ -72,7 +72,8 @@ public void configure(HttpSecurity http) { // https://www.imsglobal.org/spec/security/v1p0/#step-3-authentication-response OAuth2LoginAuthenticationFilter defaultLoginFilter = configureLoginFilter(clientRegistrationRepository(http), oidcLaunchFlowAuthenticationProvider, authorizationRequestRepository); - http.addFilterAfter(new Lti13LaunchFilter(defaultLoginFilter, LTI13_BASE_PATH + LOGIN_PATH, lti13Service(http)), AbstractPreAuthenticatedProcessingFilter.class); + String loginPath = "/" + LTI13_BASE_PATH + LOGIN_PATH; + http.addFilterAfter(new Lti13LaunchFilter(defaultLoginFilter, loginPath, lti13Service(http)), AbstractPreAuthenticatedProcessingFilter.class); } protected Lti13Service lti13Service(HttpSecurity http) { diff --git a/src/test/java/de/tum/in/www1/artemis/Lti13LaunchIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/Lti13LaunchIntegrationTest.java index 417ab7993f11..327143f249bf 100644 --- a/src/test/java/de/tum/in/www1/artemis/Lti13LaunchIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/Lti13LaunchIntegrationTest.java @@ -22,10 +22,8 @@ import org.springframework.security.test.context.support.WithAnonymousUser; import org.springframework.security.test.context.support.WithMockUser; -import de.tum.in.www1.artemis.config.lti.CustomLti13Configurer; import de.tum.in.www1.artemis.repository.UserRepository; import de.tum.in.www1.artemis.user.UserUtilService; -import de.tum.in.www1.artemis.web.rest.open.PublicLtiResource; import io.jsonwebtoken.Jwts; /** @@ -74,7 +72,7 @@ void redirectProxy() throws Exception { body.put("id_token", VALID_ID_TOKEN); body.put("state", VALID_STATE); - URI header = request.postForm(CustomLti13Configurer.LTI13_LOGIN_REDIRECT_PROXY_PATH, body, HttpStatus.FOUND); + URI header = request.postForm("/api/public/lti13/auth-callback", body, HttpStatus.FOUND); validateRedirect(header, VALID_ID_TOKEN); } @@ -85,7 +83,7 @@ void redirectProxyNoState() throws Exception { Map body = new HashMap<>(); body.put("id_token", VALID_ID_TOKEN); - request.postFormWithoutLocation(CustomLti13Configurer.LTI13_LOGIN_REDIRECT_PROXY_PATH, body, HttpStatus.BAD_REQUEST); + request.postFormWithoutLocation("/api/public/lti13/auth-callback", body, HttpStatus.BAD_REQUEST); } @Test @@ -94,7 +92,7 @@ void redirectProxyNoToken() throws Exception { Map body = new HashMap<>(); body.put("state", VALID_STATE); - request.postFormWithoutLocation(CustomLti13Configurer.LTI13_LOGIN_REDIRECT_PROXY_PATH, body, HttpStatus.BAD_REQUEST); + request.postFormWithoutLocation("/api/public/lti13/auth-callback", body, HttpStatus.BAD_REQUEST); } @Test @@ -104,7 +102,7 @@ void redirectProxyInvalidToken() throws Exception { body.put("state", VALID_STATE); body.put("id_token", "invalid-token"); - request.postFormWithoutLocation(CustomLti13Configurer.LTI13_LOGIN_REDIRECT_PROXY_PATH, body, HttpStatus.BAD_REQUEST); + request.postFormWithoutLocation("/api/public/lti13/auth-callback", body, HttpStatus.BAD_REQUEST); } @Test @@ -114,7 +112,7 @@ void redirectProxyOutdatedToken() throws Exception { body.put("state", VALID_STATE); body.put("id_token", OUTDATED_TOKEN); - request.postFormWithoutLocation(CustomLti13Configurer.LTI13_LOGIN_REDIRECT_PROXY_PATH, body, HttpStatus.BAD_REQUEST); + request.postFormWithoutLocation("/api/public/lti13/auth-callback", body, HttpStatus.BAD_REQUEST); } @Test @@ -126,19 +124,19 @@ void redirectProxyTokenInvalidSignature() throws Exception { body.put("state", VALID_STATE); body.put("id_token", invalidSignatureToken); - URI header = request.postForm(CustomLti13Configurer.LTI13_LOGIN_REDIRECT_PROXY_PATH, body, HttpStatus.FOUND); + URI header = request.postForm("/api/public/lti13/auth-callback", body, HttpStatus.FOUND); validateRedirect(header, invalidSignatureToken); } @Test @WithMockUser(value = "student1", roles = "USER") void oidcFlowFails_noRequestCached() throws Exception { - String ltiLaunchUri = CustomLti13Configurer.LTI13_LOGIN_PATH + "?id_token=some-token&state=some-state"; + String ltiLaunchUri = "/api/public/lti13/auth-login?id_token=some-token&state=some-state"; request.get(ltiLaunchUri, HttpStatus.INTERNAL_SERVER_ERROR, Object.class); } private void validateRedirect(URI locationHeader, String token) { - assertThat(locationHeader.getPath()).isEqualTo(PublicLtiResource.LOGIN_REDIRECT_CLIENT_PATH); + assertThat(locationHeader.getPath()).isEqualTo("/lti/launch"); List params = URLEncodedUtils.parse(locationHeader, StandardCharsets.UTF_8); assertUriParamsContain(params, "id_token", token); From e748c53921f9725635a4ae44623df30c985cdf8c Mon Sep 17 00:00:00 2001 From: Lucas Welscher Date: Sun, 28 Apr 2024 18:41:48 +0200 Subject: [PATCH 3/6] remove `value =` --- .../in/www1/artemis/web/rest/ModelingSubmissionResource.java | 4 ++-- .../de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java | 2 +- .../java/de/tum/in/www1/artemis/web/rest/TeamResource.java | 2 +- .../tum/in/www1/artemis/web/rest/TextSubmissionResource.java | 4 ++-- .../programming/ProgrammingExerciseParticipationResource.java | 4 ++-- .../RepositoryProgrammingExerciseParticipationResource.java | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingSubmissionResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingSubmissionResource.java index c2997f1eb6bb..57015f4e968f 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingSubmissionResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/ModelingSubmissionResource.java @@ -171,7 +171,7 @@ private ResponseEntity handleModelingSubmission(Long exercis @ResponseStatus(HttpStatus.OK) @ApiResponses({ @ApiResponse(code = 200, message = GET_200_SUBMISSIONS_REASON, response = ModelingSubmission.class, responseContainer = "List"), @ApiResponse(code = 403, message = ErrorConstants.REQ_403_REASON), @ApiResponse(code = 404, message = ErrorConstants.REQ_404_REASON), }) - @GetMapping(value = "exercises/{exerciseId}/modeling-submissions") + @GetMapping("exercises/{exerciseId}/modeling-submissions") @EnforceAtLeastTutor public ResponseEntity> getAllModelingSubmissions(@PathVariable Long exerciseId, @RequestParam(defaultValue = "false") boolean submittedOnly, @RequestParam(defaultValue = "false") boolean assessedByTutor, @RequestParam(value = "correction-round", defaultValue = "0") int correctionRound) { @@ -255,7 +255,7 @@ public ResponseEntity getModelingSubmission(@PathVariable Lo * @param correctionRound correctionRound for which submissions without a result should be returned * @return the ResponseEntity with status 200 (OK) and a modeling submission without assessment in body */ - @GetMapping(value = "exercises/{exerciseId}/modeling-submission-without-assessment") + @GetMapping("exercises/{exerciseId}/modeling-submission-without-assessment") @EnforceAtLeastTutor public ResponseEntity getModelingSubmissionWithoutAssessment(@PathVariable Long exerciseId, @RequestParam(value = "lock", defaultValue = "false") boolean lockSubmission, @RequestParam(value = "correction-round", defaultValue = "0") int correctionRound) { diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java index 8555fd1fd809..3389c3e0967f 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/QuizExerciseResource.java @@ -283,7 +283,7 @@ public ResponseEntity updateQuizExercise(@PathVariable Long exerci * @param courseId id of the course of which all exercises should be fetched * @return the ResponseEntity with status 200 (OK) and the list of quiz exercises in body */ - @GetMapping(value = "courses/{courseId}/quiz-exercises") + @GetMapping("courses/{courseId}/quiz-exercises") @EnforceAtLeastTutor public ResponseEntity> getQuizExercisesForCourse(@PathVariable Long courseId) { log.info("REST request to get all quiz exercises for the course with id : {}", courseId); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/TeamResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/TeamResource.java index 494b4b377e5d..e9c4433b35c9 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/TeamResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/TeamResource.java @@ -444,7 +444,7 @@ public ResponseEntity> importTeamsFromSourceExercise(@PathVariable lo * @param teamShortName short name of the team (all teams with the short name in the course are seen as the same team) * @return Course with exercises and participations (and latest submissions) for the team */ - @GetMapping(value = "courses/{courseId}/teams/{teamShortName}/with-exercises-and-participations") + @GetMapping("courses/{courseId}/teams/{teamShortName}/with-exercises-and-participations") @EnforceAtLeastStudent public ResponseEntity getCourseWithExercisesAndParticipationsForTeam(@PathVariable Long courseId, @PathVariable String teamShortName) { log.debug("REST request to get Course {} with exercises and participations for Team with short name {}", courseId, teamShortName); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/TextSubmissionResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/TextSubmissionResource.java index 94a7f42e57ca..6c0317c88e13 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/TextSubmissionResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/TextSubmissionResource.java @@ -187,7 +187,7 @@ public ResponseEntity getTextSubmissionWithResults(@PathVariable * @param assessedByTutor mark if only assessed Submissions should be returned * @return the ResponseEntity with status 200 (OK) and the list of textSubmissions in body */ - @GetMapping(value = "exercises/{exerciseId}/text-submissions") + @GetMapping("exercises/{exerciseId}/text-submissions") @EnforceAtLeastTutor public ResponseEntity> getAllTextSubmissions(@PathVariable Long exerciseId, @RequestParam(defaultValue = "false") boolean submittedOnly, @RequestParam(defaultValue = "false") boolean assessedByTutor, @RequestParam(value = "correction-round", defaultValue = "0") int correctionRound) { @@ -205,7 +205,7 @@ public ResponseEntity> getAllTextSubmissions(@PathVariable Long * @param lockSubmission optional value to define if the submission should be locked and has the value of false if not set manually * @return the ResponseEntity with status 200 (OK) and the list of textSubmissions in body */ - @GetMapping(value = "exercises/{exerciseId}/text-submission-without-assessment") + @GetMapping("exercises/{exerciseId}/text-submission-without-assessment") @EnforceAtLeastTutor public ResponseEntity getTextSubmissionWithoutAssessment(@PathVariable Long exerciseId, @RequestParam(value = "head", defaultValue = "false") boolean skipAssessmentOrderOptimization, diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseParticipationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseParticipationResource.java index 3c25b02549bd..6f3a3c07e3bc 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseParticipationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseParticipationResource.java @@ -132,7 +132,7 @@ public ResponseEntity getParticipationW * @return the ResponseEntity with status 200 (OK) and the latest result with feedbacks in its body, 404 if the participation can't be found or 403 if the user is not allowed * to access the participation. */ - @GetMapping(value = "programming-exercise-participations/{participationId}/latest-result-with-feedbacks") + @GetMapping("programming-exercise-participations/{participationId}/latest-result-with-feedbacks") @EnforceAtLeastStudent public ResponseEntity getLatestResultWithFeedbacksForProgrammingExerciseParticipation(@PathVariable Long participationId, @RequestParam(defaultValue = "false") boolean withSubmission) { @@ -150,7 +150,7 @@ public ResponseEntity getLatestResultWithFeedbacksForProgrammingExercise * @param participationId of the participation to check. * @return the ResponseEntity with status 200 (OK) with true if there is a result, otherwise false. */ - @GetMapping(value = "programming-exercise-participations/{participationId}/has-result") + @GetMapping("programming-exercise-participations/{participationId}/has-result") @EnforceAtLeastStudent public ResponseEntity checkIfParticipationHashResult(@PathVariable Long participationId) { boolean hasResult = resultRepository.existsByParticipationId(participationId); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/repository/RepositoryProgrammingExerciseParticipationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/repository/RepositoryProgrammingExerciseParticipationResource.java index 207f6d52d0ed..4cb5bff544dd 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/repository/RepositoryProgrammingExerciseParticipationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/repository/RepositoryProgrammingExerciseParticipationResource.java @@ -372,7 +372,7 @@ public ResponseEntity pullChanges(@PathVariable Long participationId) { * @param commit whether to commit after updating the files * @return {Map} file submissions or the appropriate http error */ - @PutMapping(value = "repository/{participationId}/files") + @PutMapping("repository/{participationId}/files") @EnforceAtLeastStudent public ResponseEntity> updateParticipationFiles(@PathVariable("participationId") Long participationId, @RequestBody List submissions, @RequestParam(defaultValue = "false") boolean commit) { From 62a787aeaf9f8abf39c5261751cb4caf9cc04d71 Mon Sep 17 00:00:00 2001 From: Lucas Welscher Date: Wed, 1 May 2024 22:12:25 +0200 Subject: [PATCH 4/6] improve code --- .../ResourceArchitectureTest.java | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/test/java/de/tum/in/www1/artemis/architecture/ResourceArchitectureTest.java b/src/test/java/de/tum/in/www1/artemis/architecture/ResourceArchitectureTest.java index 11d28d6527ee..3ef93f9d1519 100644 --- a/src/test/java/de/tum/in/www1/artemis/architecture/ResourceArchitectureTest.java +++ b/src/test/java/de/tum/in/www1/artemis/architecture/ResourceArchitectureTest.java @@ -7,6 +7,7 @@ import java.lang.annotation.Annotation; import java.util.Set; +import java.util.function.Consumer; import org.junit.jupiter.api.Test; import org.springframework.http.ResponseEntity; @@ -19,9 +20,12 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; +import com.tngtech.archunit.base.HasDescription; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.domain.JavaMethod; +import com.tngtech.archunit.core.domain.properties.HasAnnotations; +import com.tngtech.archunit.core.domain.properties.HasSourceCodeLocation; import com.tngtech.archunit.lang.ArchCondition; import com.tngtech.archunit.lang.ArchRule; import com.tngtech.archunit.lang.ConditionEvents; @@ -69,21 +73,14 @@ private ArchCondition haveCorrectRequestMappingPathForClasses() { @Override public void check(JavaClass javaClass, ConditionEvents conditionEvents) { - var annotation = findJavaAnnotation(javaClass, RequestMapping.class); - var valueProperty = annotation.tryGetExplicitlyDeclaredProperty("value"); - if (valueProperty.isEmpty()) { - conditionEvents.add(violated(javaClass, createMessage(javaClass, "RequestMapping should declare a path value."))); - return; - } - String[] values = ((String[]) valueProperty.get()); - for (String value : values) { + testRequestAnnotation(javaClass, RequestMapping.class, conditionEvents, value -> { if (value.startsWith("/")) { conditionEvents.add(violated(javaClass, createMessage(javaClass, "The @RequestMapping path value should not start with /"))); } if (!value.endsWith("/")) { conditionEvents.add(violated(javaClass, createMessage(javaClass, "The @RequestMapping path value should always end with /"))); } - } + }); } }; } @@ -93,20 +90,29 @@ private ArchCondition haveCorrectRequestMappingPathForMethods(Class< @Override public void check(JavaMethod javaMethod, ConditionEvents conditionEvents) { - var annotation = findJavaAnnotation(javaMethod, annotationClass); - var valueProperty = annotation.tryGetExplicitlyDeclaredProperty("value"); - if (valueProperty.isEmpty()) { - conditionEvents.add(violated(javaMethod, createMessage(javaMethod, "RequestMapping should declare a path value."))); - return; - } - String value = ((String[]) valueProperty.get())[0]; - if (value.startsWith("/")) { - conditionEvents.add(violated(javaMethod, createMessage(javaMethod, "The @RequestMapping path value should not start with /"))); - } - if (value.endsWith("/")) { - conditionEvents.add(violated(javaMethod, createMessage(javaMethod, "The @RequestMapping path value should not end with /"))); - } + testRequestAnnotation(javaMethod, annotationClass, conditionEvents, value -> { + if (value.startsWith("/")) { + conditionEvents.add(violated(javaMethod, createMessage(javaMethod, "The @RequestMapping path value should not start with /"))); + } + if (value.endsWith("/")) { + conditionEvents.add(violated(javaMethod, createMessage(javaMethod, "The @RequestMapping path value should not end with /"))); + } + }); } }; } + + private & HasDescription & HasSourceCodeLocation> void testRequestAnnotation(T item, Class annotationClass, ConditionEvents conditionEvents, + Consumer tester) { + var annotation = findJavaAnnotation(item, annotationClass); + var valueProperty = annotation.tryGetExplicitlyDeclaredProperty("value"); + if (valueProperty.isEmpty()) { + conditionEvents.add(violated(item, createMessage(item, "RequestMapping should declare a path value."))); + return; + } + String[] values = ((String[]) valueProperty.get()); + for (String value : values) { + tester.accept(value); + } + } } From 490b0af22ca411a3739b4158ad289768d49c9b31 Mon Sep 17 00:00:00 2001 From: Lucas Welscher Date: Thu, 2 May 2024 08:25:04 +0200 Subject: [PATCH 5/6] Update src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java Co-authored-by: Raphael Stief <118574504+rstief@users.noreply.github.com> --- .../tum/in/www1/artemis/config/lti/CustomLti13Configurer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java b/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java index eca154ca13cb..96a40a6ea352 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java +++ b/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java @@ -72,8 +72,7 @@ public void configure(HttpSecurity http) { // https://www.imsglobal.org/spec/security/v1p0/#step-3-authentication-response OAuth2LoginAuthenticationFilter defaultLoginFilter = configureLoginFilter(clientRegistrationRepository(http), oidcLaunchFlowAuthenticationProvider, authorizationRequestRepository); - String loginPath = "/" + LTI13_BASE_PATH + LOGIN_PATH; - http.addFilterAfter(new Lti13LaunchFilter(defaultLoginFilter, loginPath, lti13Service(http)), AbstractPreAuthenticatedProcessingFilter.class); + http.addFilterAfter(new Lti13LaunchFilter(defaultLoginFilter, "/" + LTI13_LOGIN_PATH, lti13Service(http)), AbstractPreAuthenticatedProcessingFilter.class); } protected Lti13Service lti13Service(HttpSecurity http) { From 927632d6288ce984785c74c6cc4ab8f7f809a5bc Mon Sep 17 00:00:00 2001 From: Lucas Welscher Date: Sun, 5 May 2024 18:55:32 +0200 Subject: [PATCH 6/6] fix violations after merge --- .../de/tum/in/www1/artemis/web/rest/CompetencyResource.java | 2 +- .../in/www1/artemis/web/rest/localci/BuildJobQueueResource.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/CompetencyResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/CompetencyResource.java index 1d9e38d75aba..506555b97604 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/CompetencyResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/CompetencyResource.java @@ -385,7 +385,7 @@ public ResponseEntity> importAllCompetencies * @return the ResponseEntity with status 201 (Created) and with body containing the imported competencies * @throws URISyntaxException if the Location URI syntax is incorrect */ - @PostMapping("/courses/{courseId}/competencies/import-standardized") + @PostMapping("courses/{courseId}/competencies/import-standardized") @EnforceAtLeastEditorInCourse public ResponseEntity> importStandardizedCompetencies(@PathVariable long courseId, @RequestBody List competencyIdsToImport) throws URISyntaxException { diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/localci/BuildJobQueueResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/localci/BuildJobQueueResource.java index 60c499270359..26cf705211b9 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/localci/BuildJobQueueResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/localci/BuildJobQueueResource.java @@ -159,7 +159,7 @@ public ResponseEntity cancelAllRunningBuildJobs(@PathVariable long courseI * @param search the search criteria * @return the page of finished build jobs */ - @GetMapping("/courses/{courseId}/finished-jobs") + @GetMapping("courses/{courseId}/finished-jobs") @EnforceAtLeastInstructorInCourse public ResponseEntity> getFinishedBuildJobsForCourse(@PathVariable long courseId, PageableSearchDTO search) { log.debug("REST request to get the finished build jobs for course {}", courseId);