From 9c2af768d3d6027f74b4303ff67c363b9855f14f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louren=C3=A7o=20Ponces=20Duarte?= Date: Tue, 9 Sep 2025 17:06:05 +0100 Subject: [PATCH 001/194] Added all the current generated code --- applications/answers/Dockerfile | 56 ++++ applications/answers/docker-compose.yml | 73 +++++ applications/answers/pom.xml | 140 ++++++++ .../socialsoftware/ms/BehaviourService.java | 54 +++ .../socialsoftware/ms/TracesService.java | 70 ++++ .../answers/AnswersSimulator.java | 42 +++ .../AnswerEventProcessing.java | 43 +++ .../CourseEventProcessing.java | 43 +++ .../CourseExecutionEventProcessing.java | 43 +++ .../QuestionEventProcessing.java | 43 +++ .../eventProcessing/QuizEventProcessing.java | 43 +++ .../eventProcessing/TopicEventProcessing.java | 43 +++ .../TournamentEventProcessing.java | 43 +++ .../eventProcessing/UserEventProcessing.java | 43 +++ .../AnswerFunctionalities.java | 48 +++ .../CourseExecutionFunctionalities.java | 45 +++ .../CourseFunctionalities.java | 42 +++ .../QuestionFunctionalities.java | 45 +++ .../functionalities/QuizFunctionalities.java | 45 +++ .../functionalities/TopicFunctionalities.java | 43 +++ .../TournamentFunctionalities.java | 50 +++ .../functionalities/UserFunctionalities.java | 43 +++ .../AnswerBusinessRuleValidator.java | 47 +++ .../validation/AnswerInvariants.java | 113 +++++++ .../AnswerValidationAnnotations.java | 152 +++++++++ .../CourseBusinessRuleValidator.java | 47 +++ .../CourseExecutionBusinessRuleValidator.java | 47 +++ .../validation/CourseExecutionInvariants.java | 140 ++++++++ .../CourseExecutionValidationAnnotations.java | 155 +++++++++ .../validation/CourseInvariants.java | 104 ++++++ .../CourseValidationAnnotations.java | 103 ++++++ .../QuestionBusinessRuleValidator.java | 47 +++ .../validation/QuestionInvariants.java | 149 +++++++++ .../QuestionValidationAnnotations.java | 172 ++++++++++ .../validation/QuizBusinessRuleValidator.java | 47 +++ .../validation/QuizInvariants.java | 167 ++++++++++ .../validation/QuizValidationAnnotations.java | 190 +++++++++++ .../StartDateEndDateRangeValidator.java | 70 ++++ .../StartTimeEndTimeRangeValidator.java | 70 ++++ .../TopicBusinessRuleValidator.java | 47 +++ .../validation/TopicInvariants.java | 77 +++++ .../TopicValidationAnnotations.java | 84 +++++ .../TournamentBusinessRuleValidator.java | 47 +++ .../validation/TournamentInvariants.java | 140 ++++++++ .../TournamentValidationAnnotations.java | 187 +++++++++++ .../validation/UserBusinessRuleValidator.java | 47 +++ .../validation/UserInvariants.java | 86 +++++ .../validation/UserValidationAnnotations.java | 85 +++++ .../validation/ValidAnswerBusinessRule.java | 37 +++ .../validation/ValidCourseBusinessRule.java | 37 +++ .../ValidCourseExecutionBusinessRule.java | 37 +++ .../validation/ValidQuestionBusinessRule.java | 37 +++ .../validation/ValidQuizBusinessRule.java | 37 +++ .../ValidStartDateEndDateRange.java | 37 +++ .../ValidStartTimeEndTimeRange.java | 37 +++ .../validation/ValidTopicBusinessRule.java | 37 +++ .../ValidTournamentBusinessRule.java | 37 +++ .../validation/ValidUserBusinessRule.java | 37 +++ .../coordination/webapi/AnswerController.java | 60 ++++ .../webapi/BehaviourController.java | 44 +++ .../coordination/webapi/CourseController.java | 60 ++++ .../webapi/CourseExecutionController.java | 60 ++++ .../webapi/QuestionController.java | 60 ++++ .../coordination/webapi/QuizController.java | 60 ++++ .../coordination/webapi/TopicController.java | 60 ++++ .../webapi/TournamentController.java | 44 +++ .../coordination/webapi/TracesController.java | 56 ++++ .../coordination/webapi/UserController.java | 60 ++++ .../answer/aggregate/Answer.java | 24 ++ .../aggregate/AnswerCustomRepository.java | 9 + .../answer/aggregate/AnswerFactory.java | 37 +++ .../answer/aggregate/AnswerRepository.java | 13 + .../answer/aggregate/AnsweredQuiz.java | 80 +++++ .../answer/aggregate/AnsweredQuizDto.java | 76 +++++ .../answer/aggregate/QuestionAnswer.java | 60 ++++ .../answer/aggregate/QuestionAnswerDto.java | 56 ++++ .../answer/aggregate/QuizAnswer.java | 120 +++++++ .../aggregate/QuizAnswerCourseExecution.java | 59 ++++ .../QuizAnswerCourseExecutionDto.java | 55 ++++ .../answer/aggregate/QuizAnswerDto.java | 116 +++++++ .../answer/aggregate/QuizAnswerStudent.java | 59 ++++ .../aggregate/QuizAnswerStudentDto.java | 55 ++++ .../events/handling/AnswerEventHandling.java | 192 +++++++++++ .../handling/handlers/AnswerEventHandler.java | 38 +++ .../handling/handlers/CreatedHandler.java | 42 +++ .../handling/handlers/DeletedHandler.java | 42 +++ .../handling/handlers/UpdatedHandler.java | 42 +++ .../events/publish/AnswerCreatedEvent.java | 115 +++++++ .../events/publish/AnswerDeletedEvent.java | 115 +++++++ .../events/publish/AnswerUpdatedEvent.java | 115 +++++++ .../events/subscribe/SubscribesCreated.java | 86 +++++ .../events/subscribe/SubscribesDeleted.java | 86 +++++ .../events/subscribe/SubscribesUpdated.java | 86 +++++ .../answer/service/AnswerService.java | 68 ++++ .../course/aggregate/Course.java | 86 +++++ .../aggregate/CourseCustomRepository.java | 9 + .../course/aggregate/CourseDto.java | 85 +++++ .../course/aggregate/CourseFactory.java | 34 ++ .../course/aggregate/CourseRepository.java | 22 ++ .../events/handling/CourseEventHandling.java | 192 +++++++++++ .../handling/handlers/CourseEventHandler.java | 38 +++ .../handling/handlers/CreatedHandler.java | 42 +++ .../handling/handlers/DeletedHandler.java | 42 +++ .../handling/handlers/UpdatedHandler.java | 42 +++ .../events/publish/CourseCreatedEvent.java | 94 ++++++ .../events/publish/CourseDeletedEvent.java | 94 ++++++ .../events/publish/CourseUpdatedEvent.java | 94 ++++++ .../events/subscribe/SubscribesCreated.java | 86 +++++ .../events/subscribe/SubscribesDeleted.java | 86 +++++ .../events/subscribe/SubscribesUpdated.java | 86 +++++ .../course/service/CourseService.java | 174 ++++++++++ .../aggregate/CourseExecution.java | 131 ++++++++ .../aggregate/CourseExecutionCourse.java | 59 ++++ .../aggregate/CourseExecutionCourseDto.java | 55 ++++ .../CourseExecutionCustomRepository.java | 9 + .../aggregate/CourseExecutionDto.java | 116 +++++++ .../aggregate/CourseExecutionFactory.java | 37 +++ .../aggregate/CourseExecutionRepository.java | 22 ++ .../aggregate/CourseExecutionStudent.java | 70 ++++ .../aggregate/CourseExecutionStudentDto.java | 66 ++++ .../CourseExecutionEventHandling.java | 192 +++++++++++ .../handlers/CourseExecutionEventHandler.java | 38 +++ .../handling/handlers/CreatedHandler.java | 42 +++ .../handling/handlers/DeletedHandler.java | 42 +++ .../handling/handlers/UpdatedHandler.java | 42 +++ .../publish/CourseExecutionCreatedEvent.java | 115 +++++++ .../publish/CourseExecutionDeletedEvent.java | 115 +++++++ .../publish/CourseExecutionUpdatedEvent.java | 115 +++++++ .../events/subscribe/SubscribesCreated.java | 86 +++++ .../events/subscribe/SubscribesDeleted.java | 86 +++++ .../events/subscribe/SubscribesUpdated.java | 86 +++++ .../service/CourseExecutionService.java | 227 +++++++++++++ .../exception/AnswersErrorMessage.java | 158 +++++++++ .../exception/AnswersException.java | 53 +++ .../exception/AnswersExceptionHandler.java | 32 ++ .../question/aggregate/Option.java | 49 +++ .../question/aggregate/OptionDto.java | 45 +++ .../question/aggregate/Question.java | 135 ++++++++ .../question/aggregate/QuestionCourse.java | 49 +++ .../question/aggregate/QuestionCourseDto.java | 45 +++ .../aggregate/QuestionCustomRepository.java | 9 + .../question/aggregate/QuestionDto.java | 125 +++++++ .../question/aggregate/QuestionFactory.java | 38 +++ .../aggregate/QuestionRepository.java | 19 ++ .../question/aggregate/QuestionTopic.java | 39 +++ .../question/aggregate/QuestionTopicDto.java | 35 ++ .../handling/QuestionEventHandling.java | 192 +++++++++++ .../handling/handlers/CreatedHandler.java | 42 +++ .../handling/handlers/DeletedHandler.java | 42 +++ .../handlers/QuestionEventHandler.java | 38 +++ .../handling/handlers/UpdatedHandler.java | 42 +++ .../events/publish/QuestionCreatedEvent.java | 122 +++++++ .../events/publish/QuestionDeletedEvent.java | 122 +++++++ .../events/publish/QuestionUpdatedEvent.java | 122 +++++++ .../events/subscribe/SubscribesCreated.java | 86 +++++ .../events/subscribe/SubscribesDeleted.java | 86 +++++ .../events/subscribe/SubscribesUpdated.java | 86 +++++ .../question/service/QuestionService.java | 218 ++++++++++++ .../microservices/quiz/aggregate/Quiz.java | 146 +++++++++ .../quiz/aggregate/QuizCourseExecution.java | 59 ++++ .../aggregate/QuizCourseExecutionDto.java | 55 ++++ .../quiz/aggregate/QuizCustomRepository.java | 9 + .../microservices/quiz/aggregate/QuizDto.java | 136 ++++++++ .../quiz/aggregate/QuizFactory.java | 39 +++ .../quiz/aggregate/QuizOption.java | 49 +++ .../quiz/aggregate/QuizOptionDto.java | 45 +++ .../quiz/aggregate/QuizQuestion.java | 59 ++++ .../quiz/aggregate/QuizQuestionDto.java | 55 ++++ .../quiz/aggregate/QuizRepository.java | 22 ++ .../events/handling/QuizEventHandling.java | 192 +++++++++++ .../handling/handlers/CreatedHandler.java | 42 +++ .../handling/handlers/DeletedHandler.java | 42 +++ .../handling/handlers/QuizEventHandler.java | 38 +++ .../handling/handlers/UpdatedHandler.java | 42 +++ .../quiz/events/publish/QuizCreatedEvent.java | 129 ++++++++ .../quiz/events/publish/QuizDeletedEvent.java | 129 ++++++++ .../quiz/events/publish/QuizUpdatedEvent.java | 129 ++++++++ .../events/subscribe/SubscribesCreated.java | 86 +++++ .../events/subscribe/SubscribesDeleted.java | 86 +++++ .../events/subscribe/SubscribesUpdated.java | 86 +++++ .../quiz/service/QuizService.java | 264 +++++++++++++++ .../microservices/topic/aggregate/Topic.java | 81 +++++ .../topic/aggregate/TopicCourse.java | 49 +++ .../topic/aggregate/TopicCourseDto.java | 45 +++ .../aggregate/TopicCustomRepository.java | 9 + .../topic/aggregate/TopicDto.java | 75 +++++ .../topic/aggregate/TopicFactory.java | 33 ++ .../topic/aggregate/TopicRepository.java | 16 + .../events/handling/TopicEventHandling.java | 192 +++++++++++ .../handling/handlers/CreatedHandler.java | 42 +++ .../handling/handlers/DeletedHandler.java | 42 +++ .../handling/handlers/TopicEventHandler.java | 38 +++ .../handling/handlers/UpdatedHandler.java | 42 +++ .../events/publish/TopicCreatedEvent.java | 87 +++++ .../events/publish/TopicDeletedEvent.java | 87 +++++ .../events/publish/TopicUpdatedEvent.java | 87 +++++ .../events/subscribe/SubscribesCreated.java | 86 +++++ .../events/subscribe/SubscribesDeleted.java | 86 +++++ .../events/subscribe/SubscribesUpdated.java | 86 +++++ .../topic/service/TopicService.java | 178 ++++++++++ .../tournament/aggregate/Tournament.java | 169 ++++++++++ .../aggregate/TournamentCourseExecution.java | 89 +++++ .../TournamentCourseExecutionDto.java | 75 +++++ .../aggregate/TournamentCreator.java | 102 ++++++ .../aggregate/TournamentCreatorDto.java | 75 +++++ .../aggregate/TournamentCustomRepository.java | 9 + .../tournament/aggregate/TournamentDto.java | 136 ++++++++ .../aggregate/TournamentFactory.java | 39 +++ .../aggregate/TournamentParticipant.java | 110 +++++++ .../aggregate/TournamentParticipantDto.java | 96 ++++++ .../TournamentParticipantQuizAnswer.java | 89 +++++ .../TournamentParticipantQuizAnswerDto.java | 75 +++++ .../tournament/aggregate/TournamentQuiz.java | 59 ++++ .../aggregate/TournamentQuizDto.java | 45 +++ .../aggregate/TournamentRepository.java | 13 + .../tournament/aggregate/TournamentTopic.java | 89 +++++ .../aggregate/TournamentTopicDto.java | 75 +++++ .../handling/TournamentEventHandling.java | 192 +++++++++++ .../handling/handlers/CreatedHandler.java | 42 +++ .../handling/handlers/DeletedHandler.java | 42 +++ .../handlers/TournamentEventHandler.java | 38 +++ .../handling/handlers/UpdatedHandler.java | 42 +++ .../publish/TournamentCreatedEvent.java | 129 ++++++++ .../publish/TournamentDeletedEvent.java | 129 ++++++++ .../publish/TournamentUpdatedEvent.java | 129 ++++++++ .../events/subscribe/SubscribesCreated.java | 86 +++++ .../events/subscribe/SubscribesDeleted.java | 86 +++++ .../events/subscribe/SubscribesUpdated.java | 86 +++++ .../tournament/service/TournamentService.java | 309 ++++++++++++++++++ .../microservices/user/aggregate/User.java | 52 +++ .../user/aggregate/UserCustomRepository.java | 9 + .../microservices/user/aggregate/UserDto.java | 74 +++++ .../user/aggregate/UserFactory.java | 33 ++ .../user/aggregate/UserRepository.java | 19 ++ .../events/handling/UserEventHandling.java | 192 +++++++++++ .../handling/handlers/CreatedHandler.java | 42 +++ .../handling/handlers/DeletedHandler.java | 42 +++ .../handling/handlers/UpdatedHandler.java | 42 +++ .../handling/handlers/UserEventHandler.java | 38 +++ .../user/events/publish/UserCreatedEvent.java | 87 +++++ .../user/events/publish/UserDeletedEvent.java | 87 +++++ .../user/events/publish/UserUpdatedEvent.java | 87 +++++ .../events/subscribe/SubscribesCreated.java | 86 +++++ .../events/subscribe/SubscribesDeleted.java | 86 +++++ .../events/subscribe/SubscribesUpdated.java | 86 +++++ .../user/service/UserService.java | 138 ++++++++ .../src/main/resources/application.properties | 23 ++ .../src/main/resources/application.yml | 26 ++ .../src/main/resources/database.properties | 37 +++ .../src/main/resources/logback-spring.xml | 86 +++++ .../tecnico/socialsoftware/SpockTest.groovy | 6 + .../answers/AnswersBeanConfiguration.groovy | 19 ++ .../answers/AnswersSpockTest.groovy | 27 ++ .../builders/AnsweredQuizBuilder.groovy | 59 ++++ .../builders/AnsweredQuizDtoBuilder.groovy | 57 ++++ .../builders/QuestionAnswerBuilder.groovy | 47 +++ .../builders/QuestionAnswerDtoBuilder.groovy | 45 +++ .../answers/builders/QuizAnswerBuilder.groovy | 61 ++++ .../QuizAnswerCourseExecutionBuilder.groovy | 47 +++ ...QuizAnswerCourseExecutionDtoBuilder.groovy | 45 +++ .../builders/QuizAnswerDtoBuilder.groovy | 59 ++++ .../builders/QuizAnswerStudentBuilder.groovy | 47 +++ .../QuizAnswerStudentDtoBuilder.groovy | 45 +++ .../answers/webapi/AnswerRestApiTest.groovy | 100 ++++++ .../target/classes/application.properties | 23 ++ .../answers/target/classes/application.yml | 26 ++ .../target/classes/database.properties | 37 +++ .../answers/target/classes/logback-spring.xml | 86 +++++ .../socialsoftware/ms/BehaviourService.class | Bin 0 -> 1691 bytes .../socialsoftware/ms/TracesService.class | Bin 0 -> 1853 bytes .../answers/AnswersSimulator.class | Bin 0 -> 1863 bytes .../AnswerEventProcessing.class | Bin 0 -> 2902 bytes .../CourseEventProcessing.class | Bin 0 -> 2902 bytes .../CourseExecutionEventProcessing.class | Bin 0 -> 2974 bytes .../QuestionEventProcessing.class | Bin 0 -> 2918 bytes .../eventProcessing/QuizEventProcessing.class | Bin 0 -> 2886 bytes .../TopicEventProcessing.class | Bin 0 -> 2894 bytes .../TournamentEventProcessing.class | Bin 0 -> 2934 bytes .../eventProcessing/UserEventProcessing.class | Bin 0 -> 2886 bytes .../AnswerFunctionalities.class | Bin 0 -> 3190 bytes .../CourseExecutionFunctionalities.class | Bin 0 -> 3190 bytes .../CourseFunctionalities.class | Bin 0 -> 2690 bytes .../QuestionFunctionalities.class | Bin 0 -> 3037 bytes .../functionalities/QuizFunctionalities.class | Bin 0 -> 3069 bytes .../TopicFunctionalities.class | Bin 0 -> 2770 bytes .../TournamentFunctionalities.class | Bin 0 -> 3590 bytes .../functionalities/UserFunctionalities.class | Bin 0 -> 2232 bytes .../AnswerBusinessRuleValidator.class | Bin 0 -> 1766 bytes .../validation/AnswerInvariants.class | Bin 0 -> 2178 bytes ...tionAnnotations$AnswerDateValidation.class | Bin 0 -> 1467 bytes ...onAnnotations$AnsweredQuizValidation.class | Bin 0 -> 1458 bytes ...nAnnotations$CompletedDateValidation.class | Bin 0 -> 1485 bytes ...ationAnnotations$CompletedValidation.class | Bin 0 -> 1443 bytes ...nnotations$QuestionAnswersValidation.class | Bin 0 -> 1530 bytes ...$QuizAnswerCourseExecutionValidation.class | Bin 0 -> 1536 bytes ...otations$QuizAnswerStudentValidation.class | Bin 0 -> 1488 bytes .../AnswerValidationAnnotations.class | Bin 0 -> 1959 bytes .../CourseBusinessRuleValidator.class | Bin 0 -> 1766 bytes ...CourseExecutionBusinessRuleValidator.class | Bin 0 -> 1910 bytes .../CourseExecutionInvariants.class | Bin 0 -> 2650 bytes ...onAnnotations$AcademicTermValidation.class | Bin 0 -> 1533 bytes ...idationAnnotations$AcronymValidation.class | Bin 0 -> 1503 bytes ...lidationAnnotations$CourseValidation.class | Bin 0 -> 1467 bytes ...idationAnnotations$EndDateValidation.class | Bin 0 -> 1494 bytes ...ValidationAnnotations$NameValidation.class | Bin 0 -> 1485 bytes ...ationAnnotations$StartDateValidation.class | Bin 0 -> 1506 bytes ...dationAnnotations$StudentsValidation.class | Bin 0 -> 1533 bytes ...CourseExecutionValidationAnnotations.class | Bin 0 -> 1962 bytes .../validation/CourseInvariants.class | Bin 0 -> 1947 bytes ...idationAnnotations$AcronymValidation.class | Bin 0 -> 1458 bytes ...tionAnnotations$CourseTypeValidation.class | Bin 0 -> 1476 bytes ...onAnnotations$CreationDateValidation.class | Bin 0 -> 1479 bytes ...ValidationAnnotations$NameValidation.class | Bin 0 -> 1440 bytes .../CourseValidationAnnotations.class | Bin 0 -> 1424 bytes .../QuestionBusinessRuleValidator.class | Bin 0 -> 1798 bytes .../validation/QuestionInvariants.class | Bin 0 -> 2640 bytes ...idationAnnotations$ContentValidation.class | Bin 0 -> 1468 bytes ...nAnnotations$CorrectOptionValidation.class | Bin 0 -> 1477 bytes ...lidationAnnotations$CourseValidation.class | Bin 0 -> 1432 bytes ...nnotations$NumberOfOptionsValidation.class | Bin 0 -> 1489 bytes ...idationAnnotations$OptionsValidation.class | Bin 0 -> 1492 bytes ...alidationAnnotations$OrderValidation.class | Bin 0 -> 1429 bytes ...alidationAnnotations$TitleValidation.class | Bin 0 -> 1456 bytes ...lidationAnnotations$TopicsValidation.class | Bin 0 -> 1486 bytes .../QuestionValidationAnnotations.class | Bin 0 -> 2042 bytes .../QuizBusinessRuleValidator.class | Bin 0 -> 1734 bytes .../validation/QuizInvariants.class | Bin 0 -> 2849 bytes ...nAnnotations$AvailableDateValidation.class | Bin 0 -> 1475 bytes ...Annotations$ConclusionDateValidation.class | Bin 0 -> 1481 bytes ...nnotations$CourseExecutionValidation.class | Bin 0 -> 1466 bytes ...ionAnnotations$DescriptionValidation.class | Bin 0 -> 1472 bytes ...otations$NumberOfQuestionsValidation.class | Bin 0 -> 1481 bytes ...idationAnnotations$OptionsValidation.class | Bin 0 -> 1472 bytes ...ationAnnotations$QuestionsValidation.class | Bin 0 -> 1484 bytes ...dationAnnotations$QuizTypeValidation.class | Bin 0 -> 1454 bytes ...alidationAnnotations$TitleValidation.class | Bin 0 -> 1436 bytes .../QuizValidationAnnotations.class | Bin 0 -> 2195 bytes .../StartDateEndDateRangeValidator.class | Bin 0 -> 1868 bytes .../StartTimeEndTimeRangeValidator.class | Bin 0 -> 1823 bytes .../TopicBusinessRuleValidator.class | Bin 0 -> 1750 bytes .../validation/TopicInvariants.class | Bin 0 -> 1535 bytes ...lidationAnnotations$CourseValidation.class | Bin 0 -> 1417 bytes ...onAnnotations$CreationDateValidation.class | Bin 0 -> 1474 bytes ...ValidationAnnotations$NameValidation.class | Bin 0 -> 1435 bytes .../TopicValidationAnnotations.class | Bin 0 -> 1262 bytes .../TournamentBusinessRuleValidator.class | Bin 0 -> 1830 bytes .../validation/TournamentInvariants.class | Bin 0 -> 2638 bytes ...ationAnnotations$CancelledValidation.class | Bin 0 -> 1463 bytes ...idationAnnotations$EndTimeValidation.class | Bin 0 -> 1469 bytes ...otations$NumberOfQuestionsValidation.class | Bin 0 -> 1511 bytes ...ationAnnotations$StartTimeValidation.class | Bin 0 -> 1481 bytes ...$TournamentCourseExecutionValidation.class | Bin 0 -> 1556 bytes ...otations$TournamentCreatorValidation.class | Bin 0 -> 1508 bytes ...ons$TournamentParticipantsValidation.class | Bin 0 -> 1592 bytes ...Annotations$TournamentQuizValidation.class | Bin 0 -> 1490 bytes ...notations$TournamentTopicsValidation.class | Bin 0 -> 1556 bytes .../TournamentValidationAnnotations.class | Bin 0 -> 2347 bytes .../UserBusinessRuleValidator.class | Bin 0 -> 1734 bytes .../validation/UserInvariants.class | Bin 0 -> 1648 bytes ...lidationAnnotations$ActiveValidation.class | Bin 0 -> 1415 bytes ...ValidationAnnotations$NameValidation.class | Bin 0 -> 1430 bytes ...dationAnnotations$UsernameValidation.class | Bin 0 -> 1454 bytes .../UserValidationAnnotations.class | Bin 0 -> 1247 bytes .../validation/ValidAnswerBusinessRule.class | Bin 0 -> 1358 bytes .../validation/ValidCourseBusinessRule.class | Bin 0 -> 1358 bytes .../ValidCourseExecutionBusinessRule.class | Bin 0 -> 1394 bytes .../ValidQuestionBusinessRule.class | Bin 0 -> 1366 bytes .../validation/ValidQuizBusinessRule.class | Bin 0 -> 1350 bytes .../ValidStartDateEndDateRange.class | Bin 0 -> 1376 bytes .../ValidStartTimeEndTimeRange.class | Bin 0 -> 1371 bytes .../validation/ValidTopicBusinessRule.class | Bin 0 -> 1354 bytes .../ValidTournamentBusinessRule.class | Bin 0 -> 1374 bytes .../validation/ValidUserBusinessRule.class | Bin 0 -> 1350 bytes .../webapi/AnswerController.class | Bin 0 -> 3814 bytes .../webapi/BehaviourController.class | Bin 0 -> 2254 bytes .../webapi/CourseController.class | Bin 0 -> 4252 bytes .../webapi/CourseExecutionController.class | Bin 0 -> 4697 bytes .../webapi/QuestionController.class | Bin 0 -> 4372 bytes .../coordination/webapi/QuizController.class | Bin 0 -> 4208 bytes .../coordination/webapi/TopicController.class | Bin 0 -> 4198 bytes .../webapi/TournamentController.class | Bin 0 -> 2615 bytes .../webapi/TracesController.class | Bin 0 -> 2808 bytes .../coordination/webapi/UserController.class | Bin 0 -> 4146 bytes .../answer/aggregate/Answer.class | Bin 0 -> 840 bytes .../aggregate/AnswerCustomRepository.class | Bin 0 -> 482 bytes .../answer/aggregate/AnswerFactory.class | Bin 0 -> 2076 bytes .../answer/aggregate/AnswerRepository.class | Bin 0 -> 573 bytes .../answer/aggregate/AnsweredQuiz.class | Bin 0 -> 2512 bytes .../answer/aggregate/AnsweredQuizDto.class | Bin 0 -> 2516 bytes .../answer/aggregate/QuestionAnswer.class | Bin 0 -> 1934 bytes .../answer/aggregate/QuestionAnswerDto.class | Bin 0 -> 1992 bytes .../answer/aggregate/QuizAnswer.class | Bin 0 -> 4278 bytes .../aggregate/QuizAnswerCourseExecution.class | Bin 0 -> 2068 bytes .../QuizAnswerCourseExecutionDto.class | Bin 0 -> 2166 bytes .../answer/aggregate/QuizAnswerDto.class | Bin 0 -> 4047 bytes .../answer/aggregate/QuizAnswerStudent.class | Bin 0 -> 1922 bytes .../aggregate/QuizAnswerStudentDto.class | Bin 0 -> 1996 bytes .../events/handling/AnswerEventHandling.class | Bin 0 -> 5192 bytes .../handlers/AnswerEventHandler.class | Bin 0 -> 1523 bytes .../handling/handlers/CreatedHandler.class | Bin 0 -> 1882 bytes .../handling/handlers/DeletedHandler.class | Bin 0 -> 1882 bytes .../handling/handlers/UpdatedHandler.class | Bin 0 -> 1882 bytes .../events/publish/AnswerCreatedEvent.class | Bin 0 -> 2979 bytes .../events/publish/AnswerDeletedEvent.class | Bin 0 -> 2979 bytes .../events/publish/AnswerUpdatedEvent.class | Bin 0 -> 2979 bytes .../subscribe/CreatedSubscription.class | Bin 0 -> 2930 bytes .../subscribe/DeletedSubscription.class | Bin 0 -> 2930 bytes .../subscribe/UpdatedSubscription.class | Bin 0 -> 2930 bytes .../answer/service/AnswerService.class | Bin 0 -> 3591 bytes .../course/aggregate/Course.class | Bin 0 -> 3620 bytes .../aggregate/CourseCustomRepository.class | Bin 0 -> 397 bytes .../course/aggregate/CourseDto.class | Bin 0 -> 3072 bytes .../course/aggregate/CourseFactory.class | Bin 0 -> 2322 bytes .../course/aggregate/CourseRepository.class | Bin 0 -> 1841 bytes .../events/handling/CourseEventHandling.class | Bin 0 -> 5192 bytes .../handlers/CourseEventHandler.class | Bin 0 -> 1523 bytes .../handling/handlers/CreatedHandler.class | Bin 0 -> 1882 bytes .../handling/handlers/DeletedHandler.class | Bin 0 -> 1882 bytes .../handling/handlers/UpdatedHandler.class | Bin 0 -> 1882 bytes .../events/publish/CourseCreatedEvent.class | Bin 0 -> 2430 bytes .../events/publish/CourseDeletedEvent.class | Bin 0 -> 2430 bytes .../events/publish/CourseUpdatedEvent.class | Bin 0 -> 2430 bytes .../subscribe/CreatedSubscription.class | Bin 0 -> 2930 bytes .../subscribe/DeletedSubscription.class | Bin 0 -> 2930 bytes .../subscribe/UpdatedSubscription.class | Bin 0 -> 2930 bytes .../course/service/CourseService.class | Bin 0 -> 7976 bytes .../aggregate/CourseExecution.class | Bin 0 -> 5275 bytes .../aggregate/CourseExecutionCourse.class | Bin 0 -> 1947 bytes .../aggregate/CourseExecutionCourseDto.class | Bin 0 -> 2051 bytes .../CourseExecutionCustomRepository.class | Bin 0 -> 507 bytes .../aggregate/CourseExecutionDto.class | Bin 0 -> 3917 bytes .../aggregate/CourseExecutionFactory.class | Bin 0 -> 2795 bytes .../aggregate/CourseExecutionRepository.class | Bin 0 -> 2101 bytes .../aggregate/CourseExecutionStudent.class | Bin 0 -> 2330 bytes .../aggregate/CourseExecutionStudentDto.class | Bin 0 -> 2410 bytes .../CourseExecutionEventHandling.class | Bin 0 -> 5615 bytes .../CourseExecutionEventHandler.class | Bin 0 -> 1658 bytes .../handling/handlers/CreatedHandler.class | Bin 0 -> 2017 bytes .../handling/handlers/DeletedHandler.class | Bin 0 -> 2017 bytes .../handling/handlers/UpdatedHandler.class | Bin 0 -> 2017 bytes .../publish/CourseExecutionCreatedEvent.class | Bin 0 -> 2899 bytes .../publish/CourseExecutionDeletedEvent.class | Bin 0 -> 2899 bytes .../publish/CourseExecutionUpdatedEvent.class | Bin 0 -> 2899 bytes .../subscribe/CreatedSubscription.class | Bin 0 -> 3092 bytes .../subscribe/DeletedSubscription.class | Bin 0 -> 3092 bytes .../subscribe/UpdatedSubscription.class | Bin 0 -> 3092 bytes .../service/CourseExecutionService.class | Bin 0 -> 10813 bytes .../exception/AnswersErrorMessage.class | Bin 0 -> 7528 bytes .../exception/AnswersException.class | Bin 0 -> 2344 bytes .../exception/AnswersExceptionHandler.class | Bin 0 -> 3176 bytes .../question/aggregate/Option.class | Bin 0 -> 1648 bytes .../question/aggregate/OptionDto.class | Bin 0 -> 1776 bytes .../question/aggregate/Question.class | Bin 0 -> 5077 bytes .../question/aggregate/QuestionCourse.class | Bin 0 -> 1645 bytes .../aggregate/QuestionCourseDto.class | Bin 0 -> 1734 bytes .../aggregate/QuestionCustomRepository.class | Bin 0 -> 409 bytes .../question/aggregate/QuestionDto.class | Bin 0 -> 3980 bytes .../question/aggregate/QuestionFactory.class | Bin 0 -> 2600 bytes .../aggregate/QuestionRepository.class | Bin 0 -> 1655 bytes .../question/aggregate/QuestionTopic.class | Bin 0 -> 1347 bytes .../question/aggregate/QuestionTopicDto.class | Bin 0 -> 1452 bytes .../handling/QuestionEventHandling.class | Bin 0 -> 5286 bytes .../handling/handlers/CreatedHandler.class | Bin 0 -> 1912 bytes .../handling/handlers/DeletedHandler.class | Bin 0 -> 1912 bytes .../handlers/QuestionEventHandler.class | Bin 0 -> 1553 bytes .../handling/handlers/UpdatedHandler.class | Bin 0 -> 1912 bytes .../events/publish/QuestionCreatedEvent.class | Bin 0 -> 3012 bytes .../events/publish/QuestionDeletedEvent.class | Bin 0 -> 3012 bytes .../events/publish/QuestionUpdatedEvent.class | Bin 0 -> 3012 bytes .../subscribe/CreatedSubscription.class | Bin 0 -> 2966 bytes .../subscribe/DeletedSubscription.class | Bin 0 -> 2966 bytes .../subscribe/UpdatedSubscription.class | Bin 0 -> 2966 bytes .../question/service/QuestionService.class | Bin 0 -> 9987 bytes .../microservices/quiz/aggregate/Quiz.class | Bin 0 -> 5653 bytes .../quiz/aggregate/QuizCourseExecution.class | Bin 0 -> 2038 bytes .../aggregate/QuizCourseExecutionDto.class | Bin 0 -> 2114 bytes .../quiz/aggregate/QuizCustomRepository.class | Bin 0 -> 192 bytes .../quiz/aggregate/QuizDto.class | Bin 0 -> 4329 bytes .../quiz/aggregate/QuizFactory.class | Bin 0 -> 2587 bytes .../quiz/aggregate/QuizOption.class | Bin 0 -> 1652 bytes .../quiz/aggregate/QuizOptionDto.class | Bin 0 -> 1784 bytes .../quiz/aggregate/QuizQuestion.class | Bin 0 -> 1858 bytes .../quiz/aggregate/QuizQuestionDto.class | Bin 0 -> 1912 bytes .../quiz/aggregate/QuizRepository.class | Bin 0 -> 1797 bytes .../events/handling/QuizEventHandling.class | Bin 0 -> 5098 bytes .../handling/handlers/CreatedHandler.class | Bin 0 -> 1852 bytes .../handling/handlers/DeletedHandler.class | Bin 0 -> 1852 bytes .../handling/handlers/QuizEventHandler.class | Bin 0 -> 1493 bytes .../handling/handlers/UpdatedHandler.class | Bin 0 -> 1852 bytes .../events/publish/QuizCreatedEvent.class | Bin 0 -> 3171 bytes .../events/publish/QuizDeletedEvent.class | Bin 0 -> 3171 bytes .../events/publish/QuizUpdatedEvent.class | Bin 0 -> 3171 bytes .../subscribe/CreatedSubscription.class | Bin 0 -> 2894 bytes .../subscribe/DeletedSubscription.class | Bin 0 -> 2894 bytes .../subscribe/UpdatedSubscription.class | Bin 0 -> 2894 bytes .../quiz/service/QuizService.class | Bin 0 -> 11101 bytes .../microservices/topic/aggregate/Topic.class | Bin 0 -> 3445 bytes .../topic/aggregate/TopicCourse.class | Bin 0 -> 1624 bytes .../topic/aggregate/TopicCourseDto.class | Bin 0 -> 1698 bytes .../aggregate/TopicCustomRepository.class | Bin 0 -> 392 bytes .../topic/aggregate/TopicDto.class | Bin 0 -> 2892 bytes .../topic/aggregate/TopicFactory.class | Bin 0 -> 2259 bytes .../topic/aggregate/TopicRepository.class | Bin 0 -> 1375 bytes .../events/handling/TopicEventHandling.class | Bin 0 -> 5145 bytes .../handling/handlers/CreatedHandler.class | Bin 0 -> 1867 bytes .../handling/handlers/DeletedHandler.class | Bin 0 -> 1867 bytes .../handling/handlers/TopicEventHandler.class | Bin 0 -> 1508 bytes .../handling/handlers/UpdatedHandler.class | Bin 0 -> 1867 bytes .../events/publish/TopicCreatedEvent.class | Bin 0 -> 2340 bytes .../events/publish/TopicDeletedEvent.class | Bin 0 -> 2340 bytes .../events/publish/TopicUpdatedEvent.class | Bin 0 -> 2340 bytes .../subscribe/CreatedSubscription.class | Bin 0 -> 2912 bytes .../subscribe/DeletedSubscription.class | Bin 0 -> 2912 bytes .../subscribe/UpdatedSubscription.class | Bin 0 -> 2912 bytes .../topic/service/TopicService.class | Bin 0 -> 8504 bytes .../tournament/aggregate/Tournament.class | Bin 0 -> 6153 bytes .../aggregate/TournamentCourseExecution.class | Bin 0 -> 2975 bytes .../TournamentCourseExecutionDto.class | Bin 0 -> 2750 bytes .../aggregate/TournamentCreator.class | Bin 0 -> 4221 bytes .../aggregate/TournamentCreatorDto.class | Bin 0 -> 3017 bytes .../TournamentCustomRepository.class | Bin 0 -> 421 bytes .../tournament/aggregate/TournamentDto.class | Bin 0 -> 4578 bytes .../aggregate/TournamentFactory.class | Bin 0 -> 2170 bytes .../aggregate/TournamentParticipant.class | Bin 0 -> 3993 bytes .../aggregate/TournamentParticipantDto.class | Bin 0 -> 3648 bytes .../TournamentParticipantQuizAnswer.class | Bin 0 -> 2925 bytes .../TournamentParticipantQuizAnswerDto.class | Bin 0 -> 2780 bytes .../tournament/aggregate/TournamentQuiz.class | Bin 0 -> 1929 bytes .../aggregate/TournamentQuizDto.class | Bin 0 -> 1732 bytes .../aggregate/TournamentRepository.class | Bin 0 -> 593 bytes .../aggregate/TournamentTopic.class | Bin 0 -> 3302 bytes .../aggregate/TournamentTopicDto.class | Bin 0 -> 2985 bytes .../handling/TournamentEventHandling.class | Bin 0 -> 5380 bytes .../handling/handlers/CreatedHandler.class | Bin 0 -> 1942 bytes .../handling/handlers/DeletedHandler.class | Bin 0 -> 1942 bytes .../handlers/TournamentEventHandler.class | Bin 0 -> 1583 bytes .../handling/handlers/UpdatedHandler.class | Bin 0 -> 1942 bytes .../publish/TournamentCreatedEvent.class | Bin 0 -> 3334 bytes .../publish/TournamentDeletedEvent.class | Bin 0 -> 3334 bytes .../publish/TournamentUpdatedEvent.class | Bin 0 -> 3334 bytes .../subscribe/CreatedSubscription.class | Bin 0 -> 3002 bytes .../subscribe/DeletedSubscription.class | Bin 0 -> 3002 bytes .../subscribe/UpdatedSubscription.class | Bin 0 -> 3002 bytes .../service/TournamentService.class | Bin 0 -> 13630 bytes .../microservices/user/aggregate/User.class | Bin 0 -> 2059 bytes .../user/aggregate/UserCustomRepository.class | Bin 0 -> 391 bytes .../user/aggregate/UserDto.class | Bin 0 -> 2845 bytes .../user/aggregate/UserFactory.class | Bin 0 -> 1975 bytes .../user/aggregate/UserRepository.class | Bin 0 -> 1571 bytes .../events/handling/UserEventHandling.class | Bin 0 -> 5098 bytes .../handling/handlers/CreatedHandler.class | Bin 0 -> 1852 bytes .../handling/handlers/DeletedHandler.class | Bin 0 -> 1852 bytes .../handling/handlers/UpdatedHandler.class | Bin 0 -> 1852 bytes .../handling/handlers/UserEventHandler.class | Bin 0 -> 1493 bytes .../events/publish/UserCreatedEvent.class | Bin 0 -> 2321 bytes .../events/publish/UserDeletedEvent.class | Bin 0 -> 2321 bytes .../events/publish/UserUpdatedEvent.class | Bin 0 -> 2321 bytes .../subscribe/CreatedSubscription.class | Bin 0 -> 2894 bytes .../subscribe/DeletedSubscription.class | Bin 0 -> 2894 bytes .../subscribe/UpdatedSubscription.class | Bin 0 -> 2894 bytes .../user/service/UserService.class | Bin 0 -> 7084 bytes .../tournament/aggregate/Tournament.java | 21 +- 562 files changed, 19794 insertions(+), 16 deletions(-) create mode 100644 applications/answers/Dockerfile create mode 100644 applications/answers/docker-compose.yml create mode 100644 applications/answers/pom.xml create mode 100644 applications/answers/src/main/java/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/BehaviourService.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/TracesService.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/AnswersSimulator.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/AnswerEventProcessing.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseEventProcessing.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseExecutionEventProcessing.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/QuestionEventProcessing.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/QuizEventProcessing.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TopicEventProcessing.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TournamentEventProcessing.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/UserEventProcessing.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/AnswerFunctionalities.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/CourseExecutionFunctionalities.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/CourseFunctionalities.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuestionFunctionalities.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuizFunctionalities.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TopicFunctionalities.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TournamentFunctionalities.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/UserFunctionalities.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerBusinessRuleValidator.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerInvariants.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseBusinessRuleValidator.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionBusinessRuleValidator.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionInvariants.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseInvariants.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseValidationAnnotations.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionBusinessRuleValidator.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionInvariants.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizBusinessRuleValidator.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizInvariants.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartDateEndDateRangeValidator.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartTimeEndTimeRangeValidator.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicBusinessRuleValidator.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicInvariants.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentBusinessRuleValidator.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentInvariants.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserBusinessRuleValidator.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserInvariants.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserValidationAnnotations.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidAnswerBusinessRule.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidCourseBusinessRule.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidCourseExecutionBusinessRule.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuestionBusinessRule.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuizBusinessRule.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartDateEndDateRange.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartTimeEndTimeRange.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidTopicBusinessRule.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidTournamentBusinessRule.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidUserBusinessRule.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/AnswerController.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/BehaviourController.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/CourseController.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/CourseExecutionController.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/QuestionController.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/QuizController.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TopicController.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TournamentController.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TracesController.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/UserController.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/Answer.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerCustomRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerFactory.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnsweredQuiz.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnsweredQuizDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuestionAnswer.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuestionAnswerDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswer.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerCourseExecution.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerCourseExecutionDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerStudent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerStudentDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/AnswerEventHandling.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/AnswerEventHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/CreatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/DeletedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/UpdatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerCreatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerDeletedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerUpdatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/SubscribesCreated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/SubscribesDeleted.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/SubscribesUpdated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/service/AnswerService.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/Course.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseCustomRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseFactory.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/CourseEventHandling.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/CourseEventHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/CreatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/DeletedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/UpdatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseCreatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseDeletedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseUpdatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/SubscribesCreated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/SubscribesDeleted.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/SubscribesUpdated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/service/CourseService.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecution.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCourse.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCourseDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCustomRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionFactory.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionStudent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionStudentDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/CourseExecutionEventHandling.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/CourseExecutionEventHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/CreatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/DeletedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/UpdatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionCreatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionDeletedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionUpdatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/SubscribesCreated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/SubscribesDeleted.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/SubscribesUpdated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/service/CourseExecutionService.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersErrorMessage.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersException.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersExceptionHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/Option.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/OptionDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/Question.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourse.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourseDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCustomRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionFactory.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopic.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopicDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/QuestionEventHandling.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/CreatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/DeletedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/QuestionEventHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/UpdatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionCreatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionDeletedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionUpdatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/SubscribesCreated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/SubscribesDeleted.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/SubscribesUpdated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/service/QuestionService.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/Quiz.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCourseExecution.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCourseExecutionDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCustomRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizFactory.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizOption.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizOptionDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestion.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestionDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/QuizEventHandling.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/CreatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/DeletedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/QuizEventHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/UpdatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizCreatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizDeletedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizUpdatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/SubscribesCreated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/SubscribesDeleted.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/SubscribesUpdated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/service/QuizService.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/Topic.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCourse.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCourseDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCustomRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicFactory.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/TopicEventHandling.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/CreatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/DeletedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/TopicEventHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/UpdatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicCreatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicDeletedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicUpdatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/SubscribesCreated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/SubscribesDeleted.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/SubscribesUpdated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/service/TopicService.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/Tournament.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecution.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecutionDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCreator.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCreatorDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCustomRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentFactory.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipant.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswer.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswerDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuiz.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuizDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopic.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopicDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/TournamentEventHandling.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/CreatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/DeletedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/TournamentEventHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/UpdatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentCreatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentDeletedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentUpdatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/SubscribesCreated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/SubscribesDeleted.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/SubscribesUpdated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/service/TournamentService.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/User.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserCustomRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserDto.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserFactory.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserRepository.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/UserEventHandling.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/CreatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/DeletedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/UpdatedHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/UserEventHandler.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserCreatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserDeletedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserUpdatedEvent.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/SubscribesCreated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/SubscribesDeleted.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/SubscribesUpdated.java create mode 100644 applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/service/UserService.java create mode 100644 applications/answers/src/main/resources/application.properties create mode 100644 applications/answers/src/main/resources/application.yml create mode 100644 applications/answers/src/main/resources/database.properties create mode 100644 applications/answers/src/main/resources/logback-spring.xml create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/SpockTest.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/AnswersBeanConfiguration.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/AnswersSpockTest.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/AnsweredQuizBuilder.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/AnsweredQuizDtoBuilder.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuestionAnswerBuilder.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuestionAnswerDtoBuilder.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerBuilder.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerCourseExecutionBuilder.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerCourseExecutionDtoBuilder.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerDtoBuilder.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerStudentBuilder.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerStudentDtoBuilder.groovy create mode 100644 applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/webapi/AnswerRestApiTest.groovy create mode 100644 applications/answers/target/classes/application.properties create mode 100644 applications/answers/target/classes/application.yml create mode 100644 applications/answers/target/classes/database.properties create mode 100644 applications/answers/target/classes/logback-spring.xml create mode 100644 applications/answers/target/classes/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/BehaviourService.class create mode 100644 applications/answers/target/classes/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/TracesService.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/AnswersSimulator.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/AnswerEventProcessing.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseEventProcessing.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseExecutionEventProcessing.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/QuestionEventProcessing.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/QuizEventProcessing.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TopicEventProcessing.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TournamentEventProcessing.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/UserEventProcessing.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/AnswerFunctionalities.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/CourseExecutionFunctionalities.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/CourseFunctionalities.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuestionFunctionalities.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuizFunctionalities.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TopicFunctionalities.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TournamentFunctionalities.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/UserFunctionalities.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerBusinessRuleValidator.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerInvariants.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$AnswerDateValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$AnsweredQuizValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$CompletedDateValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$CompletedValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$QuestionAnswersValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$QuizAnswerCourseExecutionValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$QuizAnswerStudentValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseBusinessRuleValidator.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionBusinessRuleValidator.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionInvariants.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$AcademicTermValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$AcronymValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$CourseValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$EndDateValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$NameValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$StartDateValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$StudentsValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseInvariants.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseValidationAnnotations$AcronymValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseValidationAnnotations$CourseTypeValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseValidationAnnotations$CreationDateValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseValidationAnnotations$NameValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseValidationAnnotations.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionBusinessRuleValidator.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionInvariants.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$ContentValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$CorrectOptionValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$CourseValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$NumberOfOptionsValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$OptionsValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$OrderValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$TitleValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$TopicsValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizBusinessRuleValidator.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizInvariants.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$AvailableDateValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$ConclusionDateValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$CourseExecutionValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$DescriptionValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$NumberOfQuestionsValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$OptionsValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$QuestionsValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$QuizTypeValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$TitleValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartDateEndDateRangeValidator.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartTimeEndTimeRangeValidator.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicBusinessRuleValidator.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicInvariants.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations$CourseValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations$CreationDateValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations$NameValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentBusinessRuleValidator.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentInvariants.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$CancelledValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$EndTimeValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$NumberOfQuestionsValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$StartTimeValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$TournamentCourseExecutionValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$TournamentCreatorValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$TournamentParticipantsValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$TournamentQuizValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$TournamentTopicsValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserBusinessRuleValidator.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserInvariants.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserValidationAnnotations$ActiveValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserValidationAnnotations$NameValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserValidationAnnotations$UsernameValidation.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserValidationAnnotations.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidAnswerBusinessRule.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidCourseBusinessRule.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidCourseExecutionBusinessRule.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuestionBusinessRule.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuizBusinessRule.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartDateEndDateRange.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartTimeEndTimeRange.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidTopicBusinessRule.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidTournamentBusinessRule.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidUserBusinessRule.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/AnswerController.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/BehaviourController.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/CourseController.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/CourseExecutionController.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/QuestionController.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/QuizController.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TopicController.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TournamentController.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TracesController.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/UserController.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/Answer.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerCustomRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerFactory.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnsweredQuiz.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnsweredQuizDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuestionAnswer.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuestionAnswerDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswer.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerCourseExecution.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerCourseExecutionDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerStudent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerStudentDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/AnswerEventHandling.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/AnswerEventHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/CreatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/DeletedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/UpdatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerCreatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerDeletedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerUpdatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/CreatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/DeletedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/UpdatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/service/AnswerService.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/Course.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseCustomRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseFactory.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/CourseEventHandling.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/CourseEventHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/CreatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/DeletedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/UpdatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseCreatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseDeletedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseUpdatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/CreatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/DeletedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/UpdatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/service/CourseService.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecution.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCourse.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCourseDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCustomRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionFactory.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionStudent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionStudentDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/CourseExecutionEventHandling.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/CourseExecutionEventHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/CreatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/DeletedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/UpdatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionCreatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionDeletedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionUpdatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/CreatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/DeletedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/UpdatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/service/CourseExecutionService.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersErrorMessage.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersException.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersExceptionHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/Option.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/OptionDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/Question.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourse.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourseDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCustomRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionFactory.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopic.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopicDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/QuestionEventHandling.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/CreatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/DeletedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/QuestionEventHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/UpdatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionCreatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionDeletedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionUpdatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/CreatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/DeletedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/UpdatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/service/QuestionService.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/Quiz.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCourseExecution.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCourseExecutionDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCustomRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizFactory.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizOption.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizOptionDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestion.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestionDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/QuizEventHandling.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/CreatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/DeletedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/QuizEventHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/UpdatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizCreatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizDeletedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizUpdatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/CreatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/DeletedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/UpdatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/service/QuizService.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/Topic.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCourse.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCourseDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCustomRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicFactory.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/TopicEventHandling.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/CreatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/DeletedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/TopicEventHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/UpdatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicCreatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicDeletedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicUpdatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/CreatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/DeletedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/UpdatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/service/TopicService.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/Tournament.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecution.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecutionDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCreator.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCreatorDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCustomRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentFactory.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipant.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswer.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswerDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuiz.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuizDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopic.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopicDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/TournamentEventHandling.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/CreatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/DeletedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/TournamentEventHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/UpdatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentCreatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentDeletedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentUpdatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/CreatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/DeletedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/UpdatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/service/TournamentService.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/User.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserCustomRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserDto.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserFactory.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserRepository.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/UserEventHandling.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/CreatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/DeletedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/UpdatedHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/UserEventHandler.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserCreatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserDeletedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserUpdatedEvent.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/CreatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/DeletedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/UpdatedSubscription.class create mode 100644 applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/service/UserService.class diff --git a/applications/answers/Dockerfile b/applications/answers/Dockerfile new file mode 100644 index 000000000..81a974450 --- /dev/null +++ b/applications/answers/Dockerfile @@ -0,0 +1,56 @@ +# Multi-stage Docker build for answers +FROM amazoncorretto:17-alpine as builder + +# Install Maven +RUN apk add --no-cache maven + +# Set working directory +WORKDIR /app + +# Copy pom.xml +COPY pom.xml . + +# Download dependencies (this layer will be cached if pom.xml doesn't change) +RUN mvn dependency:go-offline -B + +# Copy source code +COPY src src + +# Build the application +RUN mvn clean package -DskipTests + +# Production stage +FROM amazoncorretto:17-alpine + +# Install curl for health checks and create non-root user for security +RUN apk add --no-cache curl && addgroup -g 1001 -S appuser && adduser -u 1001 -S appuser -G appuser + +# Set working directory +WORKDIR /app + +# Copy the built jar from builder stage +COPY --from=builder /app/target/*.jar app.jar + +# Change ownership to non-root user +RUN chown -R appuser:appuser /app +USER appuser + +# Expose port +EXPOSE 9115 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:9115/actuator/health || exit 1 + +# JVM optimization for containers +ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC -XX:+UseStringDeduplication" + +# Run the application +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar app.jar"] + +# Labels for better container management +LABEL maintainer="Generated by Nebula DSL" +LABEL service.name="answers" +LABEL service.version="1.0.0" +LABEL architecture="default" +LABEL features="events,validation,webapi,coordination" \ No newline at end of file diff --git a/applications/answers/docker-compose.yml b/applications/answers/docker-compose.yml new file mode 100644 index 000000000..dd8c6c483 --- /dev/null +++ b/applications/answers/docker-compose.yml @@ -0,0 +1,73 @@ +version: '3.8' + +services: + answers: + build: . + container_name: answers + ports: + - "${APP_PORT:-9115}:9115" + environment: + - SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-sagas} + - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/answers + - SPRING_DATASOURCE_USERNAME=${POSTGRESQL_USER:-postgres} + - SPRING_DATASOURCE_PASSWORD=${POSTGRESQL_PASSWORD:-password} + depends_on: + postgresql: + condition: service_healthy + networks: + - answers-network + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9115/actuator/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + postgresql: + image: postgres:15-alpine + container_name: answers-postgresql + environment: + - POSTGRES_DB=${POSTGRES_DB:-answers} + - POSTGRES_USER=${POSTGRES_USER:-postgres} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password} + ports: + - "${POSTGRES_PORT:-5432}:5432" + volumes: + - postgresql_data:/var/lib/postgresql/data + - ./init-scripts:/docker-entrypoint-initdb.d + networks: + - answers-network + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-answers}"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + container_name: answers-redis + ports: + - "${REDIS_PORT:-6379}:6379" + volumes: + - redis_data:/data + networks: + - answers-network + restart: unless-stopped + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 3s + retries: 3 + +volumes: + postgresql_data: + driver: local + redis_data: + driver: local + +networks: + answers-network: + driver: bridge + name: answers-network \ No newline at end of file diff --git a/applications/answers/pom.xml b/applications/answers/pom.xml new file mode 100644 index 000000000..e961fbfc0 --- /dev/null +++ b/applications/answers/pom.xml @@ -0,0 +1,140 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.3.9 + + + pt.ulisboa.tecnico.socialsoftware + answers + 2.1.0-SNAPSHOT + answers + answers - Generated with Nebula DSL (default architecture) + + 21 + + + + + + pt.ulisboa.tecnico.socialsoftware + MicroservicesSimulator + 2.1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-test + test + + + org.hibernate.orm + hibernate-core + + + com.h2database + h2 + runtime + + + org.postgresql + postgresql + runtime + + + + + dev + + dev + + + true + + + + sagas + + sagas + + + + dev-sagas + + dev,sagas + + + + test-sagas + + test,sagas + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + 4.2.0 + + + + addTestSources + compileTests + + + + + + + + + test + + test + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + 4.2.0 + + + + addTestSources + compileTests + + + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/BehaviourService.java b/applications/answers/src/main/java/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/BehaviourService.java new file mode 100644 index 000000000..0535adbc3 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/BehaviourService.java @@ -0,0 +1,54 @@ +package pt.ulisboa.tecnico.socialsoftware.ms; + +import org.springframework.stereotype.Service; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** +* Service for managing microservice behavior simulation +*/ +@Service +public class BehaviourService { + +private final Map behaviorMap = new ConcurrentHashMap<>(); + private boolean globalBehavior = false; + + public String startBehaviour(String behaviourSpec) { + try { + // Parse and store behavior specification + // TODO: Implement behavior parsing logic + globalBehavior = true; + return "Behaviour started successfully: " + behaviourSpec; + } catch (Exception e) { + throw new RuntimeException("Failed to start behaviour: " + e.getMessage(), e); + } + } + + public void stopBehaviour() { + try { + globalBehavior = false; + behaviorMap.clear(); + } catch (Exception e) { + throw new RuntimeException("Failed to stop behaviour: " + e.getMessage(), e); + } + } + + public String getStatus() { + return globalBehavior ? "ACTIVE" : "INACTIVE"; + } + + public void addBehaviour(String serviceName, String methodName, String behavior) { + String key = serviceName + "." + methodName; + behaviorMap.put(key, behavior); + } + + public void clearBehaviour() { + behaviorMap.clear(); + globalBehavior = false; + } + + public String getBehaviour(String serviceName, String methodName) { + String key = serviceName + "." + methodName; + return behaviorMap.get(key); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/TracesService.java b/applications/answers/src/main/java/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/TracesService.java new file mode 100644 index 000000000..b8890b6d1 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/TracesService.java @@ -0,0 +1,70 @@ +package pt.ulisboa.tecnico.socialsoftware.ms; + +import org.springframework.stereotype.Service; +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.time.LocalDateTime; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** +* Service for managing execution traces and monitoring +*/ +@Service +public class TracesService { + +private final Queue> traces = new ConcurrentLinkedQueue<>(); + private final ObjectMapper objectMapper = new ObjectMapper(); + + public List> getAllTraces() { + return new ArrayList<>(traces); + } + + public List> getTracesByAggregate(String aggregateName) { + return traces.stream() + .filter(trace -> aggregateName.equals(trace.get("aggregate"))) + .collect(ArrayList::new, (list, trace) -> list.add(trace), ArrayList::addAll); + } + + public void addTrace(String aggregate, String operation, String status, Object data) { + Map trace = new HashMap<>(); + trace.put("timestamp", LocalDateTime.now().toString()); + trace.put("aggregate", aggregate); + trace.put("operation", operation); + trace.put("status", status); + trace.put("data", data); + traces.offer(trace); + + // Keep only last 1000 traces to prevent memory issues + while (traces.size() > 1000) { + traces.poll(); + } + } + + public void clearTraces() { + traces.clear(); + } + + public String exportTraces(String format) { + try { + List> allTraces = getAllTraces(); + if ("json".equalsIgnoreCase(format)) { + return objectMapper.writeValueAsString(allTraces); + } else if ("csv".equalsIgnoreCase(format)) { + // Simple CSV export + StringBuilder csv = new StringBuilder(); + csv.append("timestamp,aggregate,operation,status\n"); + for (Map trace : allTraces) { + csv.append(String.format("%s,%s,%s,%s\n", + trace.get("timestamp"), + trace.get("aggregate"), + trace.get("operation"), + trace.get("status"))); + } + return csv.toString(); + } + return objectMapper.writeValueAsString(allTraces); + } catch (Exception e) { + throw new RuntimeException("Failed to export traces: " + e.getMessage(), e); + } + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/AnswersSimulator.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/AnswersSimulator.java new file mode 100644 index 000000000..db908ac7d --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/AnswersSimulator.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.PropertySource; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import com.generated.microservices.ms.domain.event.EventService; + +@PropertySource({"classpath:application.properties"}) +@EnableJpaRepositories(basePackages = {".ms.*", +".answers.*"}) +@EntityScan(basePackages = {".ms.*", +".answers.*"}) +@EnableTransactionManagement +@EnableJpaAuditing + + +@SpringBootApplication(scanBasePackages = {".ms.*", +".answers.*"}) +public class AnswersSimulator implements InitializingBean { + +@Autowired +private EventService eventService; + + +public static void main(String[] args) { +SpringApplication.run(AnswersSimulator.class, args); +} + +@Override +public void afterPropertiesSet() { +// Run on startup + +eventService.clearEventsAtApplicationStartUp(); + +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/AnswerEventProcessing.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/AnswerEventProcessing.java new file mode 100644 index 000000000..d04443fff --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/AnswerEventProcessing.java @@ -0,0 +1,43 @@ +package com.generated.microservices.answers.coordination.eventProcessing; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.UNDEFINED_TRANSACTIONAL_MODEL; +import java.util.Arrays; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWorkService; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import com.generated.microservices.answers.microservices.answer.service.AnswerService; + +@Service +public class AnswerEventProcessing { + @Autowired + private AnswerService answerService; + + @Autowired + private UnitOfWorkService unitOfWorkService; + + @Autowired + private Environment env; + + private TransactionalModel workflowType; + + @PostConstruct + public void init() { + String[] activeProfiles = env.getActiveProfiles(); + workflowType = Arrays.asList(activeProfiles).contains(SAGAS.getValue()) ? SAGAS : null; + if (workflowType == null) { + throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); + } + } + + public void processAnswerEvent(String eventType, Integer aggregateId, Integer aggregateVersion) { + String functionalityName = new Throwable().getStackTrace()[0].getMethodName(); + UnitOfWork unitOfWork = unitOfWorkService.createUnitOfWork(functionalityName); + unitOfWorkService.commit(unitOfWork); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseEventProcessing.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseEventProcessing.java new file mode 100644 index 000000000..ca3863dba --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseEventProcessing.java @@ -0,0 +1,43 @@ +package com.generated.microservices.answers.coordination.eventProcessing; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.UNDEFINED_TRANSACTIONAL_MODEL; +import java.util.Arrays; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWorkService; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import com.generated.microservices.answers.microservices.course.service.CourseService; + +@Service +public class CourseEventProcessing { + @Autowired + private CourseService courseService; + + @Autowired + private UnitOfWorkService unitOfWorkService; + + @Autowired + private Environment env; + + private TransactionalModel workflowType; + + @PostConstruct + public void init() { + String[] activeProfiles = env.getActiveProfiles(); + workflowType = Arrays.asList(activeProfiles).contains(SAGAS.getValue()) ? SAGAS : null; + if (workflowType == null) { + throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); + } + } + + public void processCourseEvent(String eventType, Integer aggregateId, Integer aggregateVersion) { + String functionalityName = new Throwable().getStackTrace()[0].getMethodName(); + UnitOfWork unitOfWork = unitOfWorkService.createUnitOfWork(functionalityName); + unitOfWorkService.commit(unitOfWork); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseExecutionEventProcessing.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseExecutionEventProcessing.java new file mode 100644 index 000000000..5091c75f8 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseExecutionEventProcessing.java @@ -0,0 +1,43 @@ +package com.generated.microservices.answers.coordination.eventProcessing; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.UNDEFINED_TRANSACTIONAL_MODEL; +import java.util.Arrays; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWorkService; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import com.generated.microservices.answers.microservices.courseexecution.service.CourseExecutionService; + +@Service +public class CourseExecutionEventProcessing { + @Autowired + private CourseExecutionService courseexecutionService; + + @Autowired + private UnitOfWorkService unitOfWorkService; + + @Autowired + private Environment env; + + private TransactionalModel workflowType; + + @PostConstruct + public void init() { + String[] activeProfiles = env.getActiveProfiles(); + workflowType = Arrays.asList(activeProfiles).contains(SAGAS.getValue()) ? SAGAS : null; + if (workflowType == null) { + throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); + } + } + + public void processCourseExecutionEvent(String eventType, Integer aggregateId, Integer aggregateVersion) { + String functionalityName = new Throwable().getStackTrace()[0].getMethodName(); + UnitOfWork unitOfWork = unitOfWorkService.createUnitOfWork(functionalityName); + unitOfWorkService.commit(unitOfWork); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/QuestionEventProcessing.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/QuestionEventProcessing.java new file mode 100644 index 000000000..309011a28 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/QuestionEventProcessing.java @@ -0,0 +1,43 @@ +package com.generated.microservices.answers.coordination.eventProcessing; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.UNDEFINED_TRANSACTIONAL_MODEL; +import java.util.Arrays; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWorkService; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import com.generated.microservices.answers.microservices.question.service.QuestionService; + +@Service +public class QuestionEventProcessing { + @Autowired + private QuestionService questionService; + + @Autowired + private UnitOfWorkService unitOfWorkService; + + @Autowired + private Environment env; + + private TransactionalModel workflowType; + + @PostConstruct + public void init() { + String[] activeProfiles = env.getActiveProfiles(); + workflowType = Arrays.asList(activeProfiles).contains(SAGAS.getValue()) ? SAGAS : null; + if (workflowType == null) { + throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); + } + } + + public void processQuestionEvent(String eventType, Integer aggregateId, Integer aggregateVersion) { + String functionalityName = new Throwable().getStackTrace()[0].getMethodName(); + UnitOfWork unitOfWork = unitOfWorkService.createUnitOfWork(functionalityName); + unitOfWorkService.commit(unitOfWork); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/QuizEventProcessing.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/QuizEventProcessing.java new file mode 100644 index 000000000..1b2746a15 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/QuizEventProcessing.java @@ -0,0 +1,43 @@ +package com.generated.microservices.answers.coordination.eventProcessing; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.UNDEFINED_TRANSACTIONAL_MODEL; +import java.util.Arrays; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWorkService; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import com.generated.microservices.answers.microservices.quiz.service.QuizService; + +@Service +public class QuizEventProcessing { + @Autowired + private QuizService quizService; + + @Autowired + private UnitOfWorkService unitOfWorkService; + + @Autowired + private Environment env; + + private TransactionalModel workflowType; + + @PostConstruct + public void init() { + String[] activeProfiles = env.getActiveProfiles(); + workflowType = Arrays.asList(activeProfiles).contains(SAGAS.getValue()) ? SAGAS : null; + if (workflowType == null) { + throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); + } + } + + public void processQuizEvent(String eventType, Integer aggregateId, Integer aggregateVersion) { + String functionalityName = new Throwable().getStackTrace()[0].getMethodName(); + UnitOfWork unitOfWork = unitOfWorkService.createUnitOfWork(functionalityName); + unitOfWorkService.commit(unitOfWork); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TopicEventProcessing.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TopicEventProcessing.java new file mode 100644 index 000000000..068268edc --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TopicEventProcessing.java @@ -0,0 +1,43 @@ +package com.generated.microservices.answers.coordination.eventProcessing; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.UNDEFINED_TRANSACTIONAL_MODEL; +import java.util.Arrays; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWorkService; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import com.generated.microservices.answers.microservices.topic.service.TopicService; + +@Service +public class TopicEventProcessing { + @Autowired + private TopicService topicService; + + @Autowired + private UnitOfWorkService unitOfWorkService; + + @Autowired + private Environment env; + + private TransactionalModel workflowType; + + @PostConstruct + public void init() { + String[] activeProfiles = env.getActiveProfiles(); + workflowType = Arrays.asList(activeProfiles).contains(SAGAS.getValue()) ? SAGAS : null; + if (workflowType == null) { + throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); + } + } + + public void processTopicEvent(String eventType, Integer aggregateId, Integer aggregateVersion) { + String functionalityName = new Throwable().getStackTrace()[0].getMethodName(); + UnitOfWork unitOfWork = unitOfWorkService.createUnitOfWork(functionalityName); + unitOfWorkService.commit(unitOfWork); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TournamentEventProcessing.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TournamentEventProcessing.java new file mode 100644 index 000000000..06d4a7ac3 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TournamentEventProcessing.java @@ -0,0 +1,43 @@ +package com.generated.microservices.answers.coordination.eventProcessing; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.UNDEFINED_TRANSACTIONAL_MODEL; +import java.util.Arrays; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWorkService; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import com.generated.microservices.answers.microservices.tournament.service.TournamentService; + +@Service +public class TournamentEventProcessing { + @Autowired + private TournamentService tournamentService; + + @Autowired + private UnitOfWorkService unitOfWorkService; + + @Autowired + private Environment env; + + private TransactionalModel workflowType; + + @PostConstruct + public void init() { + String[] activeProfiles = env.getActiveProfiles(); + workflowType = Arrays.asList(activeProfiles).contains(SAGAS.getValue()) ? SAGAS : null; + if (workflowType == null) { + throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); + } + } + + public void processTournamentEvent(String eventType, Integer aggregateId, Integer aggregateVersion) { + String functionalityName = new Throwable().getStackTrace()[0].getMethodName(); + UnitOfWork unitOfWork = unitOfWorkService.createUnitOfWork(functionalityName); + unitOfWorkService.commit(unitOfWork); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/UserEventProcessing.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/UserEventProcessing.java new file mode 100644 index 000000000..412699b93 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/UserEventProcessing.java @@ -0,0 +1,43 @@ +package com.generated.microservices.answers.coordination.eventProcessing; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.UNDEFINED_TRANSACTIONAL_MODEL; +import java.util.Arrays; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWorkService; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import com.generated.microservices.answers.microservices.user.service.UserService; + +@Service +public class UserEventProcessing { + @Autowired + private UserService userService; + + @Autowired + private UnitOfWorkService unitOfWorkService; + + @Autowired + private Environment env; + + private TransactionalModel workflowType; + + @PostConstruct + public void init() { + String[] activeProfiles = env.getActiveProfiles(); + workflowType = Arrays.asList(activeProfiles).contains(SAGAS.getValue()) ? SAGAS : null; + if (workflowType == null) { + throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); + } + } + + public void processUserEvent(String eventType, Integer aggregateId, Integer aggregateVersion) { + String functionalityName = new Throwable().getStackTrace()[0].getMethodName(); + UnitOfWork unitOfWork = unitOfWorkService.createUnitOfWork(functionalityName); + unitOfWorkService.commit(unitOfWork); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/AnswerFunctionalities.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/AnswerFunctionalities.java new file mode 100644 index 000000000..a4eea1ad0 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/AnswerFunctionalities.java @@ -0,0 +1,48 @@ +package com.generated.microservices.answers.coordination.functionalities; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.answers.microservices.user.aggregate.UserDto; +import com.generated.microservices.ms.domain.aggregate.Aggregate.AggregateState; +import com.generated.microservices.answers.microservices.answer.service.AnswerService; +import com.generated.microservices.answers.microservices.answer.aggregate.QuizAnswerDto; +import com.generated.microservices.answers.microservices.answer.aggregate.QuizAnswerStudentDto; +import com.generated.microservices.answers.microservices.answer.aggregate.QuizAnswerCourseExecutionDto; +import com.generated.microservices.answers.microservices.answer.aggregate.QuestionAnswerDto; +import com.generated.microservices.answers.microservices.answer.aggregate.AnsweredQuizDto; + +@Service +public class AnswerFunctionalities { + +private AnswerService answerService; + +@Autowired +private Environment env; + +private TransactionalModel workflowType; + +@PostConstruct +public void init() { +String[] activeProfiles = env.getActiveProfiles(); +if (Arrays.asList(activeProfiles).contains(SAGAS.getValue())) { +workflowType = SAGAS; +} else { +throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); +} +} + +[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object] +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/CourseExecutionFunctionalities.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/CourseExecutionFunctionalities.java new file mode 100644 index 000000000..06ddb066f --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/CourseExecutionFunctionalities.java @@ -0,0 +1,45 @@ +package com.generated.microservices.answers.coordination.functionalities; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.answers.microservices.user.aggregate.UserDto; +import com.generated.microservices.answers.microservices.courseexecution.service.CourseExecutionService; +import com.generated.microservices.answers.microservices.courseexecution.aggregate.CourseExecutionDto; +import com.generated.microservices.answers.microservices.courseexecution.aggregate.CourseExecutionCourseDto; +import com.generated.microservices.answers.microservices.courseexecution.aggregate.CourseExecutionStudentDto; + +@Service +public class CourseExecutionFunctionalities { + +private CourseExecutionService courseexecutionService; + +@Autowired +private Environment env; + +private TransactionalModel workflowType; + +@PostConstruct +public void init() { +String[] activeProfiles = env.getActiveProfiles(); +if (Arrays.asList(activeProfiles).contains(SAGAS.getValue())) { +workflowType = SAGAS; +} else { +throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); +} +} + +[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object] +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/CourseFunctionalities.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/CourseFunctionalities.java new file mode 100644 index 000000000..a6e526afd --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/CourseFunctionalities.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.coordination.functionalities; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.answers.microservices.course.service.CourseService; +import com.generated.microservices.answers.microservices.course.aggregate.CourseDto; + +@Service +public class CourseFunctionalities { + +private CourseService courseService; + +@Autowired +private Environment env; + +private TransactionalModel workflowType; + +@PostConstruct +public void init() { +String[] activeProfiles = env.getActiveProfiles(); +if (Arrays.asList(activeProfiles).contains(SAGAS.getValue())) { +workflowType = SAGAS; +} else { +throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); +} +} + +[object Object],[object Object],[object Object] +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuestionFunctionalities.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuestionFunctionalities.java new file mode 100644 index 000000000..562dc355e --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuestionFunctionalities.java @@ -0,0 +1,45 @@ +package com.generated.microservices.answers.coordination.functionalities; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.answers.microservices.question.service.QuestionService; +import com.generated.microservices.answers.microservices.question.aggregate.QuestionDto; +import com.generated.microservices.answers.microservices.question.aggregate.QuestionCourseDto; +import com.generated.microservices.answers.microservices.question.aggregate.QuestionTopicDto; +import com.generated.microservices.answers.microservices.question.aggregate.OptionDto; + +@Service +public class QuestionFunctionalities { + +private QuestionService questionService; + +@Autowired +private Environment env; + +private TransactionalModel workflowType; + +@PostConstruct +public void init() { +String[] activeProfiles = env.getActiveProfiles(); +if (Arrays.asList(activeProfiles).contains(SAGAS.getValue())) { +workflowType = SAGAS; +} else { +throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); +} +} + +[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object] +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuizFunctionalities.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuizFunctionalities.java new file mode 100644 index 000000000..8d0be32ae --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuizFunctionalities.java @@ -0,0 +1,45 @@ +package com.generated.microservices.answers.coordination.functionalities; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.answers.microservices.quiz.service.QuizService; +import com.generated.microservices.answers.microservices.quiz.aggregate.QuizDto; +import com.generated.microservices.answers.microservices.quiz.aggregate.QuizCourseExecutionDto; +import com.generated.microservices.answers.microservices.quiz.aggregate.QuizQuestionDto; +import com.generated.microservices.answers.microservices.quiz.aggregate.QuizOptionDto; + +@Service +public class QuizFunctionalities { + +private QuizService quizService; + +@Autowired +private Environment env; + +private TransactionalModel workflowType; + +@PostConstruct +public void init() { +String[] activeProfiles = env.getActiveProfiles(); +if (Arrays.asList(activeProfiles).contains(SAGAS.getValue())) { +workflowType = SAGAS; +} else { +throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); +} +} + +[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object] +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TopicFunctionalities.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TopicFunctionalities.java new file mode 100644 index 000000000..8046c017c --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TopicFunctionalities.java @@ -0,0 +1,43 @@ +package com.generated.microservices.answers.coordination.functionalities; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.answers.microservices.topic.service.TopicService; +import com.generated.microservices.answers.microservices.topic.aggregate.TopicDto; +import com.generated.microservices.answers.microservices.topic.aggregate.TopicCourseDto; + +@Service +public class TopicFunctionalities { + +private TopicService topicService; + +@Autowired +private Environment env; + +private TransactionalModel workflowType; + +@PostConstruct +public void init() { +String[] activeProfiles = env.getActiveProfiles(); +if (Arrays.asList(activeProfiles).contains(SAGAS.getValue())) { +workflowType = SAGAS; +} else { +throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); +} +} + +[object Object],[object Object],[object Object],[object Object] +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TournamentFunctionalities.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TournamentFunctionalities.java new file mode 100644 index 000000000..f6f5103c8 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TournamentFunctionalities.java @@ -0,0 +1,50 @@ +package com.generated.microservices.answers.coordination.functionalities; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.answers.microservices.user.aggregate.UserDto; +import com.generated.microservices.ms.domain.aggregate.Aggregate.AggregateState; +import com.generated.microservices.answers.microservices.tournament.service.TournamentService; +import com.generated.microservices.answers.microservices.tournament.aggregate.TournamentDto; +import com.generated.microservices.answers.microservices.tournament.aggregate.TournamentCourseExecutionDto; +import com.generated.microservices.answers.microservices.tournament.aggregate.TournamentCreatorDto; +import com.generated.microservices.answers.microservices.tournament.aggregate.TournamentParticipantDto; +import com.generated.microservices.answers.microservices.tournament.aggregate.TournamentParticipantQuizAnswerDto; +import com.generated.microservices.answers.microservices.tournament.aggregate.TournamentTopicDto; +import com.generated.microservices.answers.microservices.tournament.aggregate.TournamentQuizDto; + +@Service +public class TournamentFunctionalities { + +private TournamentService tournamentService; + +@Autowired +private Environment env; + +private TransactionalModel workflowType; + +@PostConstruct +public void init() { +String[] activeProfiles = env.getActiveProfiles(); +if (Arrays.asList(activeProfiles).contains(SAGAS.getValue())) { +workflowType = SAGAS; +} else { +throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); +} +} + +[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object] +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/UserFunctionalities.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/UserFunctionalities.java new file mode 100644 index 000000000..eaf5f8799 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/UserFunctionalities.java @@ -0,0 +1,43 @@ +package com.generated.microservices.answers.coordination.functionalities; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; + +import static com.generated.microservices.ms.TransactionalModel.SAGAS; +import static com.generated.microservices.answers.microservices.exception.AnswersErrorMessage.*; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import com.generated.microservices.answers.microservices.exception.AnswersException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import com.generated.microservices.ms.TransactionalModel; +import com.generated.microservices.ms.coordination.unitOfWork.UnitOfWork; +import com.generated.microservices.answers.microservices.user.aggregate.UserDto; +import com.generated.microservices.answers.microservices.user.service.UserService; +import com.generated.microservices.answers.microservices.user.aggregate.UserDto; + +@Service +public class UserFunctionalities { + +private UserService userService; + +@Autowired +private Environment env; + +private TransactionalModel workflowType; + +@PostConstruct +public void init() { +String[] activeProfiles = env.getActiveProfiles(); +if (Arrays.asList(activeProfiles).contains(SAGAS.getValue())) { +workflowType = SAGAS; +} else { +throw new AnswersException(UNDEFINED_TRANSACTIONAL_MODEL); +} +} + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerBusinessRuleValidator.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerBusinessRuleValidator.java new file mode 100644 index 000000000..6b5b716b6 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerBusinessRuleValidator.java @@ -0,0 +1,47 @@ +package com.generated.microservices.answers.microservices.answer.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +public class AnswerBusinessRuleValidator implements ConstraintValidator { + + @Override + public void initialize(ValidAnswerBusinessRule constraintAnnotation) { + // Initialize validator if needed + } + + @Override + public boolean isValid(Answer value, ConstraintValidatorContext context) { + if (value == null) { + return true; // Let @NotNull handle null validation + } + + // Implement business rule validation logic for Answer + // Example: Check business hours, validate against external systems, etc. + // TODO: Implement specific business rules + return true; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerInvariants.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerInvariants.java new file mode 100644 index 000000000..7d44bfdfb --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerInvariants.java @@ -0,0 +1,113 @@ +package com.generated.microservices.answers.microservices.answer.validation.invariants; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +/** + * Invariant validation methods for Answer + */ +public class AnswerInvariants { + + /** + * AnswerDate cannot be null + */ + public static void invariantAnswerDateNotNull(QuizAnswer entity) { + if (entity.getAnswerDate() == null) { + throw new IllegalStateException("AnswerDate cannot be null"); + } + } + + /** + * CompletedDate cannot be null + */ + public static void invariantCompletedDateNotNull(QuizAnswer entity) { + if (entity.getCompletedDate() == null) { + throw new IllegalStateException("CompletedDate cannot be null"); + } + } + + /** + * Completed cannot be null + */ + public static void invariantCompletedNotNull(QuizAnswer entity) { + if (entity.isCompleted() == null) { + throw new IllegalStateException("Completed cannot be null"); + } + } + + /** + * QuizAnswerStudent cannot be null + */ + public static void invariantQuizAnswerStudentNotNull(QuizAnswer entity) { + if (entity.getQuizAnswerStudent() == null) { + throw new IllegalStateException("QuizAnswerStudent cannot be null"); + } + } + + /** + * QuizAnswerCourseExecution cannot be null + */ + public static void invariantQuizAnswerCourseExecutionNotNull(QuizAnswer entity) { + if (entity.getQuizAnswerCourseExecution() == null) { + throw new IllegalStateException("QuizAnswerCourseExecution cannot be null"); + } + } + + /** + * QuestionAnswers cannot be null + */ + public static void invariantQuestionAnswersNotNull(QuizAnswer entity) { + if (entity.getQuestionAnswers() == null) { + throw new IllegalStateException("QuestionAnswers cannot be null"); + } + } + + /** + * QuestionAnswers cannot be empty + */ + public static void invariantQuestionAnswersNotEmpty(QuizAnswer entity) { + if (entity.getQuestionAnswers() == null || ((java.util.Collection) entity.getQuestionAnswers()).isEmpty()) { + throw new IllegalStateException("QuestionAnswers cannot be empty"); + } + } + + /** + * AnsweredQuiz cannot be null + */ + public static void invariantAnsweredQuizNotNull(QuizAnswer entity) { + if (entity.getAnsweredQuiz() == null) { + throw new IllegalStateException("AnsweredQuiz cannot be null"); + } + } + + /** + * Answer aggregate must be in a valid state + */ + public static void invariantAnswerValid(QuizAnswer entity) { + // Aggregate-level validation logic + // Validate business rules that span multiple properties + // Example: startDate must be before endDate + // TODO: Implement aggregate-specific business rules + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations.java new file mode 100644 index 000000000..6c5ab60a3 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations.java @@ -0,0 +1,152 @@ +package com.generated.microservices.answers.microservices.answer.validation.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; + + +/** + * Validation annotations for Answer properties + */ +public class AnswerValidationAnnotations { + + /** + * Validation annotations for answerDate + */ + public static class AnswerDateValidation { + @NotNull + private LocalDateTime answerDate; + + // Getter and setter + public LocalDateTime getAnswerDate() { + return answerDate; + } + + public void setAnswerDate(LocalDateTime answerDate) { + this.answerDate = answerDate; + } + } + + /** + * Validation annotations for completedDate + */ + public static class CompletedDateValidation { + @NotNull + private LocalDateTime completedDate; + + // Getter and setter + public LocalDateTime getCompletedDate() { + return completedDate; + } + + public void setCompletedDate(LocalDateTime completedDate) { + this.completedDate = completedDate; + } + } + + /** + * Validation annotations for completed + */ + public static class CompletedValidation { + @NotNull + private Boolean completed; + + // Getter and setter + public Boolean getCompleted() { + return completed; + } + + public void setCompleted(Boolean completed) { + this.completed = completed; + } + } + + /** + * Validation annotations for quizAnswerStudent + */ + public static class QuizAnswerStudentValidation { + @NotNull + private Object quizAnswerStudent; + + // Getter and setter + public Object getQuizAnswerStudent() { + return quizAnswerStudent; + } + + public void setQuizAnswerStudent(Object quizAnswerStudent) { + this.quizAnswerStudent = quizAnswerStudent; + } + } + + /** + * Validation annotations for quizAnswerCourseExecution + */ + public static class QuizAnswerCourseExecutionValidation { + @NotNull + private Object quizAnswerCourseExecution; + + // Getter and setter + public Object getQuizAnswerCourseExecution() { + return quizAnswerCourseExecution; + } + + public void setQuizAnswerCourseExecution(Object quizAnswerCourseExecution) { + this.quizAnswerCourseExecution = quizAnswerCourseExecution; + } + } + + /** + * Validation annotations for questionAnswers + */ + public static class QuestionAnswersValidation { + @NotNull + @NotEmpty + private Object questionAnswers; + + // Getter and setter + public Object getQuestionAnswers() { + return questionAnswers; + } + + public void setQuestionAnswers(Object questionAnswers) { + this.questionAnswers = questionAnswers; + } + } + + /** + * Validation annotations for answeredQuiz + */ + public static class AnsweredQuizValidation { + @NotNull + private Object answeredQuiz; + + // Getter and setter + public Object getAnsweredQuiz() { + return answeredQuiz; + } + + public void setAnsweredQuiz(Object answeredQuiz) { + this.answeredQuiz = answeredQuiz; + } + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseBusinessRuleValidator.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseBusinessRuleValidator.java new file mode 100644 index 000000000..43c325f91 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseBusinessRuleValidator.java @@ -0,0 +1,47 @@ +package com.generated.microservices.answers.microservices.course.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.course.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +public class CourseBusinessRuleValidator implements ConstraintValidator { + + @Override + public void initialize(ValidCourseBusinessRule constraintAnnotation) { + // Initialize validator if needed + } + + @Override + public boolean isValid(Course value, ConstraintValidatorContext context) { + if (value == null) { + return true; // Let @NotNull handle null validation + } + + // Implement business rule validation logic for Course + // Example: Check business hours, validate against external systems, etc. + // TODO: Implement specific business rules + return true; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionBusinessRuleValidator.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionBusinessRuleValidator.java new file mode 100644 index 000000000..5e02f8e5d --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionBusinessRuleValidator.java @@ -0,0 +1,47 @@ +package com.generated.microservices.answers.microservices.courseexecution.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +public class CourseExecutionBusinessRuleValidator implements ConstraintValidator { + + @Override + public void initialize(ValidCourseExecutionBusinessRule constraintAnnotation) { + // Initialize validator if needed + } + + @Override + public boolean isValid(CourseExecution value, ConstraintValidatorContext context) { + if (value == null) { + return true; // Let @NotNull handle null validation + } + + // Implement business rule validation logic for CourseExecution + // Example: Check business hours, validate against external systems, etc. + // TODO: Implement specific business rules + return true; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionInvariants.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionInvariants.java new file mode 100644 index 000000000..e467c4c89 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionInvariants.java @@ -0,0 +1,140 @@ +package com.generated.microservices.answers.microservices.courseexecution.validation.invariants; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +/** + * Invariant validation methods for CourseExecution + */ +public class CourseExecutionInvariants { + + /** + * Name cannot be null + */ + public static void invariantNameNotNull(CourseExecution entity) { + if (entity.getName() == null) { + throw new IllegalStateException("Name cannot be null"); + } + } + + /** + * Name cannot be blank + */ + public static void invariantNameNotBlank(CourseExecution entity) { + if (entity.getName() == null || entity.getName().trim().isEmpty()) { + throw new IllegalStateException("Name cannot be blank"); + } + } + + /** + * Acronym cannot be null + */ + public static void invariantAcronymNotNull(CourseExecution entity) { + if (entity.getAcronym() == null) { + throw new IllegalStateException("Acronym cannot be null"); + } + } + + /** + * Acronym cannot be blank + */ + public static void invariantAcronymNotBlank(CourseExecution entity) { + if (entity.getAcronym() == null || entity.getAcronym().trim().isEmpty()) { + throw new IllegalStateException("Acronym cannot be blank"); + } + } + + /** + * AcademicTerm cannot be null + */ + public static void invariantAcademicTermNotNull(CourseExecution entity) { + if (entity.getAcademicTerm() == null) { + throw new IllegalStateException("AcademicTerm cannot be null"); + } + } + + /** + * AcademicTerm cannot be blank + */ + public static void invariantAcademicTermNotBlank(CourseExecution entity) { + if (entity.getAcademicTerm() == null || entity.getAcademicTerm().trim().isEmpty()) { + throw new IllegalStateException("AcademicTerm cannot be blank"); + } + } + + /** + * StartDate cannot be null + */ + public static void invariantStartDateNotNull(CourseExecution entity) { + if (entity.getStartDate() == null) { + throw new IllegalStateException("StartDate cannot be null"); + } + } + + /** + * EndDate cannot be null + */ + public static void invariantEndDateNotNull(CourseExecution entity) { + if (entity.getEndDate() == null) { + throw new IllegalStateException("EndDate cannot be null"); + } + } + + /** + * Course cannot be null + */ + public static void invariantCourseNotNull(CourseExecution entity) { + if (entity.getCourse() == null) { + throw new IllegalStateException("Course cannot be null"); + } + } + + /** + * Students cannot be null + */ + public static void invariantStudentsNotNull(CourseExecution entity) { + if (entity.getStudents() == null) { + throw new IllegalStateException("Students cannot be null"); + } + } + + /** + * Students cannot be empty + */ + public static void invariantStudentsNotEmpty(CourseExecution entity) { + if (entity.getStudents() == null || ((java.util.Collection) entity.getStudents()).isEmpty()) { + throw new IllegalStateException("Students cannot be empty"); + } + } + + /** + * CourseExecution aggregate must be in a valid state + */ + public static void invariantCourseExecutionValid(CourseExecution entity) { + // Aggregate-level validation logic + // Validate business rules that span multiple properties + // Example: startDate must be before endDate + // TODO: Implement aggregate-specific business rules + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations.java new file mode 100644 index 000000000..0164ad22c --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations.java @@ -0,0 +1,155 @@ +package com.generated.microservices.answers.microservices.courseexecution.validation.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; + + +/** + * Validation annotations for CourseExecution properties + */ +public class CourseExecutionValidationAnnotations { + + /** + * Validation annotations for name + */ + public static class NameValidation { + @NotNull + @NotBlank + private String name; + + // Getter and setter + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + /** + * Validation annotations for acronym + */ + public static class AcronymValidation { + @NotNull + @NotBlank + private String acronym; + + // Getter and setter + public String getAcronym() { + return acronym; + } + + public void setAcronym(String acronym) { + this.acronym = acronym; + } + } + + /** + * Validation annotations for academicTerm + */ + public static class AcademicTermValidation { + @NotNull + @NotBlank + private String academicTerm; + + // Getter and setter + public String getAcademicTerm() { + return academicTerm; + } + + public void setAcademicTerm(String academicTerm) { + this.academicTerm = academicTerm; + } + } + + /** + * Validation annotations for startDate + */ + public static class StartDateValidation { + @NotNull + private LocalDateTime startDate; + + // Getter and setter + public LocalDateTime getStartDate() { + return startDate; + } + + public void setStartDate(LocalDateTime startDate) { + this.startDate = startDate; + } + } + + /** + * Validation annotations for endDate + */ + public static class EndDateValidation { + @NotNull + private LocalDateTime endDate; + + // Getter and setter + public LocalDateTime getEndDate() { + return endDate; + } + + public void setEndDate(LocalDateTime endDate) { + this.endDate = endDate; + } + } + + /** + * Validation annotations for course + */ + public static class CourseValidation { + @NotNull + private Object course; + + // Getter and setter + public Object getCourse() { + return course; + } + + public void setCourse(Object course) { + this.course = course; + } + } + + /** + * Validation annotations for students + */ + public static class StudentsValidation { + @NotNull + @NotEmpty + private Object students; + + // Getter and setter + public Object getStudents() { + return students; + } + + public void setStudents(Object students) { + this.students = students; + } + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseInvariants.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseInvariants.java new file mode 100644 index 000000000..9b3d5aed8 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseInvariants.java @@ -0,0 +1,104 @@ +package com.generated.microservices.answers.microservices.course.validation.invariants; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.course.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +/** + * Invariant validation methods for Course + */ +public class CourseInvariants { + + /** + * Name cannot be null + */ + public static void invariantNameNotNull(Course entity) { + if (entity.getName() == null) { + throw new IllegalStateException("Name cannot be null"); + } + } + + /** + * Name cannot be blank + */ + public static void invariantNameNotBlank(Course entity) { + if (entity.getName() == null || entity.getName().trim().isEmpty()) { + throw new IllegalStateException("Name cannot be blank"); + } + } + + /** + * Acronym cannot be null + */ + public static void invariantAcronymNotNull(Course entity) { + if (entity.getAcronym() == null) { + throw new IllegalStateException("Acronym cannot be null"); + } + } + + /** + * Acronym cannot be blank + */ + public static void invariantAcronymNotBlank(Course entity) { + if (entity.getAcronym() == null || entity.getAcronym().trim().isEmpty()) { + throw new IllegalStateException("Acronym cannot be blank"); + } + } + + /** + * CourseType cannot be null + */ + public static void invariantCourseTypeNotNull(Course entity) { + if (entity.getCourseType() == null) { + throw new IllegalStateException("CourseType cannot be null"); + } + } + + /** + * CourseType cannot be blank + */ + public static void invariantCourseTypeNotBlank(Course entity) { + if (entity.getCourseType() == null || entity.getCourseType().trim().isEmpty()) { + throw new IllegalStateException("CourseType cannot be blank"); + } + } + + /** + * CreationDate cannot be null + */ + public static void invariantCreationDateNotNull(Course entity) { + if (entity.getCreationDate() == null) { + throw new IllegalStateException("CreationDate cannot be null"); + } + } + + /** + * Course aggregate must be in a valid state + */ + public static void invariantCourseValid(Course entity) { + // Aggregate-level validation logic + // Validate business rules that span multiple properties + // Example: startDate must be before endDate + // TODO: Implement aggregate-specific business rules + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseValidationAnnotations.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseValidationAnnotations.java new file mode 100644 index 000000000..c8cfbd174 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseValidationAnnotations.java @@ -0,0 +1,103 @@ +package com.generated.microservices.answers.microservices.course.validation.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.course.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; + + +/** + * Validation annotations for Course properties + */ +public class CourseValidationAnnotations { + + /** + * Validation annotations for name + */ + public static class NameValidation { + @NotNull + @NotBlank + private String name; + + // Getter and setter + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + /** + * Validation annotations for acronym + */ + public static class AcronymValidation { + @NotNull + @NotBlank + private String acronym; + + // Getter and setter + public String getAcronym() { + return acronym; + } + + public void setAcronym(String acronym) { + this.acronym = acronym; + } + } + + /** + * Validation annotations for courseType + */ + public static class CourseTypeValidation { + @NotNull + @NotBlank + private String courseType; + + // Getter and setter + public String getCourseType() { + return courseType; + } + + public void setCourseType(String courseType) { + this.courseType = courseType; + } + } + + /** + * Validation annotations for creationDate + */ + public static class CreationDateValidation { + @NotNull + private LocalDateTime creationDate; + + // Getter and setter + public LocalDateTime getCreationDate() { + return creationDate; + } + + public void setCreationDate(LocalDateTime creationDate) { + this.creationDate = creationDate; + } + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionBusinessRuleValidator.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionBusinessRuleValidator.java new file mode 100644 index 000000000..cd482d466 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionBusinessRuleValidator.java @@ -0,0 +1,47 @@ +package com.generated.microservices.answers.microservices.question.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.question.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +public class QuestionBusinessRuleValidator implements ConstraintValidator { + + @Override + public void initialize(ValidQuestionBusinessRule constraintAnnotation) { + // Initialize validator if needed + } + + @Override + public boolean isValid(Question value, ConstraintValidatorContext context) { + if (value == null) { + return true; // Let @NotNull handle null validation + } + + // Implement business rule validation logic for Question + // Example: Check business hours, validate against external systems, etc. + // TODO: Implement specific business rules + return true; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionInvariants.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionInvariants.java new file mode 100644 index 000000000..f8fe67982 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionInvariants.java @@ -0,0 +1,149 @@ +package com.generated.microservices.answers.microservices.question.validation.invariants; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.question.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +/** + * Invariant validation methods for Question + */ +public class QuestionInvariants { + + /** + * Title cannot be null + */ + public static void invariantTitleNotNull(Question entity) { + if (entity.getTitle() == null) { + throw new IllegalStateException("Title cannot be null"); + } + } + + /** + * Title cannot be blank + */ + public static void invariantTitleNotBlank(Question entity) { + if (entity.getTitle() == null || entity.getTitle().trim().isEmpty()) { + throw new IllegalStateException("Title cannot be blank"); + } + } + + /** + * Content cannot be null + */ + public static void invariantContentNotNull(Question entity) { + if (entity.getContent() == null) { + throw new IllegalStateException("Content cannot be null"); + } + } + + /** + * Content cannot be blank + */ + public static void invariantContentNotBlank(Question entity) { + if (entity.getContent() == null || entity.getContent().trim().isEmpty()) { + throw new IllegalStateException("Content cannot be blank"); + } + } + + /** + * NumberOfOptions cannot be null + */ + public static void invariantNumberOfOptionsNotNull(Question entity) { + if (entity.getNumberOfOptions() == null) { + throw new IllegalStateException("NumberOfOptions cannot be null"); + } + } + + /** + * CorrectOption cannot be null + */ + public static void invariantCorrectOptionNotNull(Question entity) { + if (entity.getCorrectOption() == null) { + throw new IllegalStateException("CorrectOption cannot be null"); + } + } + + /** + * Order cannot be null + */ + public static void invariantOrderNotNull(Question entity) { + if (entity.getOrder() == null) { + throw new IllegalStateException("Order cannot be null"); + } + } + + /** + * Course cannot be null + */ + public static void invariantCourseNotNull(Question entity) { + if (entity.getCourse() == null) { + throw new IllegalStateException("Course cannot be null"); + } + } + + /** + * Topics cannot be null + */ + public static void invariantTopicsNotNull(Question entity) { + if (entity.getTopics() == null) { + throw new IllegalStateException("Topics cannot be null"); + } + } + + /** + * Topics cannot be empty + */ + public static void invariantTopicsNotEmpty(Question entity) { + if (entity.getTopics() == null || ((java.util.Collection) entity.getTopics()).isEmpty()) { + throw new IllegalStateException("Topics cannot be empty"); + } + } + + /** + * Options cannot be null + */ + public static void invariantOptionsNotNull(Question entity) { + if (entity.getOptions() == null) { + throw new IllegalStateException("Options cannot be null"); + } + } + + /** + * Options cannot be empty + */ + public static void invariantOptionsNotEmpty(Question entity) { + if (entity.getOptions() == null || ((java.util.Collection) entity.getOptions()).isEmpty()) { + throw new IllegalStateException("Options cannot be empty"); + } + } + + /** + * Question aggregate must be in a valid state + */ + public static void invariantQuestionValid(Question entity) { + // Aggregate-level validation logic + // Validate business rules that span multiple properties + // Example: startDate must be before endDate + // TODO: Implement aggregate-specific business rules + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations.java new file mode 100644 index 000000000..7a9133ddd --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations.java @@ -0,0 +1,172 @@ +package com.generated.microservices.answers.microservices.question.validation.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.question.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; + + +/** + * Validation annotations for Question properties + */ +public class QuestionValidationAnnotations { + + /** + * Validation annotations for title + */ + public static class TitleValidation { + @NotNull + @NotBlank + private String title; + + // Getter and setter + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + } + + /** + * Validation annotations for content + */ + public static class ContentValidation { + @NotNull + @NotBlank + private String content; + + // Getter and setter + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + } + + /** + * Validation annotations for numberOfOptions + */ + public static class NumberOfOptionsValidation { + @NotNull + private Integer numberOfOptions; + + // Getter and setter + public Integer getNumberOfOptions() { + return numberOfOptions; + } + + public void setNumberOfOptions(Integer numberOfOptions) { + this.numberOfOptions = numberOfOptions; + } + } + + /** + * Validation annotations for correctOption + */ + public static class CorrectOptionValidation { + @NotNull + private Integer correctOption; + + // Getter and setter + public Integer getCorrectOption() { + return correctOption; + } + + public void setCorrectOption(Integer correctOption) { + this.correctOption = correctOption; + } + } + + /** + * Validation annotations for order + */ + public static class OrderValidation { + @NotNull + private Integer order; + + // Getter and setter + public Integer getOrder() { + return order; + } + + public void setOrder(Integer order) { + this.order = order; + } + } + + /** + * Validation annotations for course + */ + public static class CourseValidation { + @NotNull + private Object course; + + // Getter and setter + public Object getCourse() { + return course; + } + + public void setCourse(Object course) { + this.course = course; + } + } + + /** + * Validation annotations for topics + */ + public static class TopicsValidation { + @NotNull + @NotEmpty + private Object topics; + + // Getter and setter + public Object getTopics() { + return topics; + } + + public void setTopics(Object topics) { + this.topics = topics; + } + } + + /** + * Validation annotations for options + */ + public static class OptionsValidation { + @NotNull + @NotEmpty + private Object options; + + // Getter and setter + public Object getOptions() { + return options; + } + + public void setOptions(Object options) { + this.options = options; + } + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizBusinessRuleValidator.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizBusinessRuleValidator.java new file mode 100644 index 000000000..b878134d6 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizBusinessRuleValidator.java @@ -0,0 +1,47 @@ +package com.generated.microservices.answers.microservices.quiz.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +public class QuizBusinessRuleValidator implements ConstraintValidator { + + @Override + public void initialize(ValidQuizBusinessRule constraintAnnotation) { + // Initialize validator if needed + } + + @Override + public boolean isValid(Quiz value, ConstraintValidatorContext context) { + if (value == null) { + return true; // Let @NotNull handle null validation + } + + // Implement business rule validation logic for Quiz + // Example: Check business hours, validate against external systems, etc. + // TODO: Implement specific business rules + return true; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizInvariants.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizInvariants.java new file mode 100644 index 000000000..865c82e04 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizInvariants.java @@ -0,0 +1,167 @@ +package com.generated.microservices.answers.microservices.quiz.validation.invariants; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +/** + * Invariant validation methods for Quiz + */ +public class QuizInvariants { + + /** + * Title cannot be null + */ + public static void invariantTitleNotNull(Quiz entity) { + if (entity.getTitle() == null) { + throw new IllegalStateException("Title cannot be null"); + } + } + + /** + * Title cannot be blank + */ + public static void invariantTitleNotBlank(Quiz entity) { + if (entity.getTitle() == null || entity.getTitle().trim().isEmpty()) { + throw new IllegalStateException("Title cannot be blank"); + } + } + + /** + * Description cannot be null + */ + public static void invariantDescriptionNotNull(Quiz entity) { + if (entity.getDescription() == null) { + throw new IllegalStateException("Description cannot be null"); + } + } + + /** + * Description cannot be blank + */ + public static void invariantDescriptionNotBlank(Quiz entity) { + if (entity.getDescription() == null || entity.getDescription().trim().isEmpty()) { + throw new IllegalStateException("Description cannot be blank"); + } + } + + /** + * QuizType cannot be null + */ + public static void invariantQuizTypeNotNull(Quiz entity) { + if (entity.getQuizType() == null) { + throw new IllegalStateException("QuizType cannot be null"); + } + } + + /** + * QuizType cannot be blank + */ + public static void invariantQuizTypeNotBlank(Quiz entity) { + if (entity.getQuizType() == null || entity.getQuizType().trim().isEmpty()) { + throw new IllegalStateException("QuizType cannot be blank"); + } + } + + /** + * AvailableDate cannot be null + */ + public static void invariantAvailableDateNotNull(Quiz entity) { + if (entity.getAvailableDate() == null) { + throw new IllegalStateException("AvailableDate cannot be null"); + } + } + + /** + * ConclusionDate cannot be null + */ + public static void invariantConclusionDateNotNull(Quiz entity) { + if (entity.getConclusionDate() == null) { + throw new IllegalStateException("ConclusionDate cannot be null"); + } + } + + /** + * NumberOfQuestions cannot be null + */ + public static void invariantNumberOfQuestionsNotNull(Quiz entity) { + if (entity.getNumberOfQuestions() == null) { + throw new IllegalStateException("NumberOfQuestions cannot be null"); + } + } + + /** + * CourseExecution cannot be null + */ + public static void invariantCourseExecutionNotNull(Quiz entity) { + if (entity.getCourseExecution() == null) { + throw new IllegalStateException("CourseExecution cannot be null"); + } + } + + /** + * Questions cannot be null + */ + public static void invariantQuestionsNotNull(Quiz entity) { + if (entity.getQuestions() == null) { + throw new IllegalStateException("Questions cannot be null"); + } + } + + /** + * Questions cannot be empty + */ + public static void invariantQuestionsNotEmpty(Quiz entity) { + if (entity.getQuestions() == null || ((java.util.Collection) entity.getQuestions()).isEmpty()) { + throw new IllegalStateException("Questions cannot be empty"); + } + } + + /** + * Options cannot be null + */ + public static void invariantOptionsNotNull(Quiz entity) { + if (entity.getOptions() == null) { + throw new IllegalStateException("Options cannot be null"); + } + } + + /** + * Options cannot be empty + */ + public static void invariantOptionsNotEmpty(Quiz entity) { + if (entity.getOptions() == null || ((java.util.Collection) entity.getOptions()).isEmpty()) { + throw new IllegalStateException("Options cannot be empty"); + } + } + + /** + * Quiz aggregate must be in a valid state + */ + public static void invariantQuizValid(Quiz entity) { + // Aggregate-level validation logic + // Validate business rules that span multiple properties + // Example: startDate must be before endDate + // TODO: Implement aggregate-specific business rules + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations.java new file mode 100644 index 000000000..343140458 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations.java @@ -0,0 +1,190 @@ +package com.generated.microservices.answers.microservices.quiz.validation.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; + + +/** + * Validation annotations for Quiz properties + */ +public class QuizValidationAnnotations { + + /** + * Validation annotations for title + */ + public static class TitleValidation { + @NotNull + @NotBlank + private String title; + + // Getter and setter + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + } + + /** + * Validation annotations for description + */ + public static class DescriptionValidation { + @NotNull + @NotBlank + private String description; + + // Getter and setter + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + } + + /** + * Validation annotations for quizType + */ + public static class QuizTypeValidation { + @NotNull + @NotBlank + private String quizType; + + // Getter and setter + public String getQuizType() { + return quizType; + } + + public void setQuizType(String quizType) { + this.quizType = quizType; + } + } + + /** + * Validation annotations for availableDate + */ + public static class AvailableDateValidation { + @NotNull + private LocalDateTime availableDate; + + // Getter and setter + public LocalDateTime getAvailableDate() { + return availableDate; + } + + public void setAvailableDate(LocalDateTime availableDate) { + this.availableDate = availableDate; + } + } + + /** + * Validation annotations for conclusionDate + */ + public static class ConclusionDateValidation { + @NotNull + private LocalDateTime conclusionDate; + + // Getter and setter + public LocalDateTime getConclusionDate() { + return conclusionDate; + } + + public void setConclusionDate(LocalDateTime conclusionDate) { + this.conclusionDate = conclusionDate; + } + } + + /** + * Validation annotations for numberOfQuestions + */ + public static class NumberOfQuestionsValidation { + @NotNull + private Integer numberOfQuestions; + + // Getter and setter + public Integer getNumberOfQuestions() { + return numberOfQuestions; + } + + public void setNumberOfQuestions(Integer numberOfQuestions) { + this.numberOfQuestions = numberOfQuestions; + } + } + + /** + * Validation annotations for courseExecution + */ + public static class CourseExecutionValidation { + @NotNull + private Object courseExecution; + + // Getter and setter + public Object getCourseExecution() { + return courseExecution; + } + + public void setCourseExecution(Object courseExecution) { + this.courseExecution = courseExecution; + } + } + + /** + * Validation annotations for questions + */ + public static class QuestionsValidation { + @NotNull + @NotEmpty + private Object questions; + + // Getter and setter + public Object getQuestions() { + return questions; + } + + public void setQuestions(Object questions) { + this.questions = questions; + } + } + + /** + * Validation annotations for options + */ + public static class OptionsValidation { + @NotNull + @NotEmpty + private Object options; + + // Getter and setter + public Object getOptions() { + return options; + } + + public void setOptions(Object options) { + this.options = options; + } + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartDateEndDateRangeValidator.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartDateEndDateRangeValidator.java new file mode 100644 index 000000000..21bddee22 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartDateEndDateRangeValidator.java @@ -0,0 +1,70 @@ +package com.generated.microservices.answers.microservices.courseexecution.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +public class StartDateEndDateRangeValidator implements ConstraintValidator { + + @Override + public void initialize(ValidStartDateEndDateRange constraintAnnotation) { + // Initialize validator if needed + } + + @Override + public boolean isValid(CourseExecution value, ConstraintValidatorContext context) { + if (value == null) { + return true; // Let @NotNull handle null validation + } + + // Validate date range + java.lang.reflect.Field startField = null; + java.lang.reflect.Field endField = null; + + try { + startField = value.getClass().getDeclaredField("startDate"); + endField = value.getClass().getDeclaredField("endDate"); + startField.setAccessible(true); + endField.setAccessible(true); + + Object startValue = startField.get(value); + Object endValue = endField.get(value); + + if (startValue == null || endValue == null) { + return true; // Let other validators handle null values + } + + if (startValue instanceof java.time.LocalDateTime && endValue instanceof java.time.LocalDateTime) { + return ((java.time.LocalDateTime) startValue).isBefore((java.time.LocalDateTime) endValue); + } + + // Add more date type comparisons as needed + return true; + + } catch (Exception e) { + return false; // Validation failed due to reflection issues + } + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartTimeEndTimeRangeValidator.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartTimeEndTimeRangeValidator.java new file mode 100644 index 000000000..c687dcc5b --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartTimeEndTimeRangeValidator.java @@ -0,0 +1,70 @@ +package com.generated.microservices.answers.microservices.tournament.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +public class StartTimeEndTimeRangeValidator implements ConstraintValidator { + + @Override + public void initialize(ValidStartTimeEndTimeRange constraintAnnotation) { + // Initialize validator if needed + } + + @Override + public boolean isValid(Tournament value, ConstraintValidatorContext context) { + if (value == null) { + return true; // Let @NotNull handle null validation + } + + // Validate date range + java.lang.reflect.Field startField = null; + java.lang.reflect.Field endField = null; + + try { + startField = value.getClass().getDeclaredField("startTime"); + endField = value.getClass().getDeclaredField("endTime"); + startField.setAccessible(true); + endField.setAccessible(true); + + Object startValue = startField.get(value); + Object endValue = endField.get(value); + + if (startValue == null || endValue == null) { + return true; // Let other validators handle null values + } + + if (startValue instanceof java.time.LocalDateTime && endValue instanceof java.time.LocalDateTime) { + return ((java.time.LocalDateTime) startValue).isBefore((java.time.LocalDateTime) endValue); + } + + // Add more date type comparisons as needed + return true; + + } catch (Exception e) { + return false; // Validation failed due to reflection issues + } + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicBusinessRuleValidator.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicBusinessRuleValidator.java new file mode 100644 index 000000000..ec252163a --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicBusinessRuleValidator.java @@ -0,0 +1,47 @@ +package com.generated.microservices.answers.microservices.topic.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +public class TopicBusinessRuleValidator implements ConstraintValidator { + + @Override + public void initialize(ValidTopicBusinessRule constraintAnnotation) { + // Initialize validator if needed + } + + @Override + public boolean isValid(Topic value, ConstraintValidatorContext context) { + if (value == null) { + return true; // Let @NotNull handle null validation + } + + // Implement business rule validation logic for Topic + // Example: Check business hours, validate against external systems, etc. + // TODO: Implement specific business rules + return true; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicInvariants.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicInvariants.java new file mode 100644 index 000000000..4fc7ce459 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicInvariants.java @@ -0,0 +1,77 @@ +package com.generated.microservices.answers.microservices.topic.validation.invariants; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +/** + * Invariant validation methods for Topic + */ +public class TopicInvariants { + + /** + * Name cannot be null + */ + public static void invariantNameNotNull(Topic entity) { + if (entity.getName() == null) { + throw new IllegalStateException("Name cannot be null"); + } + } + + /** + * Name cannot be blank + */ + public static void invariantNameNotBlank(Topic entity) { + if (entity.getName() == null || entity.getName().trim().isEmpty()) { + throw new IllegalStateException("Name cannot be blank"); + } + } + + /** + * Course cannot be null + */ + public static void invariantCourseNotNull(Topic entity) { + if (entity.getCourse() == null) { + throw new IllegalStateException("Course cannot be null"); + } + } + + /** + * CreationDate cannot be null + */ + public static void invariantCreationDateNotNull(Topic entity) { + if (entity.getCreationDate() == null) { + throw new IllegalStateException("CreationDate cannot be null"); + } + } + + /** + * Topic aggregate must be in a valid state + */ + public static void invariantTopicValid(Topic entity) { + // Aggregate-level validation logic + // Validate business rules that span multiple properties + // Example: startDate must be before endDate + // TODO: Implement aggregate-specific business rules + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations.java new file mode 100644 index 000000000..3a9a7a990 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations.java @@ -0,0 +1,84 @@ +package com.generated.microservices.answers.microservices.topic.validation.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; + + +/** + * Validation annotations for Topic properties + */ +public class TopicValidationAnnotations { + + /** + * Validation annotations for name + */ + public static class NameValidation { + @NotNull + @NotBlank + private String name; + + // Getter and setter + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + /** + * Validation annotations for course + */ + public static class CourseValidation { + @NotNull + private Object course; + + // Getter and setter + public Object getCourse() { + return course; + } + + public void setCourse(Object course) { + this.course = course; + } + } + + /** + * Validation annotations for creationDate + */ + public static class CreationDateValidation { + @NotNull + private LocalDateTime creationDate; + + // Getter and setter + public LocalDateTime getCreationDate() { + return creationDate; + } + + public void setCreationDate(LocalDateTime creationDate) { + this.creationDate = creationDate; + } + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentBusinessRuleValidator.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentBusinessRuleValidator.java new file mode 100644 index 000000000..e42cf6429 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentBusinessRuleValidator.java @@ -0,0 +1,47 @@ +package com.generated.microservices.answers.microservices.tournament.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +public class TournamentBusinessRuleValidator implements ConstraintValidator { + + @Override + public void initialize(ValidTournamentBusinessRule constraintAnnotation) { + // Initialize validator if needed + } + + @Override + public boolean isValid(Tournament value, ConstraintValidatorContext context) { + if (value == null) { + return true; // Let @NotNull handle null validation + } + + // Implement business rule validation logic for Tournament + // Example: Check business hours, validate against external systems, etc. + // TODO: Implement specific business rules + return true; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentInvariants.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentInvariants.java new file mode 100644 index 000000000..5b1de4198 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentInvariants.java @@ -0,0 +1,140 @@ +package com.generated.microservices.answers.microservices.tournament.validation.invariants; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +/** + * Invariant validation methods for Tournament + */ +public class TournamentInvariants { + + /** + * StartTime cannot be null + */ + public static void invariantStartTimeNotNull(Tournament entity) { + if (entity.getStartTime() == null) { + throw new IllegalStateException("StartTime cannot be null"); + } + } + + /** + * EndTime cannot be null + */ + public static void invariantEndTimeNotNull(Tournament entity) { + if (entity.getEndTime() == null) { + throw new IllegalStateException("EndTime cannot be null"); + } + } + + /** + * NumberOfQuestions cannot be null + */ + public static void invariantNumberOfQuestionsNotNull(Tournament entity) { + if (entity.getNumberOfQuestions() == null) { + throw new IllegalStateException("NumberOfQuestions cannot be null"); + } + } + + /** + * Cancelled cannot be null + */ + public static void invariantCancelledNotNull(Tournament entity) { + if (entity.getCancelled() == null) { + throw new IllegalStateException("Cancelled cannot be null"); + } + } + + /** + * TournamentCreator cannot be null + */ + public static void invariantTournamentCreatorNotNull(Tournament entity) { + if (entity.getTournamentCreator() == null) { + throw new IllegalStateException("TournamentCreator cannot be null"); + } + } + + /** + * TournamentParticipants cannot be null + */ + public static void invariantTournamentParticipantsNotNull(Tournament entity) { + if (entity.getTournamentParticipants() == null) { + throw new IllegalStateException("TournamentParticipants cannot be null"); + } + } + + /** + * TournamentParticipants cannot be empty + */ + public static void invariantTournamentParticipantsNotEmpty(Tournament entity) { + if (entity.getTournamentParticipants() == null || ((java.util.Collection) entity.getTournamentParticipants()).isEmpty()) { + throw new IllegalStateException("TournamentParticipants cannot be empty"); + } + } + + /** + * TournamentCourseExecution cannot be null + */ + public static void invariantTournamentCourseExecutionNotNull(Tournament entity) { + if (entity.getTournamentCourseExecution() == null) { + throw new IllegalStateException("TournamentCourseExecution cannot be null"); + } + } + + /** + * TournamentTopics cannot be null + */ + public static void invariantTournamentTopicsNotNull(Tournament entity) { + if (entity.getTournamentTopics() == null) { + throw new IllegalStateException("TournamentTopics cannot be null"); + } + } + + /** + * TournamentTopics cannot be empty + */ + public static void invariantTournamentTopicsNotEmpty(Tournament entity) { + if (entity.getTournamentTopics() == null || ((java.util.Collection) entity.getTournamentTopics()).isEmpty()) { + throw new IllegalStateException("TournamentTopics cannot be empty"); + } + } + + /** + * TournamentQuiz cannot be null + */ + public static void invariantTournamentQuizNotNull(Tournament entity) { + if (entity.getTournamentQuiz() == null) { + throw new IllegalStateException("TournamentQuiz cannot be null"); + } + } + + /** + * Tournament aggregate must be in a valid state + */ + public static void invariantTournamentValid(Tournament entity) { + // Aggregate-level validation logic + // Validate business rules that span multiple properties + // Example: startDate must be before endDate + // TODO: Implement aggregate-specific business rules + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations.java new file mode 100644 index 000000000..e28ba668a --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations.java @@ -0,0 +1,187 @@ +package com.generated.microservices.answers.microservices.tournament.validation.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; + + +/** + * Validation annotations for Tournament properties + */ +public class TournamentValidationAnnotations { + + /** + * Validation annotations for startTime + */ + public static class StartTimeValidation { + @NotNull + private LocalDateTime startTime; + + // Getter and setter + public LocalDateTime getStartTime() { + return startTime; + } + + public void setStartTime(LocalDateTime startTime) { + this.startTime = startTime; + } + } + + /** + * Validation annotations for endTime + */ + public static class EndTimeValidation { + @NotNull + private LocalDateTime endTime; + + // Getter and setter + public LocalDateTime getEndTime() { + return endTime; + } + + public void setEndTime(LocalDateTime endTime) { + this.endTime = endTime; + } + } + + /** + * Validation annotations for numberOfQuestions + */ + public static class NumberOfQuestionsValidation { + @NotNull + private Integer numberOfQuestions; + + // Getter and setter + public Integer getNumberOfQuestions() { + return numberOfQuestions; + } + + public void setNumberOfQuestions(Integer numberOfQuestions) { + this.numberOfQuestions = numberOfQuestions; + } + } + + /** + * Validation annotations for cancelled + */ + public static class CancelledValidation { + @NotNull + private Boolean cancelled; + + // Getter and setter + public Boolean getCancelled() { + return cancelled; + } + + public void setCancelled(Boolean cancelled) { + this.cancelled = cancelled; + } + } + + /** + * Validation annotations for tournamentCreator + */ + public static class TournamentCreatorValidation { + @NotNull + private Object tournamentCreator; + + // Getter and setter + public Object getTournamentCreator() { + return tournamentCreator; + } + + public void setTournamentCreator(Object tournamentCreator) { + this.tournamentCreator = tournamentCreator; + } + } + + /** + * Validation annotations for tournamentParticipants + */ + public static class TournamentParticipantsValidation { + @NotNull + @NotEmpty + private Object tournamentParticipants; + + // Getter and setter + public Object getTournamentParticipants() { + return tournamentParticipants; + } + + public void setTournamentParticipants(Object tournamentParticipants) { + this.tournamentParticipants = tournamentParticipants; + } + } + + /** + * Validation annotations for tournamentCourseExecution + */ + public static class TournamentCourseExecutionValidation { + @NotNull + private Object tournamentCourseExecution; + + // Getter and setter + public Object getTournamentCourseExecution() { + return tournamentCourseExecution; + } + + public void setTournamentCourseExecution(Object tournamentCourseExecution) { + this.tournamentCourseExecution = tournamentCourseExecution; + } + } + + /** + * Validation annotations for tournamentTopics + */ + public static class TournamentTopicsValidation { + @NotNull + @NotEmpty + private Object tournamentTopics; + + // Getter and setter + public Object getTournamentTopics() { + return tournamentTopics; + } + + public void setTournamentTopics(Object tournamentTopics) { + this.tournamentTopics = tournamentTopics; + } + } + + /** + * Validation annotations for tournamentQuiz + */ + public static class TournamentQuizValidation { + @NotNull + private Object tournamentQuiz; + + // Getter and setter + public Object getTournamentQuiz() { + return tournamentQuiz; + } + + public void setTournamentQuiz(Object tournamentQuiz) { + this.tournamentQuiz = tournamentQuiz; + } + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserBusinessRuleValidator.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserBusinessRuleValidator.java new file mode 100644 index 000000000..902bfdfe9 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserBusinessRuleValidator.java @@ -0,0 +1,47 @@ +package com.generated.microservices.answers.microservices.user.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.user.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +public class UserBusinessRuleValidator implements ConstraintValidator { + + @Override + public void initialize(ValidUserBusinessRule constraintAnnotation) { + // Initialize validator if needed + } + + @Override + public boolean isValid(User value, ConstraintValidatorContext context) { + if (value == null) { + return true; // Let @NotNull handle null validation + } + + // Implement business rule validation logic for User + // Example: Check business hours, validate against external systems, etc. + // TODO: Implement specific business rules + return true; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserInvariants.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserInvariants.java new file mode 100644 index 000000000..197f590d6 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserInvariants.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.user.validation.invariants; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.user.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +/** + * Invariant validation methods for User + */ +public class UserInvariants { + + /** + * Name cannot be null + */ + public static void invariantNameNotNull(User entity) { + if (entity.getName() == null) { + throw new IllegalStateException("Name cannot be null"); + } + } + + /** + * Name cannot be blank + */ + public static void invariantNameNotBlank(User entity) { + if (entity.getName() == null || entity.getName().trim().isEmpty()) { + throw new IllegalStateException("Name cannot be blank"); + } + } + + /** + * Username cannot be null + */ + public static void invariantUsernameNotNull(User entity) { + if (entity.getUsername() == null) { + throw new IllegalStateException("Username cannot be null"); + } + } + + /** + * Username cannot be blank + */ + public static void invariantUsernameNotBlank(User entity) { + if (entity.getUsername() == null || entity.getUsername().trim().isEmpty()) { + throw new IllegalStateException("Username cannot be blank"); + } + } + + /** + * Active cannot be null + */ + public static void invariantActiveNotNull(User entity) { + if (entity.isActive() == null) { + throw new IllegalStateException("Active cannot be null"); + } + } + + /** + * User aggregate must be in a valid state + */ + public static void invariantUserValid(User entity) { + // Aggregate-level validation logic + // Validate business rules that span multiple properties + // Example: startDate must be before endDate + // TODO: Implement aggregate-specific business rules + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserValidationAnnotations.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserValidationAnnotations.java new file mode 100644 index 000000000..4010e944c --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserValidationAnnotations.java @@ -0,0 +1,85 @@ +package com.generated.microservices.answers.microservices.user.validation.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.user.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; + + +/** + * Validation annotations for User properties + */ +public class UserValidationAnnotations { + + /** + * Validation annotations for name + */ + public static class NameValidation { + @NotNull + @NotBlank + private String name; + + // Getter and setter + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + /** + * Validation annotations for username + */ + public static class UsernameValidation { + @NotNull + @NotBlank + private String username; + + // Getter and setter + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + } + + /** + * Validation annotations for active + */ + public static class ActiveValidation { + @NotNull + private Boolean active; + + // Getter and setter + public Boolean getActive() { + return active; + } + + public void setActive(Boolean active) { + this.active = active; + } + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidAnswerBusinessRule.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidAnswerBusinessRule.java new file mode 100644 index 000000000..9c2d679d1 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidAnswerBusinessRule.java @@ -0,0 +1,37 @@ +package com.generated.microservices.answers.microservices.answer.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +@Documented +@Constraint(validatedBy = AnswerBusinessRuleValidator.class) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidAnswerBusinessRule { + String message() default "Answer must comply with business rules"; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidCourseBusinessRule.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidCourseBusinessRule.java new file mode 100644 index 000000000..53ed462ed --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidCourseBusinessRule.java @@ -0,0 +1,37 @@ +package com.generated.microservices.answers.microservices.course.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.course.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +@Documented +@Constraint(validatedBy = CourseBusinessRuleValidator.class) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidCourseBusinessRule { + String message() default "Course must comply with business rules"; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidCourseExecutionBusinessRule.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidCourseExecutionBusinessRule.java new file mode 100644 index 000000000..ce117ee22 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidCourseExecutionBusinessRule.java @@ -0,0 +1,37 @@ +package com.generated.microservices.answers.microservices.courseexecution.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +@Documented +@Constraint(validatedBy = CourseExecutionBusinessRuleValidator.class) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidCourseExecutionBusinessRule { + String message() default "CourseExecution must comply with business rules"; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuestionBusinessRule.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuestionBusinessRule.java new file mode 100644 index 000000000..9d3997ff0 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuestionBusinessRule.java @@ -0,0 +1,37 @@ +package com.generated.microservices.answers.microservices.question.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.question.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +@Documented +@Constraint(validatedBy = QuestionBusinessRuleValidator.class) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidQuestionBusinessRule { + String message() default "Question must comply with business rules"; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuizBusinessRule.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuizBusinessRule.java new file mode 100644 index 000000000..46fbf94c5 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuizBusinessRule.java @@ -0,0 +1,37 @@ +package com.generated.microservices.answers.microservices.quiz.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +@Documented +@Constraint(validatedBy = QuizBusinessRuleValidator.class) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidQuizBusinessRule { + String message() default "Quiz must comply with business rules"; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartDateEndDateRange.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartDateEndDateRange.java new file mode 100644 index 000000000..64d14e5f1 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartDateEndDateRange.java @@ -0,0 +1,37 @@ +package com.generated.microservices.answers.microservices.courseexecution.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +@Documented +@Constraint(validatedBy = StartDateEndDateRangeValidator.class) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidStartDateEndDateRange { + String message() default "StartDate must be before EndDate"; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartTimeEndTimeRange.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartTimeEndTimeRange.java new file mode 100644 index 000000000..9a5312c61 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartTimeEndTimeRange.java @@ -0,0 +1,37 @@ +package com.generated.microservices.answers.microservices.tournament.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +@Documented +@Constraint(validatedBy = StartTimeEndTimeRangeValidator.class) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidStartTimeEndTimeRange { + String message() default "StartTime must be before EndTime"; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidTopicBusinessRule.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidTopicBusinessRule.java new file mode 100644 index 000000000..82e6d0668 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidTopicBusinessRule.java @@ -0,0 +1,37 @@ +package com.generated.microservices.answers.microservices.topic.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +@Documented +@Constraint(validatedBy = TopicBusinessRuleValidator.class) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidTopicBusinessRule { + String message() default "Topic must comply with business rules"; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidTournamentBusinessRule.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidTournamentBusinessRule.java new file mode 100644 index 000000000..ec801c515 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidTournamentBusinessRule.java @@ -0,0 +1,37 @@ +package com.generated.microservices.answers.microservices.tournament.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +@Documented +@Constraint(validatedBy = TournamentBusinessRuleValidator.class) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidTournamentBusinessRule { + String message() default "Tournament must comply with business rules"; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidUserBusinessRule.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidUserBusinessRule.java new file mode 100644 index 000000000..954f5e510 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidUserBusinessRule.java @@ -0,0 +1,37 @@ +package com.generated.microservices.answers.microservices.user.validation.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.generated.microservices.answers.microservices.user.aggregate.*; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; +import jakarta.validation.constraints.*; + + +@Documented +@Constraint(validatedBy = UserBusinessRuleValidator.class) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidUserBusinessRule { + String message() default "User must comply with business rules"; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/AnswerController.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/AnswerController.java new file mode 100644 index 000000000..b132173ac --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/AnswerController.java @@ -0,0 +1,60 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.coordination.webapi; + +import org.springframework.web.bind.annotation.*; +import org.springframework.http.ResponseEntity; +import org.springframework.beans.factory.annotation.Autowired; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.service.AnswerService; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate.QuizAnswerDto; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/answer") +public class AnswerController { + +@Autowired +private AnswerService answerService; + +@GetMapping(value = "/answers") +public ResponseEntity> getAllAnswers( + ) { + List result = answerService.getAllAnswers(); + return ResponseEntity.ok(result); + } + +@GetMapping(value = "/answers/{id}") +public ResponseEntity getAnswer( + @PathVariable Long id + ) throws Exception { + QuizAnswerDto result = answerService.getAnswer(id); + return ResponseEntity.ok(result); + } + +@PostMapping(value = "/answers") +public ResponseEntity createAnswer( + @RequestBody QuizAnswerDto answerDto + ) throws Exception { + QuizAnswerDto result = answerService.createAnswer(answerDto); + return ResponseEntity.ok(result); + } + +@PutMapping(value = "/answers/{id}") +public ResponseEntity updateAnswer( + @PathVariable Long id, + @RequestBody QuizAnswerDto answerDto + ) throws Exception { + QuizAnswerDto result = answerService.updateAnswer(id, + answerDto); + return ResponseEntity.ok(result); + } + +@DeleteMapping(value = "/answers/{id}") +public ResponseEntity deleteAnswer( + @PathVariable Long id + ) throws Exception { + answerService.deleteAnswer(id); + return ResponseEntity.ok().build(); + } + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/BehaviourController.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/BehaviourController.java new file mode 100644 index 000000000..f6d84e19e --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/BehaviourController.java @@ -0,0 +1,44 @@ +package com.generated.microservices.answers.coordination.webapi; + +import org.springframework.web.bind.annotation.*; +import org.springframework.http.ResponseEntity; +import org.springframework.beans.factory.annotation.Autowired; +import com.generated.microservices.ms.BehaviourService; + +@RestController +@RequestMapping("/api/behaviour") +public class BehaviourController { + +@Autowired +private BehaviourService behaviourService; + +@PostMapping("/start") +public ResponseEntity startBehaviour(@RequestBody String behaviourSpec) { + try { + String result = behaviourService.startBehaviour(behaviourSpec); + return ResponseEntity.ok(result); + } catch (Exception e) { + return ResponseEntity.badRequest().body("Error starting behaviour: " + e.getMessage()); + } + } + + @PostMapping("/stop") + public ResponseEntity stopBehaviour() { + try { + behaviourService.stopBehaviour(); + return ResponseEntity.ok("Behaviour stopped successfully"); + } catch (Exception e) { + return ResponseEntity.badRequest().body("Error stopping behaviour: " + e.getMessage()); + } + } + + @GetMapping("/status") + public ResponseEntity getBehaviourStatus() { + try { + String status = behaviourService.getStatus(); + return ResponseEntity.ok(status); + } catch (Exception e) { + return ResponseEntity.badRequest().body("Error getting behaviour status: " + e.getMessage()); + } + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/CourseController.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/CourseController.java new file mode 100644 index 000000000..270e6b604 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/CourseController.java @@ -0,0 +1,60 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.coordination.webapi; + +import org.springframework.web.bind.annotation.*; +import org.springframework.http.ResponseEntity; +import org.springframework.beans.factory.annotation.Autowired; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.service.CourseService; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate.CourseDto; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/course") +public class CourseController { + +@Autowired +private CourseService courseService; + +@GetMapping(value = "/courses") +public ResponseEntity> getAllCourses( + ) { + List result = courseService.getAllCourses(); + return ResponseEntity.ok(result); + } + +@GetMapping(value = "/courses/{id}") +public ResponseEntity getCourse( + @PathVariable Long id + ) throws Exception { + CourseDto result = courseService.getCourse(id); + return ResponseEntity.ok(result); + } + +@PostMapping(value = "/courses") +public ResponseEntity createCourse( + @RequestBody CourseDto courseDto + ) throws Exception { + CourseDto result = courseService.createCourse(courseDto); + return ResponseEntity.ok(result); + } + +@PutMapping(value = "/courses/{id}") +public ResponseEntity updateCourse( + @PathVariable Long id, + @RequestBody CourseDto courseDto + ) throws Exception { + CourseDto result = courseService.updateCourse(id, + courseDto); + return ResponseEntity.ok(result); + } + +@DeleteMapping(value = "/courses/{id}") +public ResponseEntity deleteCourse( + @PathVariable Long id + ) throws Exception { + courseService.deleteCourse(id); + return ResponseEntity.ok().build(); + } + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/CourseExecutionController.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/CourseExecutionController.java new file mode 100644 index 000000000..c5ac41245 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/CourseExecutionController.java @@ -0,0 +1,60 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.coordination.webapi; + +import org.springframework.web.bind.annotation.*; +import org.springframework.http.ResponseEntity; +import org.springframework.beans.factory.annotation.Autowired; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.service.CourseExecutionService; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate.CourseExecutionDto; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/courseexecution") +public class CourseExecutionController { + +@Autowired +private CourseExecutionService courseexecutionService; + +@GetMapping(value = "/courseexecutions") +public ResponseEntity> getAllCourseExecutions( + ) { + List result = courseexecutionService.getAllCourseExecutions(); + return ResponseEntity.ok(result); + } + +@GetMapping(value = "/courseexecutions/{id}") +public ResponseEntity getCourseExecution( + @PathVariable Long id + ) throws Exception { + CourseExecutionDto result = courseexecutionService.getCourseExecution(id); + return ResponseEntity.ok(result); + } + +@PostMapping(value = "/courseexecutions") +public ResponseEntity createCourseExecution( + @RequestBody CourseExecutionDto courseexecutionDto + ) throws Exception { + CourseExecutionDto result = courseexecutionService.createCourseExecution(courseexecutionDto); + return ResponseEntity.ok(result); + } + +@PutMapping(value = "/courseexecutions/{id}") +public ResponseEntity updateCourseExecution( + @PathVariable Long id, + @RequestBody CourseExecutionDto courseexecutionDto + ) throws Exception { + CourseExecutionDto result = courseexecutionService.updateCourseExecution(id, + courseexecutionDto); + return ResponseEntity.ok(result); + } + +@DeleteMapping(value = "/courseexecutions/{id}") +public ResponseEntity deleteCourseExecution( + @PathVariable Long id + ) throws Exception { + courseexecutionService.deleteCourseExecution(id); + return ResponseEntity.ok().build(); + } + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/QuestionController.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/QuestionController.java new file mode 100644 index 000000000..c2e01f1ce --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/QuestionController.java @@ -0,0 +1,60 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.coordination.webapi; + +import org.springframework.web.bind.annotation.*; +import org.springframework.http.ResponseEntity; +import org.springframework.beans.factory.annotation.Autowired; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.service.QuestionService; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.QuestionDto; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/question") +public class QuestionController { + +@Autowired +private QuestionService questionService; + +@GetMapping(value = "/questions") +public ResponseEntity> getAllQuestions( + ) { + List result = questionService.getAllQuestions(); + return ResponseEntity.ok(result); + } + +@GetMapping(value = "/questions/{id}") +public ResponseEntity getQuestion( + @PathVariable Long id + ) throws Exception { + QuestionDto result = questionService.getQuestion(id); + return ResponseEntity.ok(result); + } + +@PostMapping(value = "/questions") +public ResponseEntity createQuestion( + @RequestBody QuestionDto questionDto + ) throws Exception { + QuestionDto result = questionService.createQuestion(questionDto); + return ResponseEntity.ok(result); + } + +@PutMapping(value = "/questions/{id}") +public ResponseEntity updateQuestion( + @PathVariable Long id, + @RequestBody QuestionDto questionDto + ) throws Exception { + QuestionDto result = questionService.updateQuestion(id, + questionDto); + return ResponseEntity.ok(result); + } + +@DeleteMapping(value = "/questions/{id}") +public ResponseEntity deleteQuestion( + @PathVariable Long id + ) throws Exception { + questionService.deleteQuestion(id); + return ResponseEntity.ok().build(); + } + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/QuizController.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/QuizController.java new file mode 100644 index 000000000..675c68100 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/QuizController.java @@ -0,0 +1,60 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.coordination.webapi; + +import org.springframework.web.bind.annotation.*; +import org.springframework.http.ResponseEntity; +import org.springframework.beans.factory.annotation.Autowired; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.service.QuizService; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.QuizDto; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/quiz") +public class QuizController { + +@Autowired +private QuizService quizService; + +@GetMapping(value = "/quizs") +public ResponseEntity> getAllQuizs( + ) { + List result = quizService.getAllQuizs(); + return ResponseEntity.ok(result); + } + +@GetMapping(value = "/quizs/{id}") +public ResponseEntity getQuiz( + @PathVariable Long id + ) throws Exception { + QuizDto result = quizService.getQuiz(id); + return ResponseEntity.ok(result); + } + +@PostMapping(value = "/quizs") +public ResponseEntity createQuiz( + @RequestBody QuizDto quizDto + ) throws Exception { + QuizDto result = quizService.createQuiz(quizDto); + return ResponseEntity.ok(result); + } + +@PutMapping(value = "/quizs/{id}") +public ResponseEntity updateQuiz( + @PathVariable Long id, + @RequestBody QuizDto quizDto + ) throws Exception { + QuizDto result = quizService.updateQuiz(id, + quizDto); + return ResponseEntity.ok(result); + } + +@DeleteMapping(value = "/quizs/{id}") +public ResponseEntity deleteQuiz( + @PathVariable Long id + ) throws Exception { + quizService.deleteQuiz(id); + return ResponseEntity.ok().build(); + } + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TopicController.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TopicController.java new file mode 100644 index 000000000..3fe5c541b --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TopicController.java @@ -0,0 +1,60 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.coordination.webapi; + +import org.springframework.web.bind.annotation.*; +import org.springframework.http.ResponseEntity; +import org.springframework.beans.factory.annotation.Autowired; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.service.TopicService; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate.TopicDto; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/topic") +public class TopicController { + +@Autowired +private TopicService topicService; + +@GetMapping(value = "/topics") +public ResponseEntity> getAllTopics( + ) { + List result = topicService.getAllTopics(); + return ResponseEntity.ok(result); + } + +@GetMapping(value = "/topics/{id}") +public ResponseEntity getTopic( + @PathVariable Long id + ) throws Exception { + TopicDto result = topicService.getTopic(id); + return ResponseEntity.ok(result); + } + +@PostMapping(value = "/topics") +public ResponseEntity createTopic( + @RequestBody TopicDto topicDto + ) throws Exception { + TopicDto result = topicService.createTopic(topicDto); + return ResponseEntity.ok(result); + } + +@PutMapping(value = "/topics/{id}") +public ResponseEntity updateTopic( + @PathVariable Long id, + @RequestBody TopicDto topicDto + ) throws Exception { + TopicDto result = topicService.updateTopic(id, + topicDto); + return ResponseEntity.ok(result); + } + +@DeleteMapping(value = "/topics/{id}") +public ResponseEntity deleteTopic( + @PathVariable Long id + ) throws Exception { + topicService.deleteTopic(id); + return ResponseEntity.ok().build(); + } + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TournamentController.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TournamentController.java new file mode 100644 index 000000000..0bf015870 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TournamentController.java @@ -0,0 +1,44 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.coordination.webapi; + +import org.springframework.web.bind.annotation.*; +import org.springframework.http.ResponseEntity; +import org.springframework.beans.factory.annotation.Autowired; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.service.TournamentService; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/tournament") +public class TournamentController { + +@Autowired +private TournamentService tournamentService; + +@GetMapping(value = "/executions/{executionId}/tournaments/create") +public ResponseEntity createTournament( + @PathVariable Integer executionId, + @RequestParam Integer userId, + @RequestParam String topicsId, + @RequestBody Object tournamentDto + ) throws Exception { + Object result = tournamentService.createTournament(executionId, + userId, + topicsId, + tournamentDto); + return ResponseEntity.ok(result); + } + +@GetMapping(value = "/tournaments/{tournamentAggregateId}/join") +public ResponseEntity joinTournament( + @PathVariable Integer tournamentAggregateId, + @RequestParam Integer executionAggregateId, + @RequestParam Integer userAggregateId + ) throws Exception { + String result = tournamentService.joinTournament(tournamentAggregateId, + executionAggregateId, + userAggregateId); + return ResponseEntity.ok(result); + } + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TracesController.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TracesController.java new file mode 100644 index 000000000..86493783b --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TracesController.java @@ -0,0 +1,56 @@ +package com.generated.microservices.answers.coordination.webapi; + +import org.springframework.web.bind.annotation.*; +import org.springframework.http.ResponseEntity; +import org.springframework.beans.factory.annotation.Autowired; +import pt.ulisboa.tecnico.socialsoftware.ms.TracesService; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/traces") +public class TracesController { + +@Autowired +private TracesService tracesService; + +@GetMapping("/all") +public ResponseEntity>> getAllTraces() { + try { + List> traces = tracesService.getAllTraces(); + return ResponseEntity.ok(traces); + } catch (Exception e) { + return ResponseEntity.badRequest().body(null); + } + } + + @GetMapping("/aggregate/{aggregateName}") + public ResponseEntity>> getTracesByAggregate(@PathVariable String aggregateName) { + try { + List> traces = tracesService.getTracesByAggregate(aggregateName); + return ResponseEntity.ok(traces); + } catch (Exception e) { + return ResponseEntity.badRequest().body(null); + } + } + + @PostMapping("/clear") + public ResponseEntity clearTraces() { + try { + tracesService.clearTraces(); + return ResponseEntity.ok("Traces cleared successfully"); + } catch (Exception e) { + return ResponseEntity.badRequest().body("Error clearing traces: " + e.getMessage()); + } + } + + @GetMapping("/export") + public ResponseEntity exportTraces(@RequestParam(defaultValue = "json") String format) { + try { + String exportData = tracesService.exportTraces(format); + return ResponseEntity.ok(exportData); + } catch (Exception e) { + return ResponseEntity.badRequest().body("Error exporting traces: " + e.getMessage()); + } + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/UserController.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/UserController.java new file mode 100644 index 000000000..c3862f043 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/UserController.java @@ -0,0 +1,60 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.coordination.webapi; + +import org.springframework.web.bind.annotation.*; +import org.springframework.http.ResponseEntity; +import org.springframework.beans.factory.annotation.Autowired; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.service.UserService; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.UserDto; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/user") +public class UserController { + +@Autowired +private UserService userService; + +@GetMapping(value = "/users") +public ResponseEntity> getAllUsers( + ) { + List result = userService.getAllUsers(); + return ResponseEntity.ok(result); + } + +@GetMapping(value = "/users/{id}") +public ResponseEntity getUser( + @PathVariable Long id + ) throws Exception { + UserDto result = userService.getUser(id); + return ResponseEntity.ok(result); + } + +@PostMapping(value = "/users") +public ResponseEntity createUser( + @RequestBody UserDto userDto + ) throws Exception { + UserDto result = userService.createUser(userDto); + return ResponseEntity.ok(result); + } + +@PutMapping(value = "/users/{id}") +public ResponseEntity updateUser( + @PathVariable Long id, + @RequestBody UserDto userDto + ) throws Exception { + UserDto result = userService.updateUser(id, + userDto); + return ResponseEntity.ok(result); + } + +@DeleteMapping(value = "/users/{id}") +public ResponseEntity deleteUser( + @PathVariable Long id + ) throws Exception { + userService.deleteUser(id); + return ResponseEntity.ok().build(); + } + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/Answer.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/Answer.java new file mode 100644 index 000000000..befc2f5a6 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/Answer.java @@ -0,0 +1,24 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import jakarta.persistence.MappedSuperclass; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventSubscription; +import java.util.Set; +import java.util.HashSet; + +@MappedSuperclass +public abstract class Answer extends Aggregate { + // Abstract base class for Answer aggregate + // Concrete implementation is QuizAnswer + + @Override + public Set getEventSubscriptions() { + return new HashSet<>(); + } + + @Override + public void verifyInvariants() { + // Invariant verification implementation + // Override in concrete classes if needed + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerCustomRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerCustomRepository.java new file mode 100644 index 000000000..6de760fe6 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerCustomRepository.java @@ -0,0 +1,9 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import java.util.Optional; +import java.util.List; +import java.util.Set; + +public interface AnswerCustomRepository { + Optional findQuizAnswerIdByQuizIdAndUserId(Integer quizAggregateId, Integer studentAggregateId); +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerFactory.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerFactory.java new file mode 100644 index 000000000..cc752a911 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerFactory.java @@ -0,0 +1,37 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import org.springframework.stereotype.Service; +import pt.ulisboa.tecnico.socialsoftware.ms.causal.aggregate.CausalAggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWorkService; +import pt.ulisboa.tecnico.socialsoftware.ms.sagas.aggregate.SagaAggregate; + +@Service +public class AnswerFactory { + + public Answer createAnswer(Integer aggregateId, QuizAnswerDto answerDto) { + // Factory method implementation - create root entity directly + // Extract properties from DTO and create the root entity + return new QuizAnswer( + answerDto.getAnswerDate(), + answerDto.getCompletedDate(), + answerDto.getCompleted(), + answerDto.getQuizAnswerStudent(), + answerDto.getQuizAnswerCourseExecution(), + answerDto.getQuestionAnswers(), + answerDto.getAnsweredQuiz() + ); + } + + public Answer createAnswerFromExisting(Answer existingAnswer) { + // Create a copy of the existing aggregate + if (existingAnswer instanceof QuizAnswer) { + return new QuizAnswer((QuizAnswer) existingAnswer); + } + throw new IllegalArgumentException("Unknown aggregate type"); + } + + public QuizAnswerDto createQuizAnswerDto(Answer answer) { + return new QuizAnswerDto((QuizAnswer) answer); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerRepository.java new file mode 100644 index 000000000..c00434a06 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerRepository.java @@ -0,0 +1,13 @@ +package com.generated.microservices.answers.microservices.answer.aggregate; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import jakarta.transaction.Transactional; + +@Repository +@Transactional +public interface AnswerRepository extends JpaRepository { + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnsweredQuiz.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnsweredQuiz.java new file mode 100644 index 000000000..e6f907e96 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnsweredQuiz.java @@ -0,0 +1,80 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +@Embeddable +public class AnsweredQuiz { + private Integer quizAggregateId; + private String quizTitle; + private String quizType; + private LocalDateTime availableDate; + private LocalDateTime conclusionDate; + private Integer numberOfQuestions; + + public AnsweredQuiz(Integer quizAggregateId, String quizTitle, String quizType, LocalDateTime availableDate, LocalDateTime conclusionDate, Integer numberOfQuestions) { + this.quizAggregateId = quizAggregateId; + this.quizTitle = quizTitle; + this.quizType = quizType; + this.availableDate = availableDate; + this.conclusionDate = conclusionDate; + this.numberOfQuestions = numberOfQuestions; + } + + public AnsweredQuiz(AnsweredQuiz other) { + // Copy constructor + } + + + public Integer getQuizAggregateId() { + return quizAggregateId; + } + + public void setQuizAggregateId(Integer quizAggregateId) { + this.quizAggregateId = quizAggregateId; + } + + public String getQuizTitle() { + return quizTitle; + } + + public void setQuizTitle(String quizTitle) { + this.quizTitle = quizTitle; + } + + public String getQuizType() { + return quizType; + } + + public void setQuizType(String quizType) { + this.quizType = quizType; + } + + public LocalDateTime getAvailableDate() { + return availableDate; + } + + public void setAvailableDate(LocalDateTime availableDate) { + this.availableDate = availableDate; + } + + public LocalDateTime getConclusionDate() { + return conclusionDate; + } + + public void setConclusionDate(LocalDateTime conclusionDate) { + this.conclusionDate = conclusionDate; + } + + public Integer getNumberOfQuestions() { + return numberOfQuestions; + } + + public void setNumberOfQuestions(Integer numberOfQuestions) { + this.numberOfQuestions = numberOfQuestions; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnsweredQuizDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnsweredQuizDto.java new file mode 100644 index 000000000..c79801138 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnsweredQuizDto.java @@ -0,0 +1,76 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class AnsweredQuizDto implements Serializable { + private Integer quizAggregateId; + private String quizTitle; + private String quizType; + private LocalDateTime availableDate; + private LocalDateTime conclusionDate; + private Integer numberOfQuestions; + + public AnsweredQuizDto() { + } + + public AnsweredQuizDto(AnsweredQuiz answeredquiz) { + this.quizAggregateId = answeredquiz.getQuizAggregateId(); + this.quizTitle = answeredquiz.getQuizTitle(); + this.quizType = answeredquiz.getQuizType(); + this.availableDate = answeredquiz.getAvailableDate(); + this.conclusionDate = answeredquiz.getConclusionDate(); + this.numberOfQuestions = answeredquiz.getNumberOfQuestions(); + } + + public Integer getQuizAggregateId() { + return quizAggregateId; + } + + public void setQuizAggregateId(Integer quizAggregateId) { + this.quizAggregateId = quizAggregateId; + } + + public String getQuizTitle() { + return quizTitle; + } + + public void setQuizTitle(String quizTitle) { + this.quizTitle = quizTitle; + } + + public String getQuizType() { + return quizType; + } + + public void setQuizType(String quizType) { + this.quizType = quizType; + } + + public LocalDateTime getAvailableDate() { + return availableDate; + } + + public void setAvailableDate(LocalDateTime availableDate) { + this.availableDate = availableDate; + } + + public LocalDateTime getConclusionDate() { + return conclusionDate; + } + + public void setConclusionDate(LocalDateTime conclusionDate) { + this.conclusionDate = conclusionDate; + } + + public Integer getNumberOfQuestions() { + return numberOfQuestions; + } + + public void setNumberOfQuestions(Integer numberOfQuestions) { + this.numberOfQuestions = numberOfQuestions; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuestionAnswer.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuestionAnswer.java new file mode 100644 index 000000000..3d28de875 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuestionAnswer.java @@ -0,0 +1,60 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +@Embeddable +public class QuestionAnswer { + private Integer questionId; + private String answer; + private String option; + private LocalDateTime answerDate; + + public QuestionAnswer(Integer questionId, String answer, String option, LocalDateTime answerDate) { + this.questionId = questionId; + this.answer = answer; + this.option = option; + this.answerDate = answerDate; + } + + public QuestionAnswer(QuestionAnswer other) { + // Copy constructor + } + + + public Integer getQuestionId() { + return questionId; + } + + public void setQuestionId(Integer questionId) { + this.questionId = questionId; + } + + public String getAnswer() { + return answer; + } + + public void setAnswer(String answer) { + this.answer = answer; + } + + public String getOption() { + return option; + } + + public void setOption(String option) { + this.option = option; + } + + public LocalDateTime getAnswerDate() { + return answerDate; + } + + public void setAnswerDate(LocalDateTime answerDate) { + this.answerDate = answerDate; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuestionAnswerDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuestionAnswerDto.java new file mode 100644 index 000000000..9fec4594b --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuestionAnswerDto.java @@ -0,0 +1,56 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class QuestionAnswerDto implements Serializable { + private Integer questionId; + private String answer; + private String option; + private LocalDateTime answerDate; + + public QuestionAnswerDto() { + } + + public QuestionAnswerDto(QuestionAnswer questionanswer) { + this.questionId = questionanswer.getQuestionId(); + this.answer = questionanswer.getAnswer(); + this.option = questionanswer.getOption(); + this.answerDate = questionanswer.getAnswerDate(); + } + + public Integer getQuestionId() { + return questionId; + } + + public void setQuestionId(Integer questionId) { + this.questionId = questionId; + } + + public String getAnswer() { + return answer; + } + + public void setAnswer(String answer) { + this.answer = answer; + } + + public String getOption() { + return option; + } + + public void setOption(String option) { + this.option = option; + } + + public LocalDateTime getAnswerDate() { + return answerDate; + } + + public void setAnswerDate(LocalDateTime answerDate) { + this.answerDate = answerDate; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswer.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswer.java new file mode 100644 index 000000000..d5cf3efdb --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswer.java @@ -0,0 +1,120 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +@Entity +public class QuizAnswer extends Answer { + @Id + private LocalDateTime answerDate; + private LocalDateTime completedDate; + private Boolean completed; + private Object quizAnswerStudent; + private Object quizAnswerCourseExecution; + private Object questionAnswers; + private Object answeredQuiz; + + public QuizAnswer(LocalDateTime answerDate, LocalDateTime completedDate, Boolean completed, Object quizAnswerStudent, Object quizAnswerCourseExecution, Object questionAnswers, Object answeredQuiz) { + this.answerDate = answerDate; + this.completedDate = completedDate; + this.completed = completed; + this.quizAnswerStudent = quizAnswerStudent; + this.quizAnswerCourseExecution = quizAnswerCourseExecution; + this.questionAnswers = questionAnswers; + this.answeredQuiz = answeredQuiz; + } + + public QuizAnswer(QuizAnswer other) { + // Copy constructor + } + + + public LocalDateTime getAnswerDate() { + return answerDate; + } + + public void setAnswerDate(LocalDateTime answerDate) { + this.answerDate = answerDate; + } + + public LocalDateTime getCompletedDate() { + return completedDate; + } + + public void setCompletedDate(LocalDateTime completedDate) { + this.completedDate = completedDate; + } + + public Boolean isCompleted() { + return completed; + } + + public void setCompleted(Boolean completed) { + this.completed = completed; + } + + public Object getQuizAnswerStudent() { + return quizAnswerStudent; + } + + public void setQuizAnswerStudent(Object quizAnswerStudent) { + this.quizAnswerStudent = quizAnswerStudent; + } + + public Object getQuizAnswerCourseExecution() { + return quizAnswerCourseExecution; + } + + public void setQuizAnswerCourseExecution(Object quizAnswerCourseExecution) { + this.quizAnswerCourseExecution = quizAnswerCourseExecution; + } + + public Object getQuestionAnswers() { + return questionAnswers; + } + + public void setQuestionAnswers(Object questionAnswers) { + this.questionAnswers = questionAnswers; + } + + public Object getAnsweredQuiz() { + return answeredQuiz; + } + + public void setAnsweredQuiz(Object answeredQuiz) { + this.answeredQuiz = answeredQuiz; + } + public Object createQuizAnswer(Object student, Object courseExecution, Object quiz, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getQuizAnswerById(Integer quizAnswerId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getQuizAnswersByStudent(Integer studentId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getQuizAnswersByQuiz(Integer quizId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object submitAnswer(Integer quizAnswerId, Integer questionId, String answer, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object completeQuizAnswer(Integer quizAnswerId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerCourseExecution.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerCourseExecution.java new file mode 100644 index 000000000..5c0aae30e --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerCourseExecution.java @@ -0,0 +1,59 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class QuizAnswerCourseExecution { + private Integer courseExecutionAggregateId; + private String courseExecutionName; + private String courseExecutionAcronym; + private String courseExecutionAcademicTerm; + + public QuizAnswerCourseExecution(Integer courseExecutionAggregateId, String courseExecutionName, String courseExecutionAcronym, String courseExecutionAcademicTerm) { + this.courseExecutionAggregateId = courseExecutionAggregateId; + this.courseExecutionName = courseExecutionName; + this.courseExecutionAcronym = courseExecutionAcronym; + this.courseExecutionAcademicTerm = courseExecutionAcademicTerm; + } + + public QuizAnswerCourseExecution(QuizAnswerCourseExecution other) { + // Copy constructor + } + + + public Integer getCourseExecutionAggregateId() { + return courseExecutionAggregateId; + } + + public void setCourseExecutionAggregateId(Integer courseExecutionAggregateId) { + this.courseExecutionAggregateId = courseExecutionAggregateId; + } + + public String getCourseExecutionName() { + return courseExecutionName; + } + + public void setCourseExecutionName(String courseExecutionName) { + this.courseExecutionName = courseExecutionName; + } + + public String getCourseExecutionAcronym() { + return courseExecutionAcronym; + } + + public void setCourseExecutionAcronym(String courseExecutionAcronym) { + this.courseExecutionAcronym = courseExecutionAcronym; + } + + public String getCourseExecutionAcademicTerm() { + return courseExecutionAcademicTerm; + } + + public void setCourseExecutionAcademicTerm(String courseExecutionAcademicTerm) { + this.courseExecutionAcademicTerm = courseExecutionAcademicTerm; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerCourseExecutionDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerCourseExecutionDto.java new file mode 100644 index 000000000..3e2237e0f --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerCourseExecutionDto.java @@ -0,0 +1,55 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class QuizAnswerCourseExecutionDto implements Serializable { + private Integer courseExecutionAggregateId; + private String courseExecutionName; + private String courseExecutionAcronym; + private String courseExecutionAcademicTerm; + + public QuizAnswerCourseExecutionDto() { + } + + public QuizAnswerCourseExecutionDto(QuizAnswerCourseExecution quizanswercourseexecution) { + this.courseExecutionAggregateId = quizanswercourseexecution.getCourseExecutionAggregateId(); + this.courseExecutionName = quizanswercourseexecution.getCourseExecutionName(); + this.courseExecutionAcronym = quizanswercourseexecution.getCourseExecutionAcronym(); + this.courseExecutionAcademicTerm = quizanswercourseexecution.getCourseExecutionAcademicTerm(); + } + + public Integer getCourseExecutionAggregateId() { + return courseExecutionAggregateId; + } + + public void setCourseExecutionAggregateId(Integer courseExecutionAggregateId) { + this.courseExecutionAggregateId = courseExecutionAggregateId; + } + + public String getCourseExecutionName() { + return courseExecutionName; + } + + public void setCourseExecutionName(String courseExecutionName) { + this.courseExecutionName = courseExecutionName; + } + + public String getCourseExecutionAcronym() { + return courseExecutionAcronym; + } + + public void setCourseExecutionAcronym(String courseExecutionAcronym) { + this.courseExecutionAcronym = courseExecutionAcronym; + } + + public String getCourseExecutionAcademicTerm() { + return courseExecutionAcademicTerm; + } + + public void setCourseExecutionAcademicTerm(String courseExecutionAcademicTerm) { + this.courseExecutionAcademicTerm = courseExecutionAcademicTerm; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerDto.java new file mode 100644 index 000000000..b80348e1d --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerDto.java @@ -0,0 +1,116 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Set; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class QuizAnswerDto implements Serializable { + private Integer aggregateId; + private LocalDateTime answerDate; + private LocalDateTime completedDate; + private boolean completed; + private Object quizAnswerStudent; + private Object quizAnswerCourseExecution; + private Object questionAnswers; + private Object answeredQuiz; + private Integer version; + private AggregateState state; + + public QuizAnswerDto() { + } + + public QuizAnswerDto(QuizAnswer quizanswer) { + this.aggregateId = quizanswer.getAggregateId(); + this.answerDate = quizanswer.getAnswerDate(); + this.completedDate = quizanswer.getCompletedDate(); + this.completed = quizanswer.isCompleted(); + this.quizAnswerStudent = quizanswer.getQuizAnswerStudent(); + this.quizAnswerCourseExecution = quizanswer.getQuizAnswerCourseExecution(); + this.questionAnswers = quizanswer.getQuestionAnswers(); + this.answeredQuiz = quizanswer.getAnsweredQuiz(); + this.version = quizanswer.getVersion(); + this.state = quizanswer.getState(); + } + + public Integer getAggregateId() { + return aggregateId; + } + + public void setAggregateId(Integer aggregateId) { + this.aggregateId = aggregateId; + } + + public LocalDateTime getAnswerDate() { + return answerDate; + } + + public void setAnswerDate(LocalDateTime answerDate) { + this.answerDate = answerDate; + } + + public LocalDateTime getCompletedDate() { + return completedDate; + } + + public void setCompletedDate(LocalDateTime completedDate) { + this.completedDate = completedDate; + } + + public boolean isCompleted() { + return completed; + } + + public void setCompleted(Boolean completed) { + this.completed = completed; + } + + public Object getQuizAnswerStudent() { + return quizAnswerStudent; + } + + public void setQuizAnswerStudent(Object quizAnswerStudent) { + this.quizAnswerStudent = quizAnswerStudent; + } + + public Object getQuizAnswerCourseExecution() { + return quizAnswerCourseExecution; + } + + public void setQuizAnswerCourseExecution(Object quizAnswerCourseExecution) { + this.quizAnswerCourseExecution = quizAnswerCourseExecution; + } + + public Object getQuestionAnswers() { + return questionAnswers; + } + + public void setQuestionAnswers(Object questionAnswers) { + this.questionAnswers = questionAnswers; + } + + public Object getAnsweredQuiz() { + return answeredQuiz; + } + + public void setAnsweredQuiz(Object answeredQuiz) { + this.answeredQuiz = answeredQuiz; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public AggregateState getState() { + return state; + } + + public void setState(AggregateState state) { + this.state = state; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerStudent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerStudent.java new file mode 100644 index 000000000..4ca6d144c --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerStudent.java @@ -0,0 +1,59 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class QuizAnswerStudent { + private Integer studentAggregateId; + private String studentName; + private String studentUsername; + private String studentEmail; + + public QuizAnswerStudent(Integer studentAggregateId, String studentName, String studentUsername, String studentEmail) { + this.studentAggregateId = studentAggregateId; + this.studentName = studentName; + this.studentUsername = studentUsername; + this.studentEmail = studentEmail; + } + + public QuizAnswerStudent(QuizAnswerStudent other) { + // Copy constructor + } + + + public Integer getStudentAggregateId() { + return studentAggregateId; + } + + public void setStudentAggregateId(Integer studentAggregateId) { + this.studentAggregateId = studentAggregateId; + } + + public String getStudentName() { + return studentName; + } + + public void setStudentName(String studentName) { + this.studentName = studentName; + } + + public String getStudentUsername() { + return studentUsername; + } + + public void setStudentUsername(String studentUsername) { + this.studentUsername = studentUsername; + } + + public String getStudentEmail() { + return studentEmail; + } + + public void setStudentEmail(String studentEmail) { + this.studentEmail = studentEmail; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerStudentDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerStudentDto.java new file mode 100644 index 000000000..8623d0a61 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerStudentDto.java @@ -0,0 +1,55 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class QuizAnswerStudentDto implements Serializable { + private Integer studentAggregateId; + private String studentName; + private String studentUsername; + private String studentEmail; + + public QuizAnswerStudentDto() { + } + + public QuizAnswerStudentDto(QuizAnswerStudent quizanswerstudent) { + this.studentAggregateId = quizanswerstudent.getStudentAggregateId(); + this.studentName = quizanswerstudent.getStudentName(); + this.studentUsername = quizanswerstudent.getStudentUsername(); + this.studentEmail = quizanswerstudent.getStudentEmail(); + } + + public Integer getStudentAggregateId() { + return studentAggregateId; + } + + public void setStudentAggregateId(Integer studentAggregateId) { + this.studentAggregateId = studentAggregateId; + } + + public String getStudentName() { + return studentName; + } + + public void setStudentName(String studentName) { + this.studentName = studentName; + } + + public String getStudentUsername() { + return studentUsername; + } + + public void setStudentUsername(String studentUsername) { + this.studentUsername = studentUsername; + } + + public String getStudentEmail() { + return studentEmail; + } + + public void setStudentEmail(String studentEmail) { + this.studentEmail = studentEmail; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/AnswerEventHandling.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/AnswerEventHandling.java new file mode 100644 index 000000000..3761ecff7 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/AnswerEventHandling.java @@ -0,0 +1,192 @@ +package com.generated.microservices.answers.microservices.answer.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + + +@Component +public class AnswerEventHandling { +private static final Logger logger = LoggerFactory.getLogger(AnswerEventHandling.class); + +private final AnswerService answerService; +private final AnswerRepository answerRepository; + +public AnswerEventHandling(AnswerService answerService, AnswerRepository +answerRepository) { +this.answerService = answerService; +this.answerRepository = answerRepository; +} + +@EventListener +public void handleCreated(AnswerCreatedEvent event) { +try { +logger.info("Processing Created event for Answer with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Created": +processCreatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing AnswerCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process AnswerCreatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncCreated(AnswerCreatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Created event for Answer with ID: {}", event.getAggregateId()); + processAsyncCreatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of AnswerCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleUpdated(AnswerUpdatedEvent event) { +try { +logger.info("Processing Updated event for Answer with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Updated": +processUpdatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing AnswerUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process AnswerUpdatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncUpdated(AnswerUpdatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Updated event for Answer with ID: {}", event.getAggregateId()); + processAsyncUpdatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of AnswerUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleDeleted(AnswerDeletedEvent event) { +try { +logger.info("Processing Deleted event for Answer with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Deleted": +processDeletedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing AnswerDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process AnswerDeletedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncDeleted(AnswerDeletedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Deleted event for Answer with ID: {}", event.getAggregateId()); + processAsyncDeletedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of AnswerDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + + + // Event processing methods + private void processCreatedEvent(AnswerCreatedEvent event) { + // TODO: Implement Created event processing logic + logger.debug("Processing Created event for Answer ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle creation event - might trigger welcome workflows, notifications, etc. + handleCreationEvent(event); + } + + private void processAsyncCreatedEvent(AnswerCreatedEvent event) { + // TODO: Implement async Created event processing logic + logger.debug("Async processing Created event for Answer ID: {}", event.getAggregateId()); + } + + private void processUpdatedEvent(AnswerUpdatedEvent event) { + // TODO: Implement Updated event processing logic + logger.debug("Processing Updated event for Answer ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle update event - might trigger validation, sync with external systems, etc. + handleUpdateEvent(event); + } + + private void processAsyncUpdatedEvent(AnswerUpdatedEvent event) { + // TODO: Implement async Updated event processing logic + logger.debug("Async processing Updated event for Answer ID: {}", event.getAggregateId()); + } + + private void processDeletedEvent(AnswerDeletedEvent event) { + // TODO: Implement Deleted event processing logic + logger.debug("Processing Deleted event for Answer ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle deletion event - might trigger cleanup, archival, notifications, etc. + handleDeletionEvent(event); + } + + private void processAsyncDeletedEvent(AnswerDeletedEvent event) { + // TODO: Implement async Deleted event processing logic + logger.debug("Async processing Deleted event for Answer ID: {}", event.getAggregateId()); + } + + + // Helper methods for specific event types + private void handleCreationEvent(Object event) { + // TODO: Implement creation-specific logic + logger.debug("Handling creation event"); + } + + private void handleUpdateEvent(Object event) { + // TODO: Implement update-specific logic + logger.debug("Handling update event"); + } + + private void handleDeletionEvent(Object event) { + // TODO: Implement deletion-specific logic + logger.debug("Handling deletion event"); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/AnswerEventHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/AnswerEventHandler.java new file mode 100644 index 000000000..264676f88 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/AnswerEventHandler.java @@ -0,0 +1,38 @@ +package com.generated.microservices.answers.microservices.answer.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.AnswerEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +public abstract class AnswerEventHandler extends EventHandler { +private AnswerRepository answerRepository; +protected AnswerEventProcessing answerEventProcessing; + +public AnswerEventHandler(AnswerRepository answerRepository, +AnswerEventProcessing answerEventProcessing) { +this.answerRepository = answerRepository; +this.answerEventProcessing = answerEventProcessing; +} + +public Set getAggregateIds() { + return + answerRepository.findAll().stream().map(Answer::getAggregateId).collect(Collectors.toSet()); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/CreatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/CreatedHandler.java new file mode 100644 index 000000000..7d0552ec7 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/CreatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.answer.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.AnswerEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class CreatedHandler extends AnswerEventHandler { + +public CreatedHandler(AnswerRepository answerRepository, +AnswerEventProcessing answerEventProcessing) { +super(answerRepository, answerEventProcessing); +} + +@EventListener +public void handleCreated(AnswerCreatedEvent event) { +try { +// Handle Created event for Answer +answerEventProcessing.processCreated(event); +} catch (Exception e) { +logger.error("Error handling AnswerCreatedEvent", e); +throw new EventProcessingException("Failed to handle AnswerCreatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/DeletedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/DeletedHandler.java new file mode 100644 index 000000000..161c0035c --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/DeletedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.answer.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.AnswerEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class DeletedHandler extends AnswerEventHandler { + +public DeletedHandler(AnswerRepository answerRepository, +AnswerEventProcessing answerEventProcessing) { +super(answerRepository, answerEventProcessing); +} + +@EventListener +public void handleDeleted(AnswerDeletedEvent event) { +try { +// Handle Deleted event for Answer +answerEventProcessing.processDeleted(event); +} catch (Exception e) { +logger.error("Error handling AnswerDeletedEvent", e); +throw new EventProcessingException("Failed to handle AnswerDeletedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/UpdatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/UpdatedHandler.java new file mode 100644 index 000000000..178fcd6d6 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/UpdatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.answer.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.AnswerEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class UpdatedHandler extends AnswerEventHandler { + +public UpdatedHandler(AnswerRepository answerRepository, +AnswerEventProcessing answerEventProcessing) { +super(answerRepository, answerEventProcessing); +} + +@EventListener +public void handleUpdated(AnswerUpdatedEvent event) { +try { +// Handle Updated event for Answer +answerEventProcessing.processUpdated(event); +} catch (Exception e) { +logger.error("Error handling AnswerUpdatedEvent", e); +throw new EventProcessingException("Failed to handle AnswerUpdatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerCreatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerCreatedEvent.java new file mode 100644 index 000000000..b09466757 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerCreatedEvent.java @@ -0,0 +1,115 @@ +package com.generated.microservices.answers.microservices.answer.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class AnswerCreatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final LocalDateTime answerDate; +private final LocalDateTime completedDate; +private final Boolean completed; +private final Object quizAnswerStudent; +private final Object quizAnswerCourseExecution; +private final Object questionAnswers; +private final Object answeredQuiz; + +public AnswerCreatedEvent(Object source, Long aggregateId, LocalDateTime answerDate, LocalDateTime completedDate, Boolean completed, Object quizAnswerStudent, Object quizAnswerCourseExecution, Object questionAnswers, Object answeredQuiz) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Created"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.answerDate = answerDate; +this.completedDate = completedDate; +this.completed = completed; +this.quizAnswerStudent = quizAnswerStudent; +this.quizAnswerCourseExecution = quizAnswerCourseExecution; +this.questionAnswers = questionAnswers; +this.answeredQuiz = answeredQuiz; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public LocalDateTime getAnswerDate() { +return answerDate; +} + +public LocalDateTime getCompletedDate() { +return completedDate; +} + +public Boolean getCompleted() { +return completed; +} + +public Object getQuizAnswerStudent() { +return quizAnswerStudent; +} + +public Object getQuizAnswerCourseExecution() { +return quizAnswerCourseExecution; +} + +public Object getQuestionAnswers() { +return questionAnswers; +} + +public Object getAnsweredQuiz() { +return answeredQuiz; +} + +@Override +public String toString() { +return "AnswerCreatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", answerDate=" + answerDate + +", completedDate=" + completedDate + +", completed=" + completed + +", quizAnswerStudent=" + quizAnswerStudent + +", quizAnswerCourseExecution=" + quizAnswerCourseExecution + +", questionAnswers=" + questionAnswers + +", answeredQuiz=" + answeredQuiz + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerDeletedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerDeletedEvent.java new file mode 100644 index 000000000..1ae9bdf5c --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerDeletedEvent.java @@ -0,0 +1,115 @@ +package com.generated.microservices.answers.microservices.answer.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class AnswerDeletedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final LocalDateTime answerDate; +private final LocalDateTime completedDate; +private final Boolean completed; +private final Object quizAnswerStudent; +private final Object quizAnswerCourseExecution; +private final Object questionAnswers; +private final Object answeredQuiz; + +public AnswerDeletedEvent(Object source, Long aggregateId, LocalDateTime answerDate, LocalDateTime completedDate, Boolean completed, Object quizAnswerStudent, Object quizAnswerCourseExecution, Object questionAnswers, Object answeredQuiz) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Deleted"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.answerDate = answerDate; +this.completedDate = completedDate; +this.completed = completed; +this.quizAnswerStudent = quizAnswerStudent; +this.quizAnswerCourseExecution = quizAnswerCourseExecution; +this.questionAnswers = questionAnswers; +this.answeredQuiz = answeredQuiz; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public LocalDateTime getAnswerDate() { +return answerDate; +} + +public LocalDateTime getCompletedDate() { +return completedDate; +} + +public Boolean getCompleted() { +return completed; +} + +public Object getQuizAnswerStudent() { +return quizAnswerStudent; +} + +public Object getQuizAnswerCourseExecution() { +return quizAnswerCourseExecution; +} + +public Object getQuestionAnswers() { +return questionAnswers; +} + +public Object getAnsweredQuiz() { +return answeredQuiz; +} + +@Override +public String toString() { +return "AnswerDeletedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", answerDate=" + answerDate + +", completedDate=" + completedDate + +", completed=" + completed + +", quizAnswerStudent=" + quizAnswerStudent + +", quizAnswerCourseExecution=" + quizAnswerCourseExecution + +", questionAnswers=" + questionAnswers + +", answeredQuiz=" + answeredQuiz + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerUpdatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerUpdatedEvent.java new file mode 100644 index 000000000..fb120eef0 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerUpdatedEvent.java @@ -0,0 +1,115 @@ +package com.generated.microservices.answers.microservices.answer.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class AnswerUpdatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final LocalDateTime answerDate; +private final LocalDateTime completedDate; +private final Boolean completed; +private final Object quizAnswerStudent; +private final Object quizAnswerCourseExecution; +private final Object questionAnswers; +private final Object answeredQuiz; + +public AnswerUpdatedEvent(Object source, Long aggregateId, LocalDateTime answerDate, LocalDateTime completedDate, Boolean completed, Object quizAnswerStudent, Object quizAnswerCourseExecution, Object questionAnswers, Object answeredQuiz) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Updated"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.answerDate = answerDate; +this.completedDate = completedDate; +this.completed = completed; +this.quizAnswerStudent = quizAnswerStudent; +this.quizAnswerCourseExecution = quizAnswerCourseExecution; +this.questionAnswers = questionAnswers; +this.answeredQuiz = answeredQuiz; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public LocalDateTime getAnswerDate() { +return answerDate; +} + +public LocalDateTime getCompletedDate() { +return completedDate; +} + +public Boolean getCompleted() { +return completed; +} + +public Object getQuizAnswerStudent() { +return quizAnswerStudent; +} + +public Object getQuizAnswerCourseExecution() { +return quizAnswerCourseExecution; +} + +public Object getQuestionAnswers() { +return questionAnswers; +} + +public Object getAnsweredQuiz() { +return answeredQuiz; +} + +@Override +public String toString() { +return "AnswerUpdatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", answerDate=" + answerDate + +", completedDate=" + completedDate + +", completed=" + completed + +", quizAnswerStudent=" + quizAnswerStudent + +", quizAnswerCourseExecution=" + quizAnswerCourseExecution + +", questionAnswers=" + questionAnswers + +", answeredQuiz=" + answeredQuiz + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/SubscribesCreated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/SubscribesCreated.java new file mode 100644 index 000000000..b63bb29f2 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/SubscribesCreated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.answer.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class CreatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(CreatedSubscription.class); + +private final AnswerService answerService; + +public CreatedSubscription(AnswerService answerService) { +this.answerService = answerService; +} + +@EventListener +@Async +public void handleCreated(AnswerCreatedEvent event) { +try { +logger.info("Handling Created event for Answer with ID: {}", event.getAggregateId()); + +// Process the Created event +processCreated(event); + +} catch (Exception e) { +logger.error("Error handling AnswerCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitCreated(AnswerCreatedEvent event) { +try { +logger.info("Post-commit handling Created event for Answer with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessCreated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of AnswerCreatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processCreated(AnswerCreatedEvent event) { +// TODO: Implement Created event processing logic +switch (event.getEventType()) { +case "Created": +handleCreatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessCreated(AnswerCreatedEvent event) { +// TODO: Implement post-transaction Created event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Created event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleCreatedLogic(AnswerCreatedEvent event) { +// TODO: Implement specific Created business logic +logger.debug("Processing Created logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/SubscribesDeleted.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/SubscribesDeleted.java new file mode 100644 index 000000000..dad89777b --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/SubscribesDeleted.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.answer.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class DeletedSubscription { +private static final Logger logger = LoggerFactory.getLogger(DeletedSubscription.class); + +private final AnswerService answerService; + +public DeletedSubscription(AnswerService answerService) { +this.answerService = answerService; +} + +@EventListener +@Async +public void handleDeleted(AnswerDeletedEvent event) { +try { +logger.info("Handling Deleted event for Answer with ID: {}", event.getAggregateId()); + +// Process the Deleted event +processDeleted(event); + +} catch (Exception e) { +logger.error("Error handling AnswerDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitDeleted(AnswerDeletedEvent event) { +try { +logger.info("Post-commit handling Deleted event for Answer with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessDeleted(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of AnswerDeletedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processDeleted(AnswerDeletedEvent event) { +// TODO: Implement Deleted event processing logic +switch (event.getEventType()) { +case "Deleted": +handleDeletedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessDeleted(AnswerDeletedEvent event) { +// TODO: Implement post-transaction Deleted event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Deleted event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleDeletedLogic(AnswerDeletedEvent event) { +// TODO: Implement specific Deleted business logic +logger.debug("Processing Deleted logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/SubscribesUpdated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/SubscribesUpdated.java new file mode 100644 index 000000000..2292f7c11 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/SubscribesUpdated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.answer.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.answer.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class UpdatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(UpdatedSubscription.class); + +private final AnswerService answerService; + +public UpdatedSubscription(AnswerService answerService) { +this.answerService = answerService; +} + +@EventListener +@Async +public void handleUpdated(AnswerUpdatedEvent event) { +try { +logger.info("Handling Updated event for Answer with ID: {}", event.getAggregateId()); + +// Process the Updated event +processUpdated(event); + +} catch (Exception e) { +logger.error("Error handling AnswerUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitUpdated(AnswerUpdatedEvent event) { +try { +logger.info("Post-commit handling Updated event for Answer with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessUpdated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of AnswerUpdatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processUpdated(AnswerUpdatedEvent event) { +// TODO: Implement Updated event processing logic +switch (event.getEventType()) { +case "Updated": +handleUpdatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessUpdated(AnswerUpdatedEvent event) { +// TODO: Implement post-transaction Updated event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Updated event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleUpdatedLogic(AnswerUpdatedEvent event) { +// TODO: Implement specific Updated business logic +logger.debug("Processing Updated logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/service/AnswerService.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/service/AnswerService.java new file mode 100644 index 000000000..cc686befb --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/service/AnswerService.java @@ -0,0 +1,68 @@ +package com.generated.microservices.answers.microservices.answer.service; + +import org.springframework.stereotype.Service; +import org.springframework.beans.factory.annotation.Autowired; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.repository.*; +import java.util.List; +import java.util.Optional; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +public class AnswerService { + +@Autowired +private AnswerRepository answerRepository; + +public QuizAnswer createQuizAnswer(QuizAnswerDto quizanswerDto) { +// TODO: Implement createQuizAnswer method +return null; // Placeholder +} + +public Optional findQuizAnswerById(Integer id) { +// TODO: Implement findQuizAnswerById method +return null; // Placeholder +} + +public QuizAnswer updateQuizAnswer(Integer id, QuizAnswerDto quizanswerDto) { +// TODO: Implement updateQuizAnswer method +return null; // Placeholder +} + +public void deleteQuizAnswer(Integer id) { +// TODO: Implement deleteQuizAnswer method +} + +public List findAllAnswers() { +// TODO: Implement findAllAnswers method +return null; // Placeholder +} + +public QuizAnswer createQuizAnswer(QuizAnswer answer, Object user) { +// TODO: Implement createQuizAnswer method +return null; // Placeholder +} + +public QuizAnswer findQuizAnswerById(Integer id) { +// TODO: Implement findQuizAnswerById method +return null; // Placeholder +} + +public QuizAnswer updateQuizAnswerState(Integer id, Object state) { +// TODO: Implement updateQuizAnswerState method +return null; // Placeholder +} + +public Object deleteQuizAnswer(Integer id) { +// TODO: Implement deleteQuizAnswer method +return null; // Placeholder +} + +public Object findQuizAnswersByStudent(Integer studentId) { +// TODO: Implement findQuizAnswersByStudent method +return null; // Placeholder +} + +// Additional CRUD utility methods can be added here +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/Course.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/Course.java new file mode 100644 index 000000000..06f9ce45b --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/Course.java @@ -0,0 +1,86 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +@Entity +public class Course extends Aggregate { + @Id + private String name; + private String acronym; + private String courseType; + private LocalDateTime creationDate; + + public Course(String name, String acronym, String courseType, LocalDateTime creationDate) { + this.name = name; + this.acronym = acronym; + this.courseType = courseType; + this.creationDate = creationDate; + } + + public Course(Course other) { + // Copy constructor + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAcronym() { + return acronym; + } + + public void setAcronym(String acronym) { + this.acronym = acronym; + } + + public String getCourseType() { + return courseType; + } + + public void setCourseType(String courseType) { + this.courseType = courseType; + } + + public LocalDateTime getCreationDate() { + return creationDate; + } + + public void setCreationDate(LocalDateTime creationDate) { + this.creationDate = creationDate; + } + public Object createCourse(String name, String acronym, String courseType, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getCourseById(Integer courseId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getAllCourses(UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object updateCourse(Integer courseId, String name, String acronym, String courseType, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object deleteCourse(Integer courseId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseCustomRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseCustomRepository.java new file mode 100644 index 000000000..7058a94c4 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseCustomRepository.java @@ -0,0 +1,9 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate; + +import java.util.Optional; +import java.util.List; +import java.util.Set; + +public interface CourseCustomRepository { + Optional findCourseIdByName(String courseName); +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseDto.java new file mode 100644 index 000000000..f24de6ff3 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseDto.java @@ -0,0 +1,85 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class CourseDto implements Serializable { + private Integer aggregateId; + private String name; + private String acronym; + private String courseType; + private LocalDateTime creationDate; + private Integer version; + private AggregateState state; + + public CourseDto() { + } + + public CourseDto(Course course) { + this.aggregateId = course.getAggregateId(); + this.name = course.getName(); + this.acronym = course.getAcronym(); + this.courseType = course.getCourseType(); + this.creationDate = course.getCreationDate(); + this.version = course.getVersion(); + this.state = course.getState(); + } + + public Integer getAggregateId() { + return aggregateId; + } + + public void setAggregateId(Integer aggregateId) { + this.aggregateId = aggregateId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAcronym() { + return acronym; + } + + public void setAcronym(String acronym) { + this.acronym = acronym; + } + + public String getCourseType() { + return courseType; + } + + public void setCourseType(String courseType) { + this.courseType = courseType; + } + + public LocalDateTime getCreationDate() { + return creationDate; + } + + public void setCreationDate(LocalDateTime creationDate) { + this.creationDate = creationDate; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public AggregateState getState() { + return state; + } + + public void setState(AggregateState state) { + this.state = state; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseFactory.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseFactory.java new file mode 100644 index 000000000..391f40e61 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseFactory.java @@ -0,0 +1,34 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate; + +import org.springframework.stereotype.Service; +import pt.ulisboa.tecnico.socialsoftware.ms.causal.aggregate.CausalAggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWorkService; +import pt.ulisboa.tecnico.socialsoftware.ms.sagas.aggregate.SagaAggregate; + +@Service +public class CourseFactory { + + public Course createCourse(Integer aggregateId, CourseDto courseDto) { + // Factory method implementation - create root entity directly + // Extract properties from DTO and create the root entity + return new Course( + courseDto.getName(), + courseDto.getAcronym(), + courseDto.getCourseType(), + courseDto.getCreationDate() + ); + } + + public Course createCourseFromExisting(Course existingCourse) { + // Create a copy of the existing aggregate + if (existingCourse instanceof Course) { + return new Course((Course) existingCourse); + } + throw new IllegalArgumentException("Unknown aggregate type"); + } + + public CourseDto createCourseDto(Course course) { + return new CourseDto((Course) course); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseRepository.java new file mode 100644 index 000000000..223027afb --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseRepository.java @@ -0,0 +1,22 @@ +package com.generated.microservices.answers.microservices.course.aggregate; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import jakarta.transaction.Transactional; + +@Repository +@Transactional +public interface CourseRepository extends JpaRepository { + @Query(value = "select course.id from Course course where course.name = :name AND course.state = 'ACTIVE' AND course.sagaState = 'NOT_IN_SAGA'") + Optional findCourseIdByNameForSaga(String name); + + @Query(value = "select course.id from Course course where course.acronym = :acronym AND course.state = 'ACTIVE' AND course.sagaState = 'NOT_IN_SAGA'") + Optional findCourseIdByAcronymForSaga(String acronym); + + @Query(value = "select course.id from Course course where course.courseType = :courseType AND course.state = 'ACTIVE' AND course.sagaState = 'NOT_IN_SAGA'") + Optional findCourseIdByCourseTypeForSaga(String courseType); + + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/CourseEventHandling.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/CourseEventHandling.java new file mode 100644 index 000000000..190d18022 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/CourseEventHandling.java @@ -0,0 +1,192 @@ +package com.generated.microservices.answers.microservices.course.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.course.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + + +@Component +public class CourseEventHandling { +private static final Logger logger = LoggerFactory.getLogger(CourseEventHandling.class); + +private final CourseService courseService; +private final CourseRepository courseRepository; + +public CourseEventHandling(CourseService courseService, CourseRepository +courseRepository) { +this.courseService = courseService; +this.courseRepository = courseRepository; +} + +@EventListener +public void handleCreated(CourseCreatedEvent event) { +try { +logger.info("Processing Created event for Course with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Created": +processCreatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing CourseCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process CourseCreatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncCreated(CourseCreatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Created event for Course with ID: {}", event.getAggregateId()); + processAsyncCreatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of CourseCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleUpdated(CourseUpdatedEvent event) { +try { +logger.info("Processing Updated event for Course with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Updated": +processUpdatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing CourseUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process CourseUpdatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncUpdated(CourseUpdatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Updated event for Course with ID: {}", event.getAggregateId()); + processAsyncUpdatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of CourseUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleDeleted(CourseDeletedEvent event) { +try { +logger.info("Processing Deleted event for Course with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Deleted": +processDeletedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing CourseDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process CourseDeletedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncDeleted(CourseDeletedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Deleted event for Course with ID: {}", event.getAggregateId()); + processAsyncDeletedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of CourseDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + + + // Event processing methods + private void processCreatedEvent(CourseCreatedEvent event) { + // TODO: Implement Created event processing logic + logger.debug("Processing Created event for Course ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle creation event - might trigger welcome workflows, notifications, etc. + handleCreationEvent(event); + } + + private void processAsyncCreatedEvent(CourseCreatedEvent event) { + // TODO: Implement async Created event processing logic + logger.debug("Async processing Created event for Course ID: {}", event.getAggregateId()); + } + + private void processUpdatedEvent(CourseUpdatedEvent event) { + // TODO: Implement Updated event processing logic + logger.debug("Processing Updated event for Course ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle update event - might trigger validation, sync with external systems, etc. + handleUpdateEvent(event); + } + + private void processAsyncUpdatedEvent(CourseUpdatedEvent event) { + // TODO: Implement async Updated event processing logic + logger.debug("Async processing Updated event for Course ID: {}", event.getAggregateId()); + } + + private void processDeletedEvent(CourseDeletedEvent event) { + // TODO: Implement Deleted event processing logic + logger.debug("Processing Deleted event for Course ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle deletion event - might trigger cleanup, archival, notifications, etc. + handleDeletionEvent(event); + } + + private void processAsyncDeletedEvent(CourseDeletedEvent event) { + // TODO: Implement async Deleted event processing logic + logger.debug("Async processing Deleted event for Course ID: {}", event.getAggregateId()); + } + + + // Helper methods for specific event types + private void handleCreationEvent(Object event) { + // TODO: Implement creation-specific logic + logger.debug("Handling creation event"); + } + + private void handleUpdateEvent(Object event) { + // TODO: Implement update-specific logic + logger.debug("Handling update event"); + } + + private void handleDeletionEvent(Object event) { + // TODO: Implement deletion-specific logic + logger.debug("Handling deletion event"); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/CourseEventHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/CourseEventHandler.java new file mode 100644 index 000000000..68205215d --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/CourseEventHandler.java @@ -0,0 +1,38 @@ +package com.generated.microservices.answers.microservices.course.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.course.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.CourseEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +public abstract class CourseEventHandler extends EventHandler { +private CourseRepository courseRepository; +protected CourseEventProcessing courseEventProcessing; + +public CourseEventHandler(CourseRepository courseRepository, +CourseEventProcessing courseEventProcessing) { +this.courseRepository = courseRepository; +this.courseEventProcessing = courseEventProcessing; +} + +public Set getAggregateIds() { + return + courseRepository.findAll().stream().map(Course::getAggregateId).collect(Collectors.toSet()); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/CreatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/CreatedHandler.java new file mode 100644 index 000000000..44d44231b --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/CreatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.course.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.course.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.CourseEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class CreatedHandler extends CourseEventHandler { + +public CreatedHandler(CourseRepository courseRepository, +CourseEventProcessing courseEventProcessing) { +super(courseRepository, courseEventProcessing); +} + +@EventListener +public void handleCreated(CourseCreatedEvent event) { +try { +// Handle Created event for Course +courseEventProcessing.processCreated(event); +} catch (Exception e) { +logger.error("Error handling CourseCreatedEvent", e); +throw new EventProcessingException("Failed to handle CourseCreatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/DeletedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/DeletedHandler.java new file mode 100644 index 000000000..c65048bbd --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/DeletedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.course.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.course.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.CourseEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class DeletedHandler extends CourseEventHandler { + +public DeletedHandler(CourseRepository courseRepository, +CourseEventProcessing courseEventProcessing) { +super(courseRepository, courseEventProcessing); +} + +@EventListener +public void handleDeleted(CourseDeletedEvent event) { +try { +// Handle Deleted event for Course +courseEventProcessing.processDeleted(event); +} catch (Exception e) { +logger.error("Error handling CourseDeletedEvent", e); +throw new EventProcessingException("Failed to handle CourseDeletedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/UpdatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/UpdatedHandler.java new file mode 100644 index 000000000..5e0ab66a0 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/UpdatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.course.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.course.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.CourseEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class UpdatedHandler extends CourseEventHandler { + +public UpdatedHandler(CourseRepository courseRepository, +CourseEventProcessing courseEventProcessing) { +super(courseRepository, courseEventProcessing); +} + +@EventListener +public void handleUpdated(CourseUpdatedEvent event) { +try { +// Handle Updated event for Course +courseEventProcessing.processUpdated(event); +} catch (Exception e) { +logger.error("Error handling CourseUpdatedEvent", e); +throw new EventProcessingException("Failed to handle CourseUpdatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseCreatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseCreatedEvent.java new file mode 100644 index 000000000..eb2ea2173 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseCreatedEvent.java @@ -0,0 +1,94 @@ +package com.generated.microservices.answers.microservices.course.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.course.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class CourseCreatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String name; +private final String acronym; +private final String courseType; +private final LocalDateTime creationDate; + +public CourseCreatedEvent(Object source, Long aggregateId, String name, String acronym, String courseType, LocalDateTime creationDate) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Created"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.name = name; +this.acronym = acronym; +this.courseType = courseType; +this.creationDate = creationDate; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getName() { +return name; +} + +public String getAcronym() { +return acronym; +} + +public String getCourseType() { +return courseType; +} + +public LocalDateTime getCreationDate() { +return creationDate; +} + +@Override +public String toString() { +return "CourseCreatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", name=" + name + +", acronym=" + acronym + +", courseType=" + courseType + +", creationDate=" + creationDate + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseDeletedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseDeletedEvent.java new file mode 100644 index 000000000..8c6462083 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseDeletedEvent.java @@ -0,0 +1,94 @@ +package com.generated.microservices.answers.microservices.course.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.course.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class CourseDeletedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String name; +private final String acronym; +private final String courseType; +private final LocalDateTime creationDate; + +public CourseDeletedEvent(Object source, Long aggregateId, String name, String acronym, String courseType, LocalDateTime creationDate) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Deleted"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.name = name; +this.acronym = acronym; +this.courseType = courseType; +this.creationDate = creationDate; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getName() { +return name; +} + +public String getAcronym() { +return acronym; +} + +public String getCourseType() { +return courseType; +} + +public LocalDateTime getCreationDate() { +return creationDate; +} + +@Override +public String toString() { +return "CourseDeletedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", name=" + name + +", acronym=" + acronym + +", courseType=" + courseType + +", creationDate=" + creationDate + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseUpdatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseUpdatedEvent.java new file mode 100644 index 000000000..6321a6b98 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseUpdatedEvent.java @@ -0,0 +1,94 @@ +package com.generated.microservices.answers.microservices.course.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.course.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class CourseUpdatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String name; +private final String acronym; +private final String courseType; +private final LocalDateTime creationDate; + +public CourseUpdatedEvent(Object source, Long aggregateId, String name, String acronym, String courseType, LocalDateTime creationDate) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Updated"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.name = name; +this.acronym = acronym; +this.courseType = courseType; +this.creationDate = creationDate; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getName() { +return name; +} + +public String getAcronym() { +return acronym; +} + +public String getCourseType() { +return courseType; +} + +public LocalDateTime getCreationDate() { +return creationDate; +} + +@Override +public String toString() { +return "CourseUpdatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", name=" + name + +", acronym=" + acronym + +", courseType=" + courseType + +", creationDate=" + creationDate + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/SubscribesCreated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/SubscribesCreated.java new file mode 100644 index 000000000..bfd2ee3ec --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/SubscribesCreated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.course.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.course.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class CreatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(CreatedSubscription.class); + +private final CourseService courseService; + +public CreatedSubscription(CourseService courseService) { +this.courseService = courseService; +} + +@EventListener +@Async +public void handleCreated(CourseCreatedEvent event) { +try { +logger.info("Handling Created event for Course with ID: {}", event.getAggregateId()); + +// Process the Created event +processCreated(event); + +} catch (Exception e) { +logger.error("Error handling CourseCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitCreated(CourseCreatedEvent event) { +try { +logger.info("Post-commit handling Created event for Course with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessCreated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of CourseCreatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processCreated(CourseCreatedEvent event) { +// TODO: Implement Created event processing logic +switch (event.getEventType()) { +case "Created": +handleCreatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessCreated(CourseCreatedEvent event) { +// TODO: Implement post-transaction Created event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Created event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleCreatedLogic(CourseCreatedEvent event) { +// TODO: Implement specific Created business logic +logger.debug("Processing Created logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/SubscribesDeleted.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/SubscribesDeleted.java new file mode 100644 index 000000000..20143cf9e --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/SubscribesDeleted.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.course.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.course.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class DeletedSubscription { +private static final Logger logger = LoggerFactory.getLogger(DeletedSubscription.class); + +private final CourseService courseService; + +public DeletedSubscription(CourseService courseService) { +this.courseService = courseService; +} + +@EventListener +@Async +public void handleDeleted(CourseDeletedEvent event) { +try { +logger.info("Handling Deleted event for Course with ID: {}", event.getAggregateId()); + +// Process the Deleted event +processDeleted(event); + +} catch (Exception e) { +logger.error("Error handling CourseDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitDeleted(CourseDeletedEvent event) { +try { +logger.info("Post-commit handling Deleted event for Course with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessDeleted(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of CourseDeletedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processDeleted(CourseDeletedEvent event) { +// TODO: Implement Deleted event processing logic +switch (event.getEventType()) { +case "Deleted": +handleDeletedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessDeleted(CourseDeletedEvent event) { +// TODO: Implement post-transaction Deleted event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Deleted event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleDeletedLogic(CourseDeletedEvent event) { +// TODO: Implement specific Deleted business logic +logger.debug("Processing Deleted logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/SubscribesUpdated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/SubscribesUpdated.java new file mode 100644 index 000000000..b036e565d --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/SubscribesUpdated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.course.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.course.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class UpdatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(UpdatedSubscription.class); + +private final CourseService courseService; + +public UpdatedSubscription(CourseService courseService) { +this.courseService = courseService; +} + +@EventListener +@Async +public void handleUpdated(CourseUpdatedEvent event) { +try { +logger.info("Handling Updated event for Course with ID: {}", event.getAggregateId()); + +// Process the Updated event +processUpdated(event); + +} catch (Exception e) { +logger.error("Error handling CourseUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitUpdated(CourseUpdatedEvent event) { +try { +logger.info("Post-commit handling Updated event for Course with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessUpdated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of CourseUpdatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processUpdated(CourseUpdatedEvent event) { +// TODO: Implement Updated event processing logic +switch (event.getEventType()) { +case "Updated": +handleUpdatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessUpdated(CourseUpdatedEvent event) { +// TODO: Implement post-transaction Updated event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Updated event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleUpdatedLogic(CourseUpdatedEvent event) { +// TODO: Implement specific Updated business logic +logger.debug("Processing Updated logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/service/CourseService.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/service/CourseService.java new file mode 100644 index 000000000..c834991d4 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/service/CourseService.java @@ -0,0 +1,174 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.course.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.exception.*; + +import java.util.List; +import java.util.stream.Collectors; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.UserDto; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.AnswersException; + + +@Service +@Transactional +public class CourseService { + private static final Logger logger = LoggerFactory.getLogger(CourseService.class); + + @Autowired + private CourseRepository courseRepository; + + @Autowired + private CourseFactory courseFactory; + + public CourseService() {} + + // CRUD Operations + public CourseDto createCourse(String name, String acronym, String courseType, LocalDateTime creationDate) { + try { + Course course = new Course(name, acronym, courseType, creationDate); + course = courseRepository.save(course); + return new CourseDto(course); + } catch (Exception e) { + throw new AnswersException("Error creating course: " + e.getMessage()); + } + } + + public CourseDto getCourseById(Integer id) { + try { + Course course = (Course) courseRepository.findById(id) + .orElseThrow(() -> new AnswersException("Course not found with id: " + id)); + return new CourseDto(course); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error retrieving course: " + e.getMessage()); + } + } + + public List getAllCourses() { + try { + return courseRepository.findAll().stream() + .map(entity -> new CourseDto((Course) entity)) + .collect(Collectors.toList()); + } catch (Exception e) { + throw new AnswersException("Error retrieving all courses: " + e.getMessage()); + } + } + + public CourseDto updateCourse(Integer id, CourseDto courseDto) { + try { + Course course = (Course) courseRepository.findById(id) + .orElseThrow(() -> new AnswersException("Course not found with id: " + id)); + + if (courseDto.getName() != null) { + course.setName(courseDto.getName()); + } + if (courseDto.getAcronym() != null) { + course.setAcronym(courseDto.getAcronym()); + } + if (courseDto.getCourseType() != null) { + course.setCourseType(courseDto.getCourseType()); + } + if (courseDto.getCreationDate() != null) { + course.setCreationDate(courseDto.getCreationDate()); + } + + course = courseRepository.save(course); + return new CourseDto(course); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error updating course: " + e.getMessage()); + } + } + + public void deleteCourse(Integer id) { + try { + if (!courseRepository.existsById(id)) { + throw new AnswersException("Course not found with id: " + id); + } + courseRepository.deleteById(id); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error deleting course: " + e.getMessage()); + } + } + + // Business Methods + @Transactional + public Object searchCoursesByName(Integer id, String name, UnitOfWork unitOfWork) { + try { + Course course = courseRepository.findById(id) + .orElseThrow(() -> new AnswersException("Course not found with id: " + id)); + + // Business logic for searchCoursesByName + Object result = course.searchCoursesByName(); + courseRepository.save(course); + return result; + } catch (Exception e) { + throw new AnswersException("Error in searchCoursesByName: " + e.getMessage()); + } + } + + @Transactional + public Object searchCoursesByAcronym(Integer id, String acronym, UnitOfWork unitOfWork) { + try { + Course course = courseRepository.findById(id) + .orElseThrow(() -> new AnswersException("Course not found with id: " + id)); + + // Business logic for searchCoursesByAcronym + Object result = course.searchCoursesByAcronym(); + courseRepository.save(course); + return result; + } catch (Exception e) { + throw new AnswersException("Error in searchCoursesByAcronym: " + e.getMessage()); + } + } + + // No custom workflows defined + + // Query methods disabled - repository methods not implemented + + // Event Processing Methods + private void publishCourseCreatedEvent(Course course) { + try { + // TODO: Implement event publishing for CourseCreated + // eventPublisher.publishEvent(new CourseCreatedEvent(course)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish CourseCreatedEvent", e); + } + } + + private void publishCourseUpdatedEvent(Course course) { + try { + // TODO: Implement event publishing for CourseUpdated + // eventPublisher.publishEvent(new CourseUpdatedEvent(course)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish CourseUpdatedEvent", e); + } + } + + private void publishCourseDeletedEvent(Long courseId) { + try { + // TODO: Implement event publishing for CourseDeleted + // eventPublisher.publishEvent(new CourseDeletedEvent(courseId)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish CourseDeletedEvent", e); + } + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecution.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecution.java new file mode 100644 index 000000000..0a75ad69b --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecution.java @@ -0,0 +1,131 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +@Entity +public class CourseExecution extends Aggregate { + @Id + private String name; + private String acronym; + private String academicTerm; + private LocalDateTime startDate; + private LocalDateTime endDate; + private Object course; + private Object students; + + public CourseExecution(String name, String acronym, String academicTerm, LocalDateTime startDate, LocalDateTime endDate, Object course, Object students) { + this.name = name; + this.acronym = acronym; + this.academicTerm = academicTerm; + this.startDate = startDate; + this.endDate = endDate; + this.course = course; + this.students = students; + } + + public CourseExecution(CourseExecution other) { + // Copy constructor + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAcronym() { + return acronym; + } + + public void setAcronym(String acronym) { + this.acronym = acronym; + } + + public String getAcademicTerm() { + return academicTerm; + } + + public void setAcademicTerm(String academicTerm) { + this.academicTerm = academicTerm; + } + + public LocalDateTime getStartDate() { + return startDate; + } + + public void setStartDate(LocalDateTime startDate) { + this.startDate = startDate; + } + + public LocalDateTime getEndDate() { + return endDate; + } + + public void setEndDate(LocalDateTime endDate) { + this.endDate = endDate; + } + + public Object getCourse() { + return course; + } + + public void setCourse(Object course) { + this.course = course; + } + + public Object getStudents() { + return students; + } + + public void setStudents(Object students) { + this.students = students; + } + public Object createCourseExecution(String name, String acronym, String academicTerm, LocalDateTime startDate, LocalDateTime endDate, Object course, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getCourseExecutionById(Integer courseExecutionId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getAllCourseExecutions(UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getCourseExecutionsByCourse(Integer courseId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object enrollStudent(Integer courseExecutionId, Integer studentId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object unenrollStudent(Integer courseExecutionId, Integer studentId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object updateCourseExecution(Integer courseExecutionId, String name, String acronym, String academicTerm, LocalDateTime startDate, LocalDateTime endDate, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object deleteCourseExecution(Integer courseExecutionId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCourse.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCourse.java new file mode 100644 index 000000000..b87547992 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCourse.java @@ -0,0 +1,59 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class CourseExecutionCourse { + private Integer courseAggregateId; + private String courseName; + private String courseAcronym; + private String courseType; + + public CourseExecutionCourse(Integer courseAggregateId, String courseName, String courseAcronym, String courseType) { + this.courseAggregateId = courseAggregateId; + this.courseName = courseName; + this.courseAcronym = courseAcronym; + this.courseType = courseType; + } + + public CourseExecutionCourse(CourseExecutionCourse other) { + // Copy constructor + } + + + public Integer getCourseAggregateId() { + return courseAggregateId; + } + + public void setCourseAggregateId(Integer courseAggregateId) { + this.courseAggregateId = courseAggregateId; + } + + public String getCourseName() { + return courseName; + } + + public void setCourseName(String courseName) { + this.courseName = courseName; + } + + public String getCourseAcronym() { + return courseAcronym; + } + + public void setCourseAcronym(String courseAcronym) { + this.courseAcronym = courseAcronym; + } + + public String getCourseType() { + return courseType; + } + + public void setCourseType(String courseType) { + this.courseType = courseType; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCourseDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCourseDto.java new file mode 100644 index 000000000..66ecaedf0 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCourseDto.java @@ -0,0 +1,55 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class CourseExecutionCourseDto implements Serializable { + private Integer courseAggregateId; + private String courseName; + private String courseAcronym; + private String courseType; + + public CourseExecutionCourseDto() { + } + + public CourseExecutionCourseDto(CourseExecutionCourse courseexecutioncourse) { + this.courseAggregateId = courseexecutioncourse.getCourseAggregateId(); + this.courseName = courseexecutioncourse.getCourseName(); + this.courseAcronym = courseexecutioncourse.getCourseAcronym(); + this.courseType = courseexecutioncourse.getCourseType(); + } + + public Integer getCourseAggregateId() { + return courseAggregateId; + } + + public void setCourseAggregateId(Integer courseAggregateId) { + this.courseAggregateId = courseAggregateId; + } + + public String getCourseName() { + return courseName; + } + + public void setCourseName(String courseName) { + this.courseName = courseName; + } + + public String getCourseAcronym() { + return courseAcronym; + } + + public void setCourseAcronym(String courseAcronym) { + this.courseAcronym = courseAcronym; + } + + public String getCourseType() { + return courseType; + } + + public void setCourseType(String courseType) { + this.courseType = courseType; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCustomRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCustomRepository.java new file mode 100644 index 000000000..a2a6610b6 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionCustomRepository.java @@ -0,0 +1,9 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate; + +import java.util.Optional; +import java.util.List; +import java.util.Set; + +public interface CourseExecutionCustomRepository { + Optional findCourseExecutionIdByCourseIdAndAcademicTerm(Integer courseId, String academicTerm); +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionDto.java new file mode 100644 index 000000000..6bc25ffe4 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionDto.java @@ -0,0 +1,116 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Set; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class CourseExecutionDto implements Serializable { + private Integer aggregateId; + private String name; + private String acronym; + private String academicTerm; + private LocalDateTime startDate; + private LocalDateTime endDate; + private Object course; + private Object students; + private Integer version; + private AggregateState state; + + public CourseExecutionDto() { + } + + public CourseExecutionDto(CourseExecution courseexecution) { + this.aggregateId = courseexecution.getAggregateId(); + this.name = courseexecution.getName(); + this.acronym = courseexecution.getAcronym(); + this.academicTerm = courseexecution.getAcademicTerm(); + this.startDate = courseexecution.getStartDate(); + this.endDate = courseexecution.getEndDate(); + this.course = courseexecution.getCourse(); + this.students = courseexecution.getStudents(); + this.version = courseexecution.getVersion(); + this.state = courseexecution.getState(); + } + + public Integer getAggregateId() { + return aggregateId; + } + + public void setAggregateId(Integer aggregateId) { + this.aggregateId = aggregateId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAcronym() { + return acronym; + } + + public void setAcronym(String acronym) { + this.acronym = acronym; + } + + public String getAcademicTerm() { + return academicTerm; + } + + public void setAcademicTerm(String academicTerm) { + this.academicTerm = academicTerm; + } + + public LocalDateTime getStartDate() { + return startDate; + } + + public void setStartDate(LocalDateTime startDate) { + this.startDate = startDate; + } + + public LocalDateTime getEndDate() { + return endDate; + } + + public void setEndDate(LocalDateTime endDate) { + this.endDate = endDate; + } + + public Object getCourse() { + return course; + } + + public void setCourse(Object course) { + this.course = course; + } + + public Object getStudents() { + return students; + } + + public void setStudents(Object students) { + this.students = students; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public AggregateState getState() { + return state; + } + + public void setState(AggregateState state) { + this.state = state; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionFactory.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionFactory.java new file mode 100644 index 000000000..f8e96571e --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionFactory.java @@ -0,0 +1,37 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate; + +import org.springframework.stereotype.Service; +import pt.ulisboa.tecnico.socialsoftware.ms.causal.aggregate.CausalAggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWorkService; +import pt.ulisboa.tecnico.socialsoftware.ms.sagas.aggregate.SagaAggregate; + +@Service +public class CourseExecutionFactory { + + public CourseExecution createCourseExecution(Integer aggregateId, CourseExecutionDto courseexecutionDto) { + // Factory method implementation - create root entity directly + // Extract properties from DTO and create the root entity + return new CourseExecution( + courseexecutionDto.getName(), + courseexecutionDto.getAcronym(), + courseexecutionDto.getAcademicTerm(), + courseexecutionDto.getStartDate(), + courseexecutionDto.getEndDate(), + courseexecutionDto.getCourse(), + courseexecutionDto.getStudents() + ); + } + + public CourseExecution createCourseExecutionFromExisting(CourseExecution existingCourseExecution) { + // Create a copy of the existing aggregate + if (existingCourseExecution instanceof CourseExecution) { + return new CourseExecution((CourseExecution) existingCourseExecution); + } + throw new IllegalArgumentException("Unknown aggregate type"); + } + + public CourseExecutionDto createCourseExecutionDto(CourseExecution courseexecution) { + return new CourseExecutionDto((CourseExecution) courseexecution); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionRepository.java new file mode 100644 index 000000000..27b89abd7 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionRepository.java @@ -0,0 +1,22 @@ +package com.generated.microservices.answers.microservices.courseexecution.aggregate; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import jakarta.transaction.Transactional; + +@Repository +@Transactional +public interface CourseExecutionRepository extends JpaRepository { + @Query(value = "select courseexecution.id from CourseExecution courseexecution where courseexecution.name = :name AND courseexecution.state = 'ACTIVE' AND courseexecution.sagaState = 'NOT_IN_SAGA'") + Optional findCourseExecutionIdByNameForSaga(String name); + + @Query(value = "select courseexecution.id from CourseExecution courseexecution where courseexecution.acronym = :acronym AND courseexecution.state = 'ACTIVE' AND courseexecution.sagaState = 'NOT_IN_SAGA'") + Optional findCourseExecutionIdByAcronymForSaga(String acronym); + + @Query(value = "select courseexecution.id from CourseExecution courseexecution where courseexecution.academicTerm = :academicTerm AND courseexecution.state = 'ACTIVE' AND courseexecution.sagaState = 'NOT_IN_SAGA'") + Optional findCourseExecutionIdByAcademicTermForSaga(String academicTerm); + + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionStudent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionStudent.java new file mode 100644 index 000000000..a37fbc507 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionStudent.java @@ -0,0 +1,70 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +@Embeddable +public class CourseExecutionStudent { + private Integer studentAggregateId; + private String studentName; + private String studentUsername; + private String studentEmail; + private LocalDateTime enrollmentDate; + + public CourseExecutionStudent(Integer studentAggregateId, String studentName, String studentUsername, String studentEmail, LocalDateTime enrollmentDate) { + this.studentAggregateId = studentAggregateId; + this.studentName = studentName; + this.studentUsername = studentUsername; + this.studentEmail = studentEmail; + this.enrollmentDate = enrollmentDate; + } + + public CourseExecutionStudent(CourseExecutionStudent other) { + // Copy constructor + } + + + public Integer getStudentAggregateId() { + return studentAggregateId; + } + + public void setStudentAggregateId(Integer studentAggregateId) { + this.studentAggregateId = studentAggregateId; + } + + public String getStudentName() { + return studentName; + } + + public void setStudentName(String studentName) { + this.studentName = studentName; + } + + public String getStudentUsername() { + return studentUsername; + } + + public void setStudentUsername(String studentUsername) { + this.studentUsername = studentUsername; + } + + public String getStudentEmail() { + return studentEmail; + } + + public void setStudentEmail(String studentEmail) { + this.studentEmail = studentEmail; + } + + public LocalDateTime getEnrollmentDate() { + return enrollmentDate; + } + + public void setEnrollmentDate(LocalDateTime enrollmentDate) { + this.enrollmentDate = enrollmentDate; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionStudentDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionStudentDto.java new file mode 100644 index 000000000..46c6cebbc --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionStudentDto.java @@ -0,0 +1,66 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class CourseExecutionStudentDto implements Serializable { + private Integer studentAggregateId; + private String studentName; + private String studentUsername; + private String studentEmail; + private LocalDateTime enrollmentDate; + + public CourseExecutionStudentDto() { + } + + public CourseExecutionStudentDto(CourseExecutionStudent courseexecutionstudent) { + this.studentAggregateId = courseexecutionstudent.getStudentAggregateId(); + this.studentName = courseexecutionstudent.getStudentName(); + this.studentUsername = courseexecutionstudent.getStudentUsername(); + this.studentEmail = courseexecutionstudent.getStudentEmail(); + this.enrollmentDate = courseexecutionstudent.getEnrollmentDate(); + } + + public Integer getStudentAggregateId() { + return studentAggregateId; + } + + public void setStudentAggregateId(Integer studentAggregateId) { + this.studentAggregateId = studentAggregateId; + } + + public String getStudentName() { + return studentName; + } + + public void setStudentName(String studentName) { + this.studentName = studentName; + } + + public String getStudentUsername() { + return studentUsername; + } + + public void setStudentUsername(String studentUsername) { + this.studentUsername = studentUsername; + } + + public String getStudentEmail() { + return studentEmail; + } + + public void setStudentEmail(String studentEmail) { + this.studentEmail = studentEmail; + } + + public LocalDateTime getEnrollmentDate() { + return enrollmentDate; + } + + public void setEnrollmentDate(LocalDateTime enrollmentDate) { + this.enrollmentDate = enrollmentDate; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/CourseExecutionEventHandling.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/CourseExecutionEventHandling.java new file mode 100644 index 000000000..a7fb5d4d1 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/CourseExecutionEventHandling.java @@ -0,0 +1,192 @@ +package com.generated.microservices.answers.microservices.courseexecution.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + + +@Component +public class CourseExecutionEventHandling { +private static final Logger logger = LoggerFactory.getLogger(CourseExecutionEventHandling.class); + +private final CourseExecutionService courseexecutionService; +private final CourseExecutionRepository courseexecutionRepository; + +public CourseExecutionEventHandling(CourseExecutionService courseexecutionService, CourseExecutionRepository +courseexecutionRepository) { +this.courseexecutionService = courseexecutionService; +this.courseexecutionRepository = courseexecutionRepository; +} + +@EventListener +public void handleCreated(CourseExecutionCreatedEvent event) { +try { +logger.info("Processing Created event for CourseExecution with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Created": +processCreatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing CourseExecutionCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process CourseExecutionCreatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncCreated(CourseExecutionCreatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Created event for CourseExecution with ID: {}", event.getAggregateId()); + processAsyncCreatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of CourseExecutionCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleUpdated(CourseExecutionUpdatedEvent event) { +try { +logger.info("Processing Updated event for CourseExecution with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Updated": +processUpdatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing CourseExecutionUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process CourseExecutionUpdatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncUpdated(CourseExecutionUpdatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Updated event for CourseExecution with ID: {}", event.getAggregateId()); + processAsyncUpdatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of CourseExecutionUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleDeleted(CourseExecutionDeletedEvent event) { +try { +logger.info("Processing Deleted event for CourseExecution with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Deleted": +processDeletedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing CourseExecutionDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process CourseExecutionDeletedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncDeleted(CourseExecutionDeletedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Deleted event for CourseExecution with ID: {}", event.getAggregateId()); + processAsyncDeletedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of CourseExecutionDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + + + // Event processing methods + private void processCreatedEvent(CourseExecutionCreatedEvent event) { + // TODO: Implement Created event processing logic + logger.debug("Processing Created event for CourseExecution ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle creation event - might trigger welcome workflows, notifications, etc. + handleCreationEvent(event); + } + + private void processAsyncCreatedEvent(CourseExecutionCreatedEvent event) { + // TODO: Implement async Created event processing logic + logger.debug("Async processing Created event for CourseExecution ID: {}", event.getAggregateId()); + } + + private void processUpdatedEvent(CourseExecutionUpdatedEvent event) { + // TODO: Implement Updated event processing logic + logger.debug("Processing Updated event for CourseExecution ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle update event - might trigger validation, sync with external systems, etc. + handleUpdateEvent(event); + } + + private void processAsyncUpdatedEvent(CourseExecutionUpdatedEvent event) { + // TODO: Implement async Updated event processing logic + logger.debug("Async processing Updated event for CourseExecution ID: {}", event.getAggregateId()); + } + + private void processDeletedEvent(CourseExecutionDeletedEvent event) { + // TODO: Implement Deleted event processing logic + logger.debug("Processing Deleted event for CourseExecution ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle deletion event - might trigger cleanup, archival, notifications, etc. + handleDeletionEvent(event); + } + + private void processAsyncDeletedEvent(CourseExecutionDeletedEvent event) { + // TODO: Implement async Deleted event processing logic + logger.debug("Async processing Deleted event for CourseExecution ID: {}", event.getAggregateId()); + } + + + // Helper methods for specific event types + private void handleCreationEvent(Object event) { + // TODO: Implement creation-specific logic + logger.debug("Handling creation event"); + } + + private void handleUpdateEvent(Object event) { + // TODO: Implement update-specific logic + logger.debug("Handling update event"); + } + + private void handleDeletionEvent(Object event) { + // TODO: Implement deletion-specific logic + logger.debug("Handling deletion event"); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/CourseExecutionEventHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/CourseExecutionEventHandler.java new file mode 100644 index 000000000..4f384033e --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/CourseExecutionEventHandler.java @@ -0,0 +1,38 @@ +package com.generated.microservices.answers.microservices.courseexecution.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.CourseExecutionEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +public abstract class CourseExecutionEventHandler extends EventHandler { +private CourseExecutionRepository courseexecutionRepository; +protected CourseExecutionEventProcessing courseexecutionEventProcessing; + +public CourseExecutionEventHandler(CourseExecutionRepository courseexecutionRepository, +CourseExecutionEventProcessing courseexecutionEventProcessing) { +this.courseexecutionRepository = courseexecutionRepository; +this.courseexecutionEventProcessing = courseexecutionEventProcessing; +} + +public Set getAggregateIds() { + return + courseexecutionRepository.findAll().stream().map(CourseExecution::getAggregateId).collect(Collectors.toSet()); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/CreatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/CreatedHandler.java new file mode 100644 index 000000000..0d9bfb815 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/CreatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.courseexecution.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.CourseExecutionEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class CreatedHandler extends CourseExecutionEventHandler { + +public CreatedHandler(CourseExecutionRepository courseexecutionRepository, +CourseExecutionEventProcessing courseexecutionEventProcessing) { +super(courseexecutionRepository, courseexecutionEventProcessing); +} + +@EventListener +public void handleCreated(CourseExecutionCreatedEvent event) { +try { +// Handle Created event for CourseExecution +courseexecutionEventProcessing.processCreated(event); +} catch (Exception e) { +logger.error("Error handling CourseExecutionCreatedEvent", e); +throw new EventProcessingException("Failed to handle CourseExecutionCreatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/DeletedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/DeletedHandler.java new file mode 100644 index 000000000..08be0c0ff --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/DeletedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.courseexecution.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.CourseExecutionEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class DeletedHandler extends CourseExecutionEventHandler { + +public DeletedHandler(CourseExecutionRepository courseexecutionRepository, +CourseExecutionEventProcessing courseexecutionEventProcessing) { +super(courseexecutionRepository, courseexecutionEventProcessing); +} + +@EventListener +public void handleDeleted(CourseExecutionDeletedEvent event) { +try { +// Handle Deleted event for CourseExecution +courseexecutionEventProcessing.processDeleted(event); +} catch (Exception e) { +logger.error("Error handling CourseExecutionDeletedEvent", e); +throw new EventProcessingException("Failed to handle CourseExecutionDeletedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/UpdatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/UpdatedHandler.java new file mode 100644 index 000000000..b3ed9dfcf --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/UpdatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.courseexecution.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.CourseExecutionEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class UpdatedHandler extends CourseExecutionEventHandler { + +public UpdatedHandler(CourseExecutionRepository courseexecutionRepository, +CourseExecutionEventProcessing courseexecutionEventProcessing) { +super(courseexecutionRepository, courseexecutionEventProcessing); +} + +@EventListener +public void handleUpdated(CourseExecutionUpdatedEvent event) { +try { +// Handle Updated event for CourseExecution +courseexecutionEventProcessing.processUpdated(event); +} catch (Exception e) { +logger.error("Error handling CourseExecutionUpdatedEvent", e); +throw new EventProcessingException("Failed to handle CourseExecutionUpdatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionCreatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionCreatedEvent.java new file mode 100644 index 000000000..fa6ab5020 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionCreatedEvent.java @@ -0,0 +1,115 @@ +package com.generated.microservices.answers.microservices.courseexecution.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class CourseExecutionCreatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String name; +private final String acronym; +private final String academicTerm; +private final LocalDateTime startDate; +private final LocalDateTime endDate; +private final Object course; +private final Object students; + +public CourseExecutionCreatedEvent(Object source, Long aggregateId, String name, String acronym, String academicTerm, LocalDateTime startDate, LocalDateTime endDate, Object course, Object students) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Created"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.name = name; +this.acronym = acronym; +this.academicTerm = academicTerm; +this.startDate = startDate; +this.endDate = endDate; +this.course = course; +this.students = students; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getName() { +return name; +} + +public String getAcronym() { +return acronym; +} + +public String getAcademicTerm() { +return academicTerm; +} + +public LocalDateTime getStartDate() { +return startDate; +} + +public LocalDateTime getEndDate() { +return endDate; +} + +public Object getCourse() { +return course; +} + +public Object getStudents() { +return students; +} + +@Override +public String toString() { +return "CourseExecutionCreatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", name=" + name + +", acronym=" + acronym + +", academicTerm=" + academicTerm + +", startDate=" + startDate + +", endDate=" + endDate + +", course=" + course + +", students=" + students + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionDeletedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionDeletedEvent.java new file mode 100644 index 000000000..03a11b55c --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionDeletedEvent.java @@ -0,0 +1,115 @@ +package com.generated.microservices.answers.microservices.courseexecution.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class CourseExecutionDeletedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String name; +private final String acronym; +private final String academicTerm; +private final LocalDateTime startDate; +private final LocalDateTime endDate; +private final Object course; +private final Object students; + +public CourseExecutionDeletedEvent(Object source, Long aggregateId, String name, String acronym, String academicTerm, LocalDateTime startDate, LocalDateTime endDate, Object course, Object students) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Deleted"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.name = name; +this.acronym = acronym; +this.academicTerm = academicTerm; +this.startDate = startDate; +this.endDate = endDate; +this.course = course; +this.students = students; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getName() { +return name; +} + +public String getAcronym() { +return acronym; +} + +public String getAcademicTerm() { +return academicTerm; +} + +public LocalDateTime getStartDate() { +return startDate; +} + +public LocalDateTime getEndDate() { +return endDate; +} + +public Object getCourse() { +return course; +} + +public Object getStudents() { +return students; +} + +@Override +public String toString() { +return "CourseExecutionDeletedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", name=" + name + +", acronym=" + acronym + +", academicTerm=" + academicTerm + +", startDate=" + startDate + +", endDate=" + endDate + +", course=" + course + +", students=" + students + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionUpdatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionUpdatedEvent.java new file mode 100644 index 000000000..4937f88b3 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionUpdatedEvent.java @@ -0,0 +1,115 @@ +package com.generated.microservices.answers.microservices.courseexecution.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class CourseExecutionUpdatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String name; +private final String acronym; +private final String academicTerm; +private final LocalDateTime startDate; +private final LocalDateTime endDate; +private final Object course; +private final Object students; + +public CourseExecutionUpdatedEvent(Object source, Long aggregateId, String name, String acronym, String academicTerm, LocalDateTime startDate, LocalDateTime endDate, Object course, Object students) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Updated"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.name = name; +this.acronym = acronym; +this.academicTerm = academicTerm; +this.startDate = startDate; +this.endDate = endDate; +this.course = course; +this.students = students; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getName() { +return name; +} + +public String getAcronym() { +return acronym; +} + +public String getAcademicTerm() { +return academicTerm; +} + +public LocalDateTime getStartDate() { +return startDate; +} + +public LocalDateTime getEndDate() { +return endDate; +} + +public Object getCourse() { +return course; +} + +public Object getStudents() { +return students; +} + +@Override +public String toString() { +return "CourseExecutionUpdatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", name=" + name + +", acronym=" + acronym + +", academicTerm=" + academicTerm + +", startDate=" + startDate + +", endDate=" + endDate + +", course=" + course + +", students=" + students + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/SubscribesCreated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/SubscribesCreated.java new file mode 100644 index 000000000..b8159e0c9 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/SubscribesCreated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.courseexecution.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class CreatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(CreatedSubscription.class); + +private final CourseExecutionService courseexecutionService; + +public CreatedSubscription(CourseExecutionService courseexecutionService) { +this.courseexecutionService = courseexecutionService; +} + +@EventListener +@Async +public void handleCreated(CourseExecutionCreatedEvent event) { +try { +logger.info("Handling Created event for CourseExecution with ID: {}", event.getAggregateId()); + +// Process the Created event +processCreated(event); + +} catch (Exception e) { +logger.error("Error handling CourseExecutionCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitCreated(CourseExecutionCreatedEvent event) { +try { +logger.info("Post-commit handling Created event for CourseExecution with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessCreated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of CourseExecutionCreatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processCreated(CourseExecutionCreatedEvent event) { +// TODO: Implement Created event processing logic +switch (event.getEventType()) { +case "Created": +handleCreatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessCreated(CourseExecutionCreatedEvent event) { +// TODO: Implement post-transaction Created event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Created event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleCreatedLogic(CourseExecutionCreatedEvent event) { +// TODO: Implement specific Created business logic +logger.debug("Processing Created logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/SubscribesDeleted.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/SubscribesDeleted.java new file mode 100644 index 000000000..acbe09ec5 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/SubscribesDeleted.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.courseexecution.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class DeletedSubscription { +private static final Logger logger = LoggerFactory.getLogger(DeletedSubscription.class); + +private final CourseExecutionService courseexecutionService; + +public DeletedSubscription(CourseExecutionService courseexecutionService) { +this.courseexecutionService = courseexecutionService; +} + +@EventListener +@Async +public void handleDeleted(CourseExecutionDeletedEvent event) { +try { +logger.info("Handling Deleted event for CourseExecution with ID: {}", event.getAggregateId()); + +// Process the Deleted event +processDeleted(event); + +} catch (Exception e) { +logger.error("Error handling CourseExecutionDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitDeleted(CourseExecutionDeletedEvent event) { +try { +logger.info("Post-commit handling Deleted event for CourseExecution with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessDeleted(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of CourseExecutionDeletedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processDeleted(CourseExecutionDeletedEvent event) { +// TODO: Implement Deleted event processing logic +switch (event.getEventType()) { +case "Deleted": +handleDeletedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessDeleted(CourseExecutionDeletedEvent event) { +// TODO: Implement post-transaction Deleted event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Deleted event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleDeletedLogic(CourseExecutionDeletedEvent event) { +// TODO: Implement specific Deleted business logic +logger.debug("Processing Deleted logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/SubscribesUpdated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/SubscribesUpdated.java new file mode 100644 index 000000000..653f143f2 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/SubscribesUpdated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.courseexecution.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.courseexecution.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class UpdatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(UpdatedSubscription.class); + +private final CourseExecutionService courseexecutionService; + +public UpdatedSubscription(CourseExecutionService courseexecutionService) { +this.courseexecutionService = courseexecutionService; +} + +@EventListener +@Async +public void handleUpdated(CourseExecutionUpdatedEvent event) { +try { +logger.info("Handling Updated event for CourseExecution with ID: {}", event.getAggregateId()); + +// Process the Updated event +processUpdated(event); + +} catch (Exception e) { +logger.error("Error handling CourseExecutionUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitUpdated(CourseExecutionUpdatedEvent event) { +try { +logger.info("Post-commit handling Updated event for CourseExecution with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessUpdated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of CourseExecutionUpdatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processUpdated(CourseExecutionUpdatedEvent event) { +// TODO: Implement Updated event processing logic +switch (event.getEventType()) { +case "Updated": +handleUpdatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessUpdated(CourseExecutionUpdatedEvent event) { +// TODO: Implement post-transaction Updated event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Updated event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleUpdatedLogic(CourseExecutionUpdatedEvent event) { +// TODO: Implement specific Updated business logic +logger.debug("Processing Updated logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/service/CourseExecutionService.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/service/CourseExecutionService.java new file mode 100644 index 000000000..636ae9bc3 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/service/CourseExecutionService.java @@ -0,0 +1,227 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate.CourseExecutionCourse; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.courseexecution.aggregate.CourseExecutionStudent; +import pt.ulisboa.tecnico.socialsoftware.ms.exception.*; + +import java.util.*; +import java.util.stream.Collectors; + +import java.util.List; +import java.util.stream.Collectors; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.UserDto; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.AnswersException; + + +@Service +@Transactional +public class CourseExecutionService { + private static final Logger logger = LoggerFactory.getLogger(CourseExecutionService.class); + + @Autowired + private CourseExecutionRepository courseexecutionRepository; + + @Autowired + private CourseExecutionFactory courseexecutionFactory; + + public CourseExecutionService() {} + + // CRUD Operations + public CourseExecutionDto createCourseExecution(String name, String acronym, String academicTerm, LocalDateTime startDate, LocalDateTime endDate, Object course, Object students) { + try { + CourseExecution courseexecution = new CourseExecution(name, acronym, academicTerm, startDate, endDate, course, students); + courseexecution = courseexecutionRepository.save(courseexecution); + return new CourseExecutionDto(courseexecution); + } catch (Exception e) { + throw new AnswersException("Error creating courseexecution: " + e.getMessage()); + } + } + + public CourseExecutionDto getCourseExecutionById(Integer id) { + try { + CourseExecution courseexecution = (CourseExecution) courseexecutionRepository.findById(id) + .orElseThrow(() -> new AnswersException("CourseExecution not found with id: " + id)); + return new CourseExecutionDto(courseexecution); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error retrieving courseexecution: " + e.getMessage()); + } + } + + public List getAllCourseExecutions() { + try { + return courseexecutionRepository.findAll().stream() + .map(entity -> new CourseExecutionDto((CourseExecution) entity)) + .collect(Collectors.toList()); + } catch (Exception e) { + throw new AnswersException("Error retrieving all courseexecutions: " + e.getMessage()); + } + } + + public CourseExecutionDto updateCourseExecution(Integer id, CourseExecutionDto courseexecutionDto) { + try { + CourseExecution courseexecution = (CourseExecution) courseexecutionRepository.findById(id) + .orElseThrow(() -> new AnswersException("CourseExecution not found with id: " + id)); + + if (courseexecutionDto.getName() != null) { + courseexecution.setName(courseexecutionDto.getName()); + } + if (courseexecutionDto.getAcronym() != null) { + courseexecution.setAcronym(courseexecutionDto.getAcronym()); + } + if (courseexecutionDto.getAcademicTerm() != null) { + courseexecution.setAcademicTerm(courseexecutionDto.getAcademicTerm()); + } + if (courseexecutionDto.getStartDate() != null) { + courseexecution.setStartDate(courseexecutionDto.getStartDate()); + } + if (courseexecutionDto.getEndDate() != null) { + courseexecution.setEndDate(courseexecutionDto.getEndDate()); + } + if (courseexecutionDto.getCourse() != null) { + courseexecution.setCourse(courseexecutionDto.getCourse()); + } + if (courseexecutionDto.getStudents() != null) { + courseexecution.setStudents(courseexecutionDto.getStudents()); + } + + courseexecution = courseexecutionRepository.save(courseexecution); + return new CourseExecutionDto(courseexecution); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error updating courseexecution: " + e.getMessage()); + } + } + + public void deleteCourseExecution(Integer id) { + try { + if (!courseexecutionRepository.existsById(id)) { + throw new AnswersException("CourseExecution not found with id: " + id); + } + courseexecutionRepository.deleteById(id); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error deleting courseexecution: " + e.getMessage()); + } + } + + // Business Methods + @Transactional + public Object getActiveCourseExecutions(Integer id, UnitOfWork unitOfWork) { + try { + CourseExecution courseexecution = courseexecutionRepository.findById(id) + .orElseThrow(() -> new AnswersException("CourseExecution not found with id: " + id)); + + // Business logic for getActiveCourseExecutions + Object result = courseexecution.getActiveCourseExecutions(); + courseexecutionRepository.save(courseexecution); + return result; + } catch (Exception e) { + throw new AnswersException("Error in getActiveCourseExecutions: " + e.getMessage()); + } + } + + // Custom Workflow Methods + @Transactional + public void removeUser(Integer userAggregateId, Integer courseExecutionId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for removeUser + throw new UnsupportedOperationException("Workflow removeUser not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow removeUser: " + e.getMessage()); + } + } + + @Transactional + public void anonymizeStudent(Integer studentAggregateId, Integer courseExecutionId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for anonymizeStudent + throw new UnsupportedOperationException("Workflow anonymizeStudent not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow anonymizeStudent: " + e.getMessage()); + } + } + + @Transactional + public void updateStudentName(Integer studentAggregateId, String studentName, String studentUsername, Integer courseExecutionId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for updateStudentName + throw new UnsupportedOperationException("Workflow updateStudentName not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow updateStudentName: " + e.getMessage()); + } + } + + @Transactional + public void deleteCourseExecution(Integer courseExecutionId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for deleteCourseExecution + throw new UnsupportedOperationException("Workflow deleteCourseExecution not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow deleteCourseExecution: " + e.getMessage()); + } + } + + @Transactional + public void disenrollStudentFromCourseExecution(Integer studentAggregateId, Integer courseExecutionId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for disenrollStudentFromCourseExecution + throw new UnsupportedOperationException("Workflow disenrollStudentFromCourseExecution not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow disenrollStudentFromCourseExecution: " + e.getMessage()); + } + } + + // Query methods disabled - repository methods not implemented + + // Event Processing Methods + private void publishCourseExecutionCreatedEvent(CourseExecution courseexecution) { + try { + // TODO: Implement event publishing for CourseExecutionCreated + // eventPublisher.publishEvent(new CourseExecutionCreatedEvent(courseexecution)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish CourseExecutionCreatedEvent", e); + } + } + + private void publishCourseExecutionUpdatedEvent(CourseExecution courseexecution) { + try { + // TODO: Implement event publishing for CourseExecutionUpdated + // eventPublisher.publishEvent(new CourseExecutionUpdatedEvent(courseexecution)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish CourseExecutionUpdatedEvent", e); + } + } + + private void publishCourseExecutionDeletedEvent(Long courseexecutionId) { + try { + // TODO: Implement event publishing for CourseExecutionDeleted + // eventPublisher.publishEvent(new CourseExecutionDeletedEvent(courseexecutionId)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish CourseExecutionDeletedEvent", e); + } + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersErrorMessage.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersErrorMessage.java new file mode 100644 index 000000000..580a83e2a --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersErrorMessage.java @@ -0,0 +1,158 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception; + +public final class AnswersErrorMessage { + private AnswersErrorMessage() {} + + public static final String UNDEFINED_TRANSACTIONAL_MODEL = "Undefined transactional model"; + + public static final String AGGREGATE_BEING_USED_IN_OTHER_SAGA = "Aggregate is being used in %s saga"; + + public static final String INVALID_AGGREGATE_TYPE = "Aggregate type %s does not exist"; + + public static final String AGGREGATE_DELETED = "Aggregate %s with aggregate id %d already deleted."; + + public static final String AGGREGATE_NOT_FOUND = "Aggregate with aggregate id %d does not exist."; + + public static final String VERSION_MANAGER_DOES_NOT_EXIST = "Version manager does not exist."; + + public static final String AGGREGATE_MERGE_FAILURE = "Two versions of aggregate %d cannot be merged."; + + public static final String AGGREGATE_MERGE_FAILURE_DUE_TO_INTENSIONS_CONFLICT = "Two versions of aggregate cannot be merged due to intensions conflict: %s"; + + public static final String TOURNAMENT_NOT_FOUND = "Tournament with aggregate Id %d does not exist."; + + public static final String TOURNAMENT_INVALID = "Tournament version with aggregate id %d and version %d breaks invariants."; + + public static final String TOURNAMENT_MISSING_USER = "Tournament requires a user."; + + public static final String TOURNAMENT_MISSING_TOPICS = "Tournament requires topics."; + + public static final String TOURNAMENT_MISSING_START_TIME = "Tournament requires a start time."; + + public static final String TOURNAMENT_MISSING_END_TIME = "Tournament requires an end time."; + + public static final String TOURNAMENT_MISSING_NUMBER_OF_QUESTIONS = "Tournament requires a number of questions."; + + public static final String TOURNAMENT_DELETED = "Tournament with aggregate id %d already deleted."; + + public static final String TOURNAMENT_PARTICIPANT_NOT_FOUND = "User %d is not enrolled in tournament %d"; + + public static final String TOURNAMENT_TOPIC_NOT_FOUND = "Topic %d is not part of tournament %d."; + + public static final String CANNOT_UPDATE_TOURNAMENT = "Tournament %d cannot be updated."; + + public static final String CANNOT_DELETE_TOURNAMENT = "Tournament %d cannot be deleted."; + + public static final String CANNOT_ADD_PARTICIPANT = "Cannot add participant to tournament %d after it has started."; + + public static final String PARTICIPANT_NOT_STUDENT = "User %d must be a student to be added as participant to tournament %d."; + + public static final String PARTICIPANT_NOT_ENROLLED_IN_TOURNAMENT_EXECUTION = "User %d not enrolled in tournament's %d course execution."; + + public static final String TOURNAMENT_PARTICIPANT_ADDING_ANSWER_WITH_WRONG_QUIZ_ANSWER_ID = "Tournament participant is being added a wrong quiz answer id %d"; + + public static final String TOURNAMENT_IN_SAGA = "Tournament is already in a saga."; + + public static final String COURSE_EXECUTION_STUDENT_ALREADY_ENROLLED = "Student with aggregate id %d is already enrolled in course execution %d."; + + public static final String COURSE_EXECUTION_NOT_FOUND = "Course execution with aggregate id %d does not exist."; + + public static final String COURSE_EXECUTION_DELETED = "Course execution with aggregate id %d already deleted."; + + public static final String COURSE_EXECUTION_MISSING_COURSE_ID = "Course execution requires a course id."; + + public static final String COURSE_EXECUTION_MISSING_ACRONYM = "Course execution requires an acronym."; + + public static final String COURSE_EXECUTION_MISSING_ACADEMIC_TERM = "Course execution requires an academic term."; + + public static final String COURSE_EXECUTION_MISSING_END_DATE = "Course execution requires an end date."; + + public static final String COURSE_EXECUTION_INVALID = "Course execution aggregate id %d and version %d breaks invariants."; + + public static final String CANNOT_DELETE_COURSE_EXECUTION = "Cannot delete course execution with aggregate id %d."; + + public static final String COURSE_EXECUTION_STUDENT_NOT_FOUND = "Student with aggregate id %d not found in course execution %d."; + + public static final String USER_IS_ANONYMOUS = "Cant add anonymous user %d."; + + public static final String CREATOR_IS_ANONYMOUS = "Cant add user %d because creator is anonymous."; + + public static final String USER_MISSING_NAME = "User requires a name."; + + public static final String USER_MISSING_USERNAME = "User requires an username."; + + public static final String USER_MISSING_ROLE = "User requires a role."; + + public static final String USER_NOT_FOUND = "User with aggregate id %d does not exist."; + + public static final String USER_DELETED = "User with aggregate id %d alreadt deleted."; + + public static final String INACTIVE_USER = "Cannot add course execution to inactive user."; + + public static final String USER_ACTIVE = "User %d is already active."; + + public static final String USER_MERGE_FAILURE = "Two versions of a user with aggregate id %d cannot be merged."; + + public static final String QUIZ_NOT_FOUND = "Quiz with aggregate Id %d does not exist."; + + public static final String QUIZ_DELETED = "Quiz with aggregate id %d already deleted."; + + public static final String NOT_ENOUGH_QUESTIONS = "Not enough questions to generate quiz."; + + public static final String QUIZ_MERGE_FAILURE = "Two versions of a quiz with aggregate id %d cannot be merged."; + + public static final String CANNOT_UPDATE_QUIZ = "Quiz %d cannot be deleted."; + + public static final String QUIZ_DOES_NOT_BELONG_TO_COURSE_EXECUTION = "Quiz %d does not belong to course execution %d."; + + public static final String TOPIC_MISSING_NAME = "Topic requires a name."; + + public static final String TOPIC_MISSING_COURSE = "Topic requires a course."; + + public static final String TOPIC_NOT_FOUND = "Topic with aggregate id %d not found."; + + public static final String TOPIC_DELETED = "Topic with aggregate id %d already deleted."; + + public static final String COURSE_MISSING_TYPE = "Course requires a type."; + + public static final String COURSE_MISSING_NAME = "Course requires a name."; + + public static final String COURSE_NOT_FOUND = "Course with aggregate id %d not found."; + + public static final String COURSE_DELETED = "Course with aggregate id %d already deleted."; + + public static final String COURSE_INVALID = "Course version with aggregate id %d and version %d breaks invariants."; + + public static final String QUESTION_NOT_FOUND = "Question with aggregate id %d does no exist."; + + public static final String QUESTION_DELETED = "Question with aggregate id %d already deleted."; + + public static final String QUESTION_TOPIC_INVALID_COURSE = "Topic %d does not belong to course %d."; + + public static final String QUESTION_ALREADY_ANSWERED = "Question %d of quiz %d already answered."; + + public static final String INVALID_OPTION_SELECTED = "Invalid option %d for question %d."; + + public static final String QUIZ_ANSWER_NOT_FOUND = "Answer with aggregate id %d not found."; + + public static final String NO_USER_ANSWER_FOR_QUIZ = "Answer for user aggregate id %d and quiz aggregate id %d not found."; + + public static final String QUIZ_ANSWER_DELETED = "Answer with aggregate id %d already deleted."; + + public static final String CANNOT_PERFORM_CAUSAL_READ = "Cannot causally read object with aggregate id %d."; + + public static final String CANNOT_PERFORM_CAUSAL_READ_DUE_TO_EMITTED_EVENT_NOT_PROCESSED = "Cannot causally read object of class %s to causal snapshot because emitted event %s was not processed"; + + public static final String INVALID_PREV = "Prev does not match the type of the aggregate."; + + public static final String NO_PRIMARY_AGGREGATE_FOUND = "No primary aggregate was found within the transactional context."; + + public static final String TOO_MANY_PRIMARY_AGGREGATE_FOUND = "More than one primary aggregates were found within the transactional context"; + + public static final String INVARIANT_BREAK = "Aggregate %d breaks invariants"; + + public static final String INVALID_EVENT_TYPE = "Invalid event type %s."; + + public static final String CANNOT_MODIFY_INACTIVE_AGGREGATE = "Cannot update aggregate %d because it is INACTIVE."; + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersException.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersException.java new file mode 100644 index 000000000..4f10d0999 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersException.java @@ -0,0 +1,53 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; +import pt.ulisboa.tecnico.socialsoftware.ms.exception.SimulatorException; + +@ResponseStatus(HttpStatus.BAD_REQUEST) +public class AnswersException extends SimulatorException { + private static final Logger logger = LoggerFactory.getLogger(AnswersException.class); + private final String answersErrorMessage; + + public AnswersException(String answersErrorMessage) { + super(answersErrorMessage); + logger.info(answersErrorMessage); + this.answersErrorMessage = answersErrorMessage; + } + + public AnswersException(String answersErrorMessage, String value) { + super(String.format(answersErrorMessage, value)); + logger.info(String.format(answersErrorMessage, value)); + this.answersErrorMessage = answersErrorMessage; + } + + public AnswersException(String answersErrorMessage, String value1, String value2) { + super(String.format(answersErrorMessage, value1, value2)); + logger.info(String.format(answersErrorMessage, value1, value2)); + this.answersErrorMessage = answersErrorMessage; + } + + public AnswersException(String answersErrorMessage, int value) { + super(String.format(answersErrorMessage, value)); + logger.info(String.format(answersErrorMessage, value)); + this.answersErrorMessage = answersErrorMessage; + } + + public AnswersException(String answersErrorMessage, int value1, int value2) { + super(String.format(answersErrorMessage, value1, value2)); + logger.info(String.format(answersErrorMessage, value1, value2)); + this.answersErrorMessage = answersErrorMessage; + } + + public AnswersException(String answersErrorMessage, String value1, int value2) { + super(String.format(answersErrorMessage, value1, value2)); + logger.info(String.format(answersErrorMessage, value1, value2)); + this.answersErrorMessage = answersErrorMessage; + } + + public String getErrorMessage() { + return this.answersErrorMessage; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersExceptionHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersExceptionHandler.java new file mode 100644 index 000000000..0b1253bd8 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/exception/AnswersExceptionHandler.java @@ -0,0 +1,32 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.exception.SimulatorException; + +@ControllerAdvice +public class AnswersExceptionHandler { + private static final Logger logger = LoggerFactory.getLogger(AnswersExceptionHandler.class); + + @ExceptionHandler(AnswersException.class) + public ResponseEntity handleAnswersException(AnswersException ex) { + logger.warn("Handled AnswersException: {}", ex.getErrorMessage()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage()); + } + + @ExceptionHandler(SimulatorException.class) + public ResponseEntity handleSimulatorException(SimulatorException ex) { + logger.warn("Handled SimulatorException: {}", ex.getMessage()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage()); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleGenericException(Exception ex) { + logger.error("Unhandled exception", ex); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Internal server error"); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/Option.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/Option.java new file mode 100644 index 000000000..dc7c2f995 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/Option.java @@ -0,0 +1,49 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class Option { + private Integer optionNumber; + private String content; + private Boolean isCorrect; + + public Option(Integer optionNumber, String content, Boolean isCorrect) { + this.optionNumber = optionNumber; + this.content = content; + this.isCorrect = isCorrect; + } + + public Option(Option other) { + // Copy constructor + } + + + public Integer getOptionNumber() { + return optionNumber; + } + + public void setOptionNumber(Integer optionNumber) { + this.optionNumber = optionNumber; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Boolean isIsCorrect() { + return isCorrect; + } + + public void setIsCorrect(Boolean isCorrect) { + this.isCorrect = isCorrect; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/OptionDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/OptionDto.java new file mode 100644 index 000000000..a2f9231ec --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/OptionDto.java @@ -0,0 +1,45 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class OptionDto implements Serializable { + private Integer optionNumber; + private String content; + private boolean isCorrect; + + public OptionDto() { + } + + public OptionDto(Option option) { + this.optionNumber = option.getOptionNumber(); + this.content = option.getContent(); + this.isCorrect = option.isIsCorrect(); + } + + public Integer getOptionNumber() { + return optionNumber; + } + + public void setOptionNumber(Integer optionNumber) { + this.optionNumber = optionNumber; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public boolean isIsCorrect() { + return isCorrect; + } + + public void setIsCorrect(Boolean isCorrect) { + this.isCorrect = isCorrect; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/Question.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/Question.java new file mode 100644 index 000000000..9e5c6603d --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/Question.java @@ -0,0 +1,135 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Entity +public class Question extends Aggregate { + @Id + private String title; + private String content; + private Integer numberOfOptions; + private Integer correctOption; + private Integer order; + private Object course; + private Object topics; + private Object options; + + public Question(String title, String content, Integer numberOfOptions, Integer correctOption, Integer order, Object course, Object topics, Object options) { + this.title = title; + this.content = content; + this.numberOfOptions = numberOfOptions; + this.correctOption = correctOption; + this.order = order; + this.course = course; + this.topics = topics; + this.options = options; + } + + public Question(Question other) { + // Copy constructor + } + + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Integer getNumberOfOptions() { + return numberOfOptions; + } + + public void setNumberOfOptions(Integer numberOfOptions) { + this.numberOfOptions = numberOfOptions; + } + + public Integer getCorrectOption() { + return correctOption; + } + + public void setCorrectOption(Integer correctOption) { + this.correctOption = correctOption; + } + + public Integer getOrder() { + return order; + } + + public void setOrder(Integer order) { + this.order = order; + } + + public Object getCourse() { + return course; + } + + public void setCourse(Object course) { + this.course = course; + } + + public Object getTopics() { + return topics; + } + + public void setTopics(Object topics) { + this.topics = topics; + } + + public Object getOptions() { + return options; + } + + public void setOptions(Object options) { + this.options = options; + } + public Object createQuestion(String title, String content, Integer numberOfOptions, Integer correctOption, Integer order, Object course, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getQuestionById(Integer questionId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getAllQuestions(UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getQuestionsByCourse(Integer courseId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getQuestionsByTopic(Integer topicId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object updateQuestion(Integer questionId, String title, String content, Integer numberOfOptions, Integer correctOption, Integer order, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object deleteQuestion(Integer questionId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourse.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourse.java new file mode 100644 index 000000000..32e582ac2 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourse.java @@ -0,0 +1,49 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class QuestionCourse { + private Integer courseAggregateId; + private String courseName; + private String courseAcronym; + + public QuestionCourse(Integer courseAggregateId, String courseName, String courseAcronym) { + this.courseAggregateId = courseAggregateId; + this.courseName = courseName; + this.courseAcronym = courseAcronym; + } + + public QuestionCourse(QuestionCourse other) { + // Copy constructor + } + + + public Integer getCourseAggregateId() { + return courseAggregateId; + } + + public void setCourseAggregateId(Integer courseAggregateId) { + this.courseAggregateId = courseAggregateId; + } + + public String getCourseName() { + return courseName; + } + + public void setCourseName(String courseName) { + this.courseName = courseName; + } + + public String getCourseAcronym() { + return courseAcronym; + } + + public void setCourseAcronym(String courseAcronym) { + this.courseAcronym = courseAcronym; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourseDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourseDto.java new file mode 100644 index 000000000..80e43a287 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourseDto.java @@ -0,0 +1,45 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class QuestionCourseDto implements Serializable { + private Integer courseAggregateId; + private String courseName; + private String courseAcronym; + + public QuestionCourseDto() { + } + + public QuestionCourseDto(QuestionCourse questioncourse) { + this.courseAggregateId = questioncourse.getCourseAggregateId(); + this.courseName = questioncourse.getCourseName(); + this.courseAcronym = questioncourse.getCourseAcronym(); + } + + public Integer getCourseAggregateId() { + return courseAggregateId; + } + + public void setCourseAggregateId(Integer courseAggregateId) { + this.courseAggregateId = courseAggregateId; + } + + public String getCourseName() { + return courseName; + } + + public void setCourseName(String courseName) { + this.courseName = courseName; + } + + public String getCourseAcronym() { + return courseAcronym; + } + + public void setCourseAcronym(String courseAcronym) { + this.courseAcronym = courseAcronym; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCustomRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCustomRepository.java new file mode 100644 index 000000000..8825e9a6e --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCustomRepository.java @@ -0,0 +1,9 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate; + +import java.util.Optional; +import java.util.List; +import java.util.Set; + +public interface QuestionCustomRepository { + Optional findQuestionIdByTitle(String questionTitle); +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionDto.java new file mode 100644 index 000000000..0a064d140 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionDto.java @@ -0,0 +1,125 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate; + +import java.io.Serializable; +import java.util.Set; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class QuestionDto implements Serializable { + private Integer aggregateId; + private String title; + private String content; + private Integer numberOfOptions; + private Integer correctOption; + private Integer order; + private Object course; + private Object topics; + private Object options; + private Integer version; + private AggregateState state; + + public QuestionDto() { + } + + public QuestionDto(Question question) { + this.aggregateId = question.getAggregateId(); + this.title = question.getTitle(); + this.content = question.getContent(); + this.numberOfOptions = question.getNumberOfOptions(); + this.correctOption = question.getCorrectOption(); + this.order = question.getOrder(); + this.course = question.getCourse(); + this.topics = question.getTopics(); + this.options = question.getOptions(); + this.version = question.getVersion(); + this.state = question.getState(); + } + + public Integer getAggregateId() { + return aggregateId; + } + + public void setAggregateId(Integer aggregateId) { + this.aggregateId = aggregateId; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Integer getNumberOfOptions() { + return numberOfOptions; + } + + public void setNumberOfOptions(Integer numberOfOptions) { + this.numberOfOptions = numberOfOptions; + } + + public Integer getCorrectOption() { + return correctOption; + } + + public void setCorrectOption(Integer correctOption) { + this.correctOption = correctOption; + } + + public Integer getOrder() { + return order; + } + + public void setOrder(Integer order) { + this.order = order; + } + + public Object getCourse() { + return course; + } + + public void setCourse(Object course) { + this.course = course; + } + + public Object getTopics() { + return topics; + } + + public void setTopics(Object topics) { + this.topics = topics; + } + + public Object getOptions() { + return options; + } + + public void setOptions(Object options) { + this.options = options; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public AggregateState getState() { + return state; + } + + public void setState(AggregateState state) { + this.state = state; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionFactory.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionFactory.java new file mode 100644 index 000000000..436bde975 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionFactory.java @@ -0,0 +1,38 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate; + +import org.springframework.stereotype.Service; +import pt.ulisboa.tecnico.socialsoftware.ms.causal.aggregate.CausalAggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWorkService; +import pt.ulisboa.tecnico.socialsoftware.ms.sagas.aggregate.SagaAggregate; + +@Service +public class QuestionFactory { + + public Question createQuestion(Integer aggregateId, QuestionDto questionDto) { + // Factory method implementation - create root entity directly + // Extract properties from DTO and create the root entity + return new Question( + questionDto.getTitle(), + questionDto.getContent(), + questionDto.getNumberOfOptions(), + questionDto.getCorrectOption(), + questionDto.getOrder(), + questionDto.getCourse(), + questionDto.getTopics(), + questionDto.getOptions() + ); + } + + public Question createQuestionFromExisting(Question existingQuestion) { + // Create a copy of the existing aggregate + if (existingQuestion instanceof Question) { + return new Question((Question) existingQuestion); + } + throw new IllegalArgumentException("Unknown aggregate type"); + } + + public QuestionDto createQuestionDto(Question question) { + return new QuestionDto((Question) question); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionRepository.java new file mode 100644 index 000000000..8790ae5eb --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionRepository.java @@ -0,0 +1,19 @@ +package com.generated.microservices.answers.microservices.question.aggregate; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import jakarta.transaction.Transactional; + +@Repository +@Transactional +public interface QuestionRepository extends JpaRepository { + @Query(value = "select question.id from Question question where question.title = :title AND question.state = 'ACTIVE' AND question.sagaState = 'NOT_IN_SAGA'") + Optional findQuestionIdByTitleForSaga(String title); + + @Query(value = "select question.id from Question question where question.content = :content AND question.state = 'ACTIVE' AND question.sagaState = 'NOT_IN_SAGA'") + Optional findQuestionIdByContentForSaga(String content); + + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopic.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopic.java new file mode 100644 index 000000000..2847ba512 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopic.java @@ -0,0 +1,39 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class QuestionTopic { + private Integer topicId; + private String topicName; + + public QuestionTopic(Integer topicId, String topicName) { + this.topicId = topicId; + this.topicName = topicName; + } + + public QuestionTopic(QuestionTopic other) { + // Copy constructor + } + + + public Integer getTopicId() { + return topicId; + } + + public void setTopicId(Integer topicId) { + this.topicId = topicId; + } + + public String getTopicName() { + return topicName; + } + + public void setTopicName(String topicName) { + this.topicName = topicName; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopicDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopicDto.java new file mode 100644 index 000000000..8ed7d8d60 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopicDto.java @@ -0,0 +1,35 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class QuestionTopicDto implements Serializable { + private Integer topicId; + private String topicName; + + public QuestionTopicDto() { + } + + public QuestionTopicDto(QuestionTopic questiontopic) { + this.topicId = questiontopic.getTopicId(); + this.topicName = questiontopic.getTopicName(); + } + + public Integer getTopicId() { + return topicId; + } + + public void setTopicId(Integer topicId) { + this.topicId = topicId; + } + + public String getTopicName() { + return topicName; + } + + public void setTopicName(String topicName) { + this.topicName = topicName; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/QuestionEventHandling.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/QuestionEventHandling.java new file mode 100644 index 000000000..117d55af1 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/QuestionEventHandling.java @@ -0,0 +1,192 @@ +package com.generated.microservices.answers.microservices.question.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.question.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + + +@Component +public class QuestionEventHandling { +private static final Logger logger = LoggerFactory.getLogger(QuestionEventHandling.class); + +private final QuestionService questionService; +private final QuestionRepository questionRepository; + +public QuestionEventHandling(QuestionService questionService, QuestionRepository +questionRepository) { +this.questionService = questionService; +this.questionRepository = questionRepository; +} + +@EventListener +public void handleCreated(QuestionCreatedEvent event) { +try { +logger.info("Processing Created event for Question with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Created": +processCreatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing QuestionCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process QuestionCreatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncCreated(QuestionCreatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Created event for Question with ID: {}", event.getAggregateId()); + processAsyncCreatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of QuestionCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleUpdated(QuestionUpdatedEvent event) { +try { +logger.info("Processing Updated event for Question with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Updated": +processUpdatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing QuestionUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process QuestionUpdatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncUpdated(QuestionUpdatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Updated event for Question with ID: {}", event.getAggregateId()); + processAsyncUpdatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of QuestionUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleDeleted(QuestionDeletedEvent event) { +try { +logger.info("Processing Deleted event for Question with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Deleted": +processDeletedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing QuestionDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process QuestionDeletedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncDeleted(QuestionDeletedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Deleted event for Question with ID: {}", event.getAggregateId()); + processAsyncDeletedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of QuestionDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + + + // Event processing methods + private void processCreatedEvent(QuestionCreatedEvent event) { + // TODO: Implement Created event processing logic + logger.debug("Processing Created event for Question ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle creation event - might trigger welcome workflows, notifications, etc. + handleCreationEvent(event); + } + + private void processAsyncCreatedEvent(QuestionCreatedEvent event) { + // TODO: Implement async Created event processing logic + logger.debug("Async processing Created event for Question ID: {}", event.getAggregateId()); + } + + private void processUpdatedEvent(QuestionUpdatedEvent event) { + // TODO: Implement Updated event processing logic + logger.debug("Processing Updated event for Question ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle update event - might trigger validation, sync with external systems, etc. + handleUpdateEvent(event); + } + + private void processAsyncUpdatedEvent(QuestionUpdatedEvent event) { + // TODO: Implement async Updated event processing logic + logger.debug("Async processing Updated event for Question ID: {}", event.getAggregateId()); + } + + private void processDeletedEvent(QuestionDeletedEvent event) { + // TODO: Implement Deleted event processing logic + logger.debug("Processing Deleted event for Question ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle deletion event - might trigger cleanup, archival, notifications, etc. + handleDeletionEvent(event); + } + + private void processAsyncDeletedEvent(QuestionDeletedEvent event) { + // TODO: Implement async Deleted event processing logic + logger.debug("Async processing Deleted event for Question ID: {}", event.getAggregateId()); + } + + + // Helper methods for specific event types + private void handleCreationEvent(Object event) { + // TODO: Implement creation-specific logic + logger.debug("Handling creation event"); + } + + private void handleUpdateEvent(Object event) { + // TODO: Implement update-specific logic + logger.debug("Handling update event"); + } + + private void handleDeletionEvent(Object event) { + // TODO: Implement deletion-specific logic + logger.debug("Handling deletion event"); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/CreatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/CreatedHandler.java new file mode 100644 index 000000000..a627fc425 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/CreatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.question.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.question.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.QuestionEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class CreatedHandler extends QuestionEventHandler { + +public CreatedHandler(QuestionRepository questionRepository, +QuestionEventProcessing questionEventProcessing) { +super(questionRepository, questionEventProcessing); +} + +@EventListener +public void handleCreated(QuestionCreatedEvent event) { +try { +// Handle Created event for Question +questionEventProcessing.processCreated(event); +} catch (Exception e) { +logger.error("Error handling QuestionCreatedEvent", e); +throw new EventProcessingException("Failed to handle QuestionCreatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/DeletedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/DeletedHandler.java new file mode 100644 index 000000000..f204f452e --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/DeletedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.question.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.question.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.QuestionEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class DeletedHandler extends QuestionEventHandler { + +public DeletedHandler(QuestionRepository questionRepository, +QuestionEventProcessing questionEventProcessing) { +super(questionRepository, questionEventProcessing); +} + +@EventListener +public void handleDeleted(QuestionDeletedEvent event) { +try { +// Handle Deleted event for Question +questionEventProcessing.processDeleted(event); +} catch (Exception e) { +logger.error("Error handling QuestionDeletedEvent", e); +throw new EventProcessingException("Failed to handle QuestionDeletedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/QuestionEventHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/QuestionEventHandler.java new file mode 100644 index 000000000..c86149f4d --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/QuestionEventHandler.java @@ -0,0 +1,38 @@ +package com.generated.microservices.answers.microservices.question.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.question.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.QuestionEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +public abstract class QuestionEventHandler extends EventHandler { +private QuestionRepository questionRepository; +protected QuestionEventProcessing questionEventProcessing; + +public QuestionEventHandler(QuestionRepository questionRepository, +QuestionEventProcessing questionEventProcessing) { +this.questionRepository = questionRepository; +this.questionEventProcessing = questionEventProcessing; +} + +public Set getAggregateIds() { + return + questionRepository.findAll().stream().map(Question::getAggregateId).collect(Collectors.toSet()); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/UpdatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/UpdatedHandler.java new file mode 100644 index 000000000..6b6e31601 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/UpdatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.question.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.question.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.QuestionEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class UpdatedHandler extends QuestionEventHandler { + +public UpdatedHandler(QuestionRepository questionRepository, +QuestionEventProcessing questionEventProcessing) { +super(questionRepository, questionEventProcessing); +} + +@EventListener +public void handleUpdated(QuestionUpdatedEvent event) { +try { +// Handle Updated event for Question +questionEventProcessing.processUpdated(event); +} catch (Exception e) { +logger.error("Error handling QuestionUpdatedEvent", e); +throw new EventProcessingException("Failed to handle QuestionUpdatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionCreatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionCreatedEvent.java new file mode 100644 index 000000000..572aa0a80 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionCreatedEvent.java @@ -0,0 +1,122 @@ +package com.generated.microservices.answers.microservices.question.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.question.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class QuestionCreatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String title; +private final String content; +private final Integer numberOfOptions; +private final Integer correctOption; +private final Integer order; +private final Object course; +private final Object topics; +private final Object options; + +public QuestionCreatedEvent(Object source, Long aggregateId, String title, String content, Integer numberOfOptions, Integer correctOption, Integer order, Object course, Object topics, Object options) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Created"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.title = title; +this.content = content; +this.numberOfOptions = numberOfOptions; +this.correctOption = correctOption; +this.order = order; +this.course = course; +this.topics = topics; +this.options = options; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getTitle() { +return title; +} + +public String getContent() { +return content; +} + +public Integer getNumberOfOptions() { +return numberOfOptions; +} + +public Integer getCorrectOption() { +return correctOption; +} + +public Integer getOrder() { +return order; +} + +public Object getCourse() { +return course; +} + +public Object getTopics() { +return topics; +} + +public Object getOptions() { +return options; +} + +@Override +public String toString() { +return "QuestionCreatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", title=" + title + +", content=" + content + +", numberOfOptions=" + numberOfOptions + +", correctOption=" + correctOption + +", order=" + order + +", course=" + course + +", topics=" + topics + +", options=" + options + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionDeletedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionDeletedEvent.java new file mode 100644 index 000000000..e0fc14543 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionDeletedEvent.java @@ -0,0 +1,122 @@ +package com.generated.microservices.answers.microservices.question.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.question.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class QuestionDeletedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String title; +private final String content; +private final Integer numberOfOptions; +private final Integer correctOption; +private final Integer order; +private final Object course; +private final Object topics; +private final Object options; + +public QuestionDeletedEvent(Object source, Long aggregateId, String title, String content, Integer numberOfOptions, Integer correctOption, Integer order, Object course, Object topics, Object options) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Deleted"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.title = title; +this.content = content; +this.numberOfOptions = numberOfOptions; +this.correctOption = correctOption; +this.order = order; +this.course = course; +this.topics = topics; +this.options = options; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getTitle() { +return title; +} + +public String getContent() { +return content; +} + +public Integer getNumberOfOptions() { +return numberOfOptions; +} + +public Integer getCorrectOption() { +return correctOption; +} + +public Integer getOrder() { +return order; +} + +public Object getCourse() { +return course; +} + +public Object getTopics() { +return topics; +} + +public Object getOptions() { +return options; +} + +@Override +public String toString() { +return "QuestionDeletedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", title=" + title + +", content=" + content + +", numberOfOptions=" + numberOfOptions + +", correctOption=" + correctOption + +", order=" + order + +", course=" + course + +", topics=" + topics + +", options=" + options + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionUpdatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionUpdatedEvent.java new file mode 100644 index 000000000..9c1d4a635 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionUpdatedEvent.java @@ -0,0 +1,122 @@ +package com.generated.microservices.answers.microservices.question.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.question.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class QuestionUpdatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String title; +private final String content; +private final Integer numberOfOptions; +private final Integer correctOption; +private final Integer order; +private final Object course; +private final Object topics; +private final Object options; + +public QuestionUpdatedEvent(Object source, Long aggregateId, String title, String content, Integer numberOfOptions, Integer correctOption, Integer order, Object course, Object topics, Object options) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Updated"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.title = title; +this.content = content; +this.numberOfOptions = numberOfOptions; +this.correctOption = correctOption; +this.order = order; +this.course = course; +this.topics = topics; +this.options = options; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getTitle() { +return title; +} + +public String getContent() { +return content; +} + +public Integer getNumberOfOptions() { +return numberOfOptions; +} + +public Integer getCorrectOption() { +return correctOption; +} + +public Integer getOrder() { +return order; +} + +public Object getCourse() { +return course; +} + +public Object getTopics() { +return topics; +} + +public Object getOptions() { +return options; +} + +@Override +public String toString() { +return "QuestionUpdatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", title=" + title + +", content=" + content + +", numberOfOptions=" + numberOfOptions + +", correctOption=" + correctOption + +", order=" + order + +", course=" + course + +", topics=" + topics + +", options=" + options + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/SubscribesCreated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/SubscribesCreated.java new file mode 100644 index 000000000..cb4065ebf --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/SubscribesCreated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.question.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.question.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class CreatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(CreatedSubscription.class); + +private final QuestionService questionService; + +public CreatedSubscription(QuestionService questionService) { +this.questionService = questionService; +} + +@EventListener +@Async +public void handleCreated(QuestionCreatedEvent event) { +try { +logger.info("Handling Created event for Question with ID: {}", event.getAggregateId()); + +// Process the Created event +processCreated(event); + +} catch (Exception e) { +logger.error("Error handling QuestionCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitCreated(QuestionCreatedEvent event) { +try { +logger.info("Post-commit handling Created event for Question with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessCreated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of QuestionCreatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processCreated(QuestionCreatedEvent event) { +// TODO: Implement Created event processing logic +switch (event.getEventType()) { +case "Created": +handleCreatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessCreated(QuestionCreatedEvent event) { +// TODO: Implement post-transaction Created event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Created event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleCreatedLogic(QuestionCreatedEvent event) { +// TODO: Implement specific Created business logic +logger.debug("Processing Created logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/SubscribesDeleted.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/SubscribesDeleted.java new file mode 100644 index 000000000..0a850cfbc --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/SubscribesDeleted.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.question.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.question.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class DeletedSubscription { +private static final Logger logger = LoggerFactory.getLogger(DeletedSubscription.class); + +private final QuestionService questionService; + +public DeletedSubscription(QuestionService questionService) { +this.questionService = questionService; +} + +@EventListener +@Async +public void handleDeleted(QuestionDeletedEvent event) { +try { +logger.info("Handling Deleted event for Question with ID: {}", event.getAggregateId()); + +// Process the Deleted event +processDeleted(event); + +} catch (Exception e) { +logger.error("Error handling QuestionDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitDeleted(QuestionDeletedEvent event) { +try { +logger.info("Post-commit handling Deleted event for Question with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessDeleted(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of QuestionDeletedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processDeleted(QuestionDeletedEvent event) { +// TODO: Implement Deleted event processing logic +switch (event.getEventType()) { +case "Deleted": +handleDeletedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessDeleted(QuestionDeletedEvent event) { +// TODO: Implement post-transaction Deleted event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Deleted event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleDeletedLogic(QuestionDeletedEvent event) { +// TODO: Implement specific Deleted business logic +logger.debug("Processing Deleted logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/SubscribesUpdated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/SubscribesUpdated.java new file mode 100644 index 000000000..dcf1c52f6 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/SubscribesUpdated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.question.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.question.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class UpdatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(UpdatedSubscription.class); + +private final QuestionService questionService; + +public UpdatedSubscription(QuestionService questionService) { +this.questionService = questionService; +} + +@EventListener +@Async +public void handleUpdated(QuestionUpdatedEvent event) { +try { +logger.info("Handling Updated event for Question with ID: {}", event.getAggregateId()); + +// Process the Updated event +processUpdated(event); + +} catch (Exception e) { +logger.error("Error handling QuestionUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitUpdated(QuestionUpdatedEvent event) { +try { +logger.info("Post-commit handling Updated event for Question with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessUpdated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of QuestionUpdatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processUpdated(QuestionUpdatedEvent event) { +// TODO: Implement Updated event processing logic +switch (event.getEventType()) { +case "Updated": +handleUpdatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessUpdated(QuestionUpdatedEvent event) { +// TODO: Implement post-transaction Updated event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Updated event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleUpdatedLogic(QuestionUpdatedEvent event) { +// TODO: Implement specific Updated business logic +logger.debug("Processing Updated logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/service/QuestionService.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/service/QuestionService.java new file mode 100644 index 000000000..f18dbd28a --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/service/QuestionService.java @@ -0,0 +1,218 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.QuestionCourse; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.QuestionTopic; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.question.aggregate.Option; +import pt.ulisboa.tecnico.socialsoftware.ms.exception.*; + +import java.util.*; +import java.util.stream.Collectors; + +import java.util.List; +import java.util.stream.Collectors; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.UserDto; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.AnswersException; + + +@Service +@Transactional +public class QuestionService { + private static final Logger logger = LoggerFactory.getLogger(QuestionService.class); + + @Autowired + private QuestionRepository questionRepository; + + @Autowired + private QuestionFactory questionFactory; + + public QuestionService() {} + + // CRUD Operations + public QuestionDto createQuestion(String title, String content, Integer numberOfOptions, Integer correctOption, Integer order, Object course, Object topics, Object options) { + try { + Question question = new Question(title, content, numberOfOptions, correctOption, order, course, topics, options); + question = questionRepository.save(question); + return new QuestionDto(question); + } catch (Exception e) { + throw new AnswersException("Error creating question: " + e.getMessage()); + } + } + + public QuestionDto getQuestionById(Integer id) { + try { + Question question = (Question) questionRepository.findById(id) + .orElseThrow(() -> new AnswersException("Question not found with id: " + id)); + return new QuestionDto(question); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error retrieving question: " + e.getMessage()); + } + } + + public List getAllQuestions() { + try { + return questionRepository.findAll().stream() + .map(entity -> new QuestionDto((Question) entity)) + .collect(Collectors.toList()); + } catch (Exception e) { + throw new AnswersException("Error retrieving all questions: " + e.getMessage()); + } + } + + public QuestionDto updateQuestion(Integer id, QuestionDto questionDto) { + try { + Question question = (Question) questionRepository.findById(id) + .orElseThrow(() -> new AnswersException("Question not found with id: " + id)); + + if (questionDto.getTitle() != null) { + question.setTitle(questionDto.getTitle()); + } + if (questionDto.getContent() != null) { + question.setContent(questionDto.getContent()); + } + if (questionDto.getNumberOfOptions() != null) { + question.setNumberOfOptions(questionDto.getNumberOfOptions()); + } + if (questionDto.getCorrectOption() != null) { + question.setCorrectOption(questionDto.getCorrectOption()); + } + if (questionDto.getOrder() != null) { + question.setOrder(questionDto.getOrder()); + } + if (questionDto.getCourse() != null) { + question.setCourse(questionDto.getCourse()); + } + if (questionDto.getTopics() != null) { + question.setTopics(questionDto.getTopics()); + } + if (questionDto.getOptions() != null) { + question.setOptions(questionDto.getOptions()); + } + + question = questionRepository.save(question); + return new QuestionDto(question); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error updating question: " + e.getMessage()); + } + } + + public void deleteQuestion(Integer id) { + try { + if (!questionRepository.existsById(id)) { + throw new AnswersException("Question not found with id: " + id); + } + questionRepository.deleteById(id); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error deleting question: " + e.getMessage()); + } + } + + // Business Methods + @Transactional + public Object searchQuestionsByTitle(Integer id, String title, UnitOfWork unitOfWork) { + try { + Question question = questionRepository.findById(id) + .orElseThrow(() -> new AnswersException("Question not found with id: " + id)); + + // Business logic for searchQuestionsByTitle + Object result = question.searchQuestionsByTitle(); + questionRepository.save(question); + return result; + } catch (Exception e) { + throw new AnswersException("Error in searchQuestionsByTitle: " + e.getMessage()); + } + } + + // Custom Workflow Methods + @Transactional + public void deleteQuestion(Integer questionId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for deleteQuestion + throw new UnsupportedOperationException("Workflow deleteQuestion not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow deleteQuestion: " + e.getMessage()); + } + } + + @Transactional + public void updateQuestion(Integer questionId, String title, String content, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for updateQuestion + throw new UnsupportedOperationException("Workflow updateQuestion not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow updateQuestion: " + e.getMessage()); + } + } + + @Transactional + public void removeCourse(Integer courseId, Integer questionId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for removeCourse + throw new UnsupportedOperationException("Workflow removeCourse not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow removeCourse: " + e.getMessage()); + } + } + + @Transactional + public void removeTopic(Integer topicId, Integer questionId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for removeTopic + throw new UnsupportedOperationException("Workflow removeTopic not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow removeTopic: " + e.getMessage()); + } + } + + // Query methods disabled - repository methods not implemented + + // Event Processing Methods + private void publishQuestionCreatedEvent(Question question) { + try { + // TODO: Implement event publishing for QuestionCreated + // eventPublisher.publishEvent(new QuestionCreatedEvent(question)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish QuestionCreatedEvent", e); + } + } + + private void publishQuestionUpdatedEvent(Question question) { + try { + // TODO: Implement event publishing for QuestionUpdated + // eventPublisher.publishEvent(new QuestionUpdatedEvent(question)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish QuestionUpdatedEvent", e); + } + } + + private void publishQuestionDeletedEvent(Long questionId) { + try { + // TODO: Implement event publishing for QuestionDeleted + // eventPublisher.publishEvent(new QuestionDeletedEvent(questionId)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish QuestionDeletedEvent", e); + } + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/Quiz.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/Quiz.java new file mode 100644 index 000000000..61c62f4a9 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/Quiz.java @@ -0,0 +1,146 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +@Entity +public class Quiz extends Aggregate { + @Id + private String title; + private String description; + private String quizType; + private LocalDateTime availableDate; + private LocalDateTime conclusionDate; + private Integer numberOfQuestions; + private Object courseExecution; + private Object questions; + private Object options; + + public Quiz(String title, String description, String quizType, LocalDateTime availableDate, LocalDateTime conclusionDate, Integer numberOfQuestions, Object courseExecution, Object questions, Object options) { + this.title = title; + this.description = description; + this.quizType = quizType; + this.availableDate = availableDate; + this.conclusionDate = conclusionDate; + this.numberOfQuestions = numberOfQuestions; + this.courseExecution = courseExecution; + this.questions = questions; + this.options = options; + } + + public Quiz(Quiz other) { + // Copy constructor + } + + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getQuizType() { + return quizType; + } + + public void setQuizType(String quizType) { + this.quizType = quizType; + } + + public LocalDateTime getAvailableDate() { + return availableDate; + } + + public void setAvailableDate(LocalDateTime availableDate) { + this.availableDate = availableDate; + } + + public LocalDateTime getConclusionDate() { + return conclusionDate; + } + + public void setConclusionDate(LocalDateTime conclusionDate) { + this.conclusionDate = conclusionDate; + } + + public Integer getNumberOfQuestions() { + return numberOfQuestions; + } + + public void setNumberOfQuestions(Integer numberOfQuestions) { + this.numberOfQuestions = numberOfQuestions; + } + + public Object getCourseExecution() { + return courseExecution; + } + + public void setCourseExecution(Object courseExecution) { + this.courseExecution = courseExecution; + } + + public Object getQuestions() { + return questions; + } + + public void setQuestions(Object questions) { + this.questions = questions; + } + + public Object getOptions() { + return options; + } + + public void setOptions(Object options) { + this.options = options; + } + public Object createQuiz(String title, String description, String quizType, LocalDateTime availableDate, LocalDateTime conclusionDate, Integer numberOfQuestions, Object courseExecution, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getQuizById(Integer quizId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getAllQuizzes(UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getQuizzesByCourseExecution(Integer courseExecutionId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getQuizzesByType(String quizType, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object updateQuiz(Integer quizId, String title, String description, String quizType, LocalDateTime availableDate, LocalDateTime conclusionDate, Integer numberOfQuestions, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object deleteQuiz(Integer quizId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCourseExecution.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCourseExecution.java new file mode 100644 index 000000000..280bb737a --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCourseExecution.java @@ -0,0 +1,59 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class QuizCourseExecution { + private Integer courseExecutionAggregateId; + private String courseExecutionName; + private String courseExecutionAcronym; + private String courseExecutionAcademicTerm; + + public QuizCourseExecution(Integer courseExecutionAggregateId, String courseExecutionName, String courseExecutionAcronym, String courseExecutionAcademicTerm) { + this.courseExecutionAggregateId = courseExecutionAggregateId; + this.courseExecutionName = courseExecutionName; + this.courseExecutionAcronym = courseExecutionAcronym; + this.courseExecutionAcademicTerm = courseExecutionAcademicTerm; + } + + public QuizCourseExecution(QuizCourseExecution other) { + // Copy constructor + } + + + public Integer getCourseExecutionAggregateId() { + return courseExecutionAggregateId; + } + + public void setCourseExecutionAggregateId(Integer courseExecutionAggregateId) { + this.courseExecutionAggregateId = courseExecutionAggregateId; + } + + public String getCourseExecutionName() { + return courseExecutionName; + } + + public void setCourseExecutionName(String courseExecutionName) { + this.courseExecutionName = courseExecutionName; + } + + public String getCourseExecutionAcronym() { + return courseExecutionAcronym; + } + + public void setCourseExecutionAcronym(String courseExecutionAcronym) { + this.courseExecutionAcronym = courseExecutionAcronym; + } + + public String getCourseExecutionAcademicTerm() { + return courseExecutionAcademicTerm; + } + + public void setCourseExecutionAcademicTerm(String courseExecutionAcademicTerm) { + this.courseExecutionAcademicTerm = courseExecutionAcademicTerm; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCourseExecutionDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCourseExecutionDto.java new file mode 100644 index 000000000..a9491bc51 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCourseExecutionDto.java @@ -0,0 +1,55 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class QuizCourseExecutionDto implements Serializable { + private Integer courseExecutionAggregateId; + private String courseExecutionName; + private String courseExecutionAcronym; + private String courseExecutionAcademicTerm; + + public QuizCourseExecutionDto() { + } + + public QuizCourseExecutionDto(QuizCourseExecution quizcourseexecution) { + this.courseExecutionAggregateId = quizcourseexecution.getCourseExecutionAggregateId(); + this.courseExecutionName = quizcourseexecution.getCourseExecutionName(); + this.courseExecutionAcronym = quizcourseexecution.getCourseExecutionAcronym(); + this.courseExecutionAcademicTerm = quizcourseexecution.getCourseExecutionAcademicTerm(); + } + + public Integer getCourseExecutionAggregateId() { + return courseExecutionAggregateId; + } + + public void setCourseExecutionAggregateId(Integer courseExecutionAggregateId) { + this.courseExecutionAggregateId = courseExecutionAggregateId; + } + + public String getCourseExecutionName() { + return courseExecutionName; + } + + public void setCourseExecutionName(String courseExecutionName) { + this.courseExecutionName = courseExecutionName; + } + + public String getCourseExecutionAcronym() { + return courseExecutionAcronym; + } + + public void setCourseExecutionAcronym(String courseExecutionAcronym) { + this.courseExecutionAcronym = courseExecutionAcronym; + } + + public String getCourseExecutionAcademicTerm() { + return courseExecutionAcademicTerm; + } + + public void setCourseExecutionAcademicTerm(String courseExecutionAcademicTerm) { + this.courseExecutionAcademicTerm = courseExecutionAcademicTerm; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCustomRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCustomRepository.java new file mode 100644 index 000000000..ffb68ee75 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCustomRepository.java @@ -0,0 +1,9 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate; + +import java.util.Optional; +import java.util.List; +import java.util.Set; + +public interface QuizCustomRepository { + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizDto.java new file mode 100644 index 000000000..edc6fa31a --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizDto.java @@ -0,0 +1,136 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Set; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class QuizDto implements Serializable { + private Integer aggregateId; + private String title; + private String description; + private String quizType; + private LocalDateTime availableDate; + private LocalDateTime conclusionDate; + private Integer numberOfQuestions; + private Object courseExecution; + private Object questions; + private Object options; + private Integer version; + private AggregateState state; + + public QuizDto() { + } + + public QuizDto(Quiz quiz) { + this.aggregateId = quiz.getAggregateId(); + this.title = quiz.getTitle(); + this.description = quiz.getDescription(); + this.quizType = quiz.getQuizType(); + this.availableDate = quiz.getAvailableDate(); + this.conclusionDate = quiz.getConclusionDate(); + this.numberOfQuestions = quiz.getNumberOfQuestions(); + this.courseExecution = quiz.getCourseExecution(); + this.questions = quiz.getQuestions(); + this.options = quiz.getOptions(); + this.version = quiz.getVersion(); + this.state = quiz.getState(); + } + + public Integer getAggregateId() { + return aggregateId; + } + + public void setAggregateId(Integer aggregateId) { + this.aggregateId = aggregateId; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getQuizType() { + return quizType; + } + + public void setQuizType(String quizType) { + this.quizType = quizType; + } + + public LocalDateTime getAvailableDate() { + return availableDate; + } + + public void setAvailableDate(LocalDateTime availableDate) { + this.availableDate = availableDate; + } + + public LocalDateTime getConclusionDate() { + return conclusionDate; + } + + public void setConclusionDate(LocalDateTime conclusionDate) { + this.conclusionDate = conclusionDate; + } + + public Integer getNumberOfQuestions() { + return numberOfQuestions; + } + + public void setNumberOfQuestions(Integer numberOfQuestions) { + this.numberOfQuestions = numberOfQuestions; + } + + public Object getCourseExecution() { + return courseExecution; + } + + public void setCourseExecution(Object courseExecution) { + this.courseExecution = courseExecution; + } + + public Object getQuestions() { + return questions; + } + + public void setQuestions(Object questions) { + this.questions = questions; + } + + public Object getOptions() { + return options; + } + + public void setOptions(Object options) { + this.options = options; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public AggregateState getState() { + return state; + } + + public void setState(AggregateState state) { + this.state = state; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizFactory.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizFactory.java new file mode 100644 index 000000000..428df90ef --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizFactory.java @@ -0,0 +1,39 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate; + +import org.springframework.stereotype.Service; +import pt.ulisboa.tecnico.socialsoftware.ms.causal.aggregate.CausalAggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWorkService; +import pt.ulisboa.tecnico.socialsoftware.ms.sagas.aggregate.SagaAggregate; + +@Service +public class QuizFactory { + + public Quiz createQuiz(Integer aggregateId, QuizDto quizDto) { + // Factory method implementation - create root entity directly + // Extract properties from DTO and create the root entity + return new Quiz( + quizDto.getTitle(), + quizDto.getDescription(), + quizDto.getQuizType(), + quizDto.getAvailableDate(), + quizDto.getConclusionDate(), + quizDto.getNumberOfQuestions(), + quizDto.getCourseExecution(), + quizDto.getQuestions(), + quizDto.getOptions() + ); + } + + public Quiz createQuizFromExisting(Quiz existingQuiz) { + // Create a copy of the existing aggregate + if (existingQuiz instanceof Quiz) { + return new Quiz((Quiz) existingQuiz); + } + throw new IllegalArgumentException("Unknown aggregate type"); + } + + public QuizDto createQuizDto(Quiz quiz) { + return new QuizDto((Quiz) quiz); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizOption.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizOption.java new file mode 100644 index 000000000..fc00400cb --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizOption.java @@ -0,0 +1,49 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class QuizOption { + private Integer optionNumber; + private String content; + private Boolean isCorrect; + + public QuizOption(Integer optionNumber, String content, Boolean isCorrect) { + this.optionNumber = optionNumber; + this.content = content; + this.isCorrect = isCorrect; + } + + public QuizOption(QuizOption other) { + // Copy constructor + } + + + public Integer getOptionNumber() { + return optionNumber; + } + + public void setOptionNumber(Integer optionNumber) { + this.optionNumber = optionNumber; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Boolean isIsCorrect() { + return isCorrect; + } + + public void setIsCorrect(Boolean isCorrect) { + this.isCorrect = isCorrect; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizOptionDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizOptionDto.java new file mode 100644 index 000000000..fc04ae0e7 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizOptionDto.java @@ -0,0 +1,45 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class QuizOptionDto implements Serializable { + private Integer optionNumber; + private String content; + private boolean isCorrect; + + public QuizOptionDto() { + } + + public QuizOptionDto(QuizOption quizoption) { + this.optionNumber = quizoption.getOptionNumber(); + this.content = quizoption.getContent(); + this.isCorrect = quizoption.isIsCorrect(); + } + + public Integer getOptionNumber() { + return optionNumber; + } + + public void setOptionNumber(Integer optionNumber) { + this.optionNumber = optionNumber; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public boolean isIsCorrect() { + return isCorrect; + } + + public void setIsCorrect(Boolean isCorrect) { + this.isCorrect = isCorrect; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestion.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestion.java new file mode 100644 index 000000000..5cab1fbd8 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestion.java @@ -0,0 +1,59 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class QuizQuestion { + private Integer questionId; + private String questionTitle; + private String questionContent; + private Integer order; + + public QuizQuestion(Integer questionId, String questionTitle, String questionContent, Integer order) { + this.questionId = questionId; + this.questionTitle = questionTitle; + this.questionContent = questionContent; + this.order = order; + } + + public QuizQuestion(QuizQuestion other) { + // Copy constructor + } + + + public Integer getQuestionId() { + return questionId; + } + + public void setQuestionId(Integer questionId) { + this.questionId = questionId; + } + + public String getQuestionTitle() { + return questionTitle; + } + + public void setQuestionTitle(String questionTitle) { + this.questionTitle = questionTitle; + } + + public String getQuestionContent() { + return questionContent; + } + + public void setQuestionContent(String questionContent) { + this.questionContent = questionContent; + } + + public Integer getOrder() { + return order; + } + + public void setOrder(Integer order) { + this.order = order; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestionDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestionDto.java new file mode 100644 index 000000000..961fc4060 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestionDto.java @@ -0,0 +1,55 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class QuizQuestionDto implements Serializable { + private Integer questionId; + private String questionTitle; + private String questionContent; + private Integer order; + + public QuizQuestionDto() { + } + + public QuizQuestionDto(QuizQuestion quizquestion) { + this.questionId = quizquestion.getQuestionId(); + this.questionTitle = quizquestion.getQuestionTitle(); + this.questionContent = quizquestion.getQuestionContent(); + this.order = quizquestion.getOrder(); + } + + public Integer getQuestionId() { + return questionId; + } + + public void setQuestionId(Integer questionId) { + this.questionId = questionId; + } + + public String getQuestionTitle() { + return questionTitle; + } + + public void setQuestionTitle(String questionTitle) { + this.questionTitle = questionTitle; + } + + public String getQuestionContent() { + return questionContent; + } + + public void setQuestionContent(String questionContent) { + this.questionContent = questionContent; + } + + public Integer getOrder() { + return order; + } + + public void setOrder(Integer order) { + this.order = order; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizRepository.java new file mode 100644 index 000000000..51af055ee --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizRepository.java @@ -0,0 +1,22 @@ +package com.generated.microservices.answers.microservices.quiz.aggregate; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import jakarta.transaction.Transactional; + +@Repository +@Transactional +public interface QuizRepository extends JpaRepository { + @Query(value = "select quiz.id from Quiz quiz where quiz.title = :title AND quiz.state = 'ACTIVE' AND quiz.sagaState = 'NOT_IN_SAGA'") + Optional findQuizIdByTitleForSaga(String title); + + @Query(value = "select quiz.id from Quiz quiz where quiz.description = :description AND quiz.state = 'ACTIVE' AND quiz.sagaState = 'NOT_IN_SAGA'") + Optional findQuizIdByDescriptionForSaga(String description); + + @Query(value = "select quiz.id from Quiz quiz where quiz.quizType = :quizType AND quiz.state = 'ACTIVE' AND quiz.sagaState = 'NOT_IN_SAGA'") + Optional findQuizIdByQuizTypeForSaga(String quizType); + + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/QuizEventHandling.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/QuizEventHandling.java new file mode 100644 index 000000000..145cdc3d0 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/QuizEventHandling.java @@ -0,0 +1,192 @@ +package com.generated.microservices.answers.microservices.quiz.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + + +@Component +public class QuizEventHandling { +private static final Logger logger = LoggerFactory.getLogger(QuizEventHandling.class); + +private final QuizService quizService; +private final QuizRepository quizRepository; + +public QuizEventHandling(QuizService quizService, QuizRepository +quizRepository) { +this.quizService = quizService; +this.quizRepository = quizRepository; +} + +@EventListener +public void handleCreated(QuizCreatedEvent event) { +try { +logger.info("Processing Created event for Quiz with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Created": +processCreatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing QuizCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process QuizCreatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncCreated(QuizCreatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Created event for Quiz with ID: {}", event.getAggregateId()); + processAsyncCreatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of QuizCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleUpdated(QuizUpdatedEvent event) { +try { +logger.info("Processing Updated event for Quiz with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Updated": +processUpdatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing QuizUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process QuizUpdatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncUpdated(QuizUpdatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Updated event for Quiz with ID: {}", event.getAggregateId()); + processAsyncUpdatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of QuizUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleDeleted(QuizDeletedEvent event) { +try { +logger.info("Processing Deleted event for Quiz with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Deleted": +processDeletedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing QuizDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process QuizDeletedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncDeleted(QuizDeletedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Deleted event for Quiz with ID: {}", event.getAggregateId()); + processAsyncDeletedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of QuizDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + + + // Event processing methods + private void processCreatedEvent(QuizCreatedEvent event) { + // TODO: Implement Created event processing logic + logger.debug("Processing Created event for Quiz ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle creation event - might trigger welcome workflows, notifications, etc. + handleCreationEvent(event); + } + + private void processAsyncCreatedEvent(QuizCreatedEvent event) { + // TODO: Implement async Created event processing logic + logger.debug("Async processing Created event for Quiz ID: {}", event.getAggregateId()); + } + + private void processUpdatedEvent(QuizUpdatedEvent event) { + // TODO: Implement Updated event processing logic + logger.debug("Processing Updated event for Quiz ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle update event - might trigger validation, sync with external systems, etc. + handleUpdateEvent(event); + } + + private void processAsyncUpdatedEvent(QuizUpdatedEvent event) { + // TODO: Implement async Updated event processing logic + logger.debug("Async processing Updated event for Quiz ID: {}", event.getAggregateId()); + } + + private void processDeletedEvent(QuizDeletedEvent event) { + // TODO: Implement Deleted event processing logic + logger.debug("Processing Deleted event for Quiz ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle deletion event - might trigger cleanup, archival, notifications, etc. + handleDeletionEvent(event); + } + + private void processAsyncDeletedEvent(QuizDeletedEvent event) { + // TODO: Implement async Deleted event processing logic + logger.debug("Async processing Deleted event for Quiz ID: {}", event.getAggregateId()); + } + + + // Helper methods for specific event types + private void handleCreationEvent(Object event) { + // TODO: Implement creation-specific logic + logger.debug("Handling creation event"); + } + + private void handleUpdateEvent(Object event) { + // TODO: Implement update-specific logic + logger.debug("Handling update event"); + } + + private void handleDeletionEvent(Object event) { + // TODO: Implement deletion-specific logic + logger.debug("Handling deletion event"); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/CreatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/CreatedHandler.java new file mode 100644 index 000000000..7a8fa75aa --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/CreatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.quiz.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.QuizEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class CreatedHandler extends QuizEventHandler { + +public CreatedHandler(QuizRepository quizRepository, +QuizEventProcessing quizEventProcessing) { +super(quizRepository, quizEventProcessing); +} + +@EventListener +public void handleCreated(QuizCreatedEvent event) { +try { +// Handle Created event for Quiz +quizEventProcessing.processCreated(event); +} catch (Exception e) { +logger.error("Error handling QuizCreatedEvent", e); +throw new EventProcessingException("Failed to handle QuizCreatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/DeletedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/DeletedHandler.java new file mode 100644 index 000000000..53e7080d9 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/DeletedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.quiz.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.QuizEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class DeletedHandler extends QuizEventHandler { + +public DeletedHandler(QuizRepository quizRepository, +QuizEventProcessing quizEventProcessing) { +super(quizRepository, quizEventProcessing); +} + +@EventListener +public void handleDeleted(QuizDeletedEvent event) { +try { +// Handle Deleted event for Quiz +quizEventProcessing.processDeleted(event); +} catch (Exception e) { +logger.error("Error handling QuizDeletedEvent", e); +throw new EventProcessingException("Failed to handle QuizDeletedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/QuizEventHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/QuizEventHandler.java new file mode 100644 index 000000000..e1ec87005 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/QuizEventHandler.java @@ -0,0 +1,38 @@ +package com.generated.microservices.answers.microservices.quiz.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.QuizEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +public abstract class QuizEventHandler extends EventHandler { +private QuizRepository quizRepository; +protected QuizEventProcessing quizEventProcessing; + +public QuizEventHandler(QuizRepository quizRepository, +QuizEventProcessing quizEventProcessing) { +this.quizRepository = quizRepository; +this.quizEventProcessing = quizEventProcessing; +} + +public Set getAggregateIds() { + return + quizRepository.findAll().stream().map(Quiz::getAggregateId).collect(Collectors.toSet()); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/UpdatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/UpdatedHandler.java new file mode 100644 index 000000000..ffe3013a4 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/UpdatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.quiz.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.QuizEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class UpdatedHandler extends QuizEventHandler { + +public UpdatedHandler(QuizRepository quizRepository, +QuizEventProcessing quizEventProcessing) { +super(quizRepository, quizEventProcessing); +} + +@EventListener +public void handleUpdated(QuizUpdatedEvent event) { +try { +// Handle Updated event for Quiz +quizEventProcessing.processUpdated(event); +} catch (Exception e) { +logger.error("Error handling QuizUpdatedEvent", e); +throw new EventProcessingException("Failed to handle QuizUpdatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizCreatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizCreatedEvent.java new file mode 100644 index 000000000..ba69cb680 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizCreatedEvent.java @@ -0,0 +1,129 @@ +package com.generated.microservices.answers.microservices.quiz.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class QuizCreatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String title; +private final String description; +private final String quizType; +private final LocalDateTime availableDate; +private final LocalDateTime conclusionDate; +private final Integer numberOfQuestions; +private final Object courseExecution; +private final Object questions; +private final Object options; + +public QuizCreatedEvent(Object source, Long aggregateId, String title, String description, String quizType, LocalDateTime availableDate, LocalDateTime conclusionDate, Integer numberOfQuestions, Object courseExecution, Object questions, Object options) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Created"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.title = title; +this.description = description; +this.quizType = quizType; +this.availableDate = availableDate; +this.conclusionDate = conclusionDate; +this.numberOfQuestions = numberOfQuestions; +this.courseExecution = courseExecution; +this.questions = questions; +this.options = options; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getTitle() { +return title; +} + +public String getDescription() { +return description; +} + +public String getQuizType() { +return quizType; +} + +public LocalDateTime getAvailableDate() { +return availableDate; +} + +public LocalDateTime getConclusionDate() { +return conclusionDate; +} + +public Integer getNumberOfQuestions() { +return numberOfQuestions; +} + +public Object getCourseExecution() { +return courseExecution; +} + +public Object getQuestions() { +return questions; +} + +public Object getOptions() { +return options; +} + +@Override +public String toString() { +return "QuizCreatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", title=" + title + +", description=" + description + +", quizType=" + quizType + +", availableDate=" + availableDate + +", conclusionDate=" + conclusionDate + +", numberOfQuestions=" + numberOfQuestions + +", courseExecution=" + courseExecution + +", questions=" + questions + +", options=" + options + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizDeletedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizDeletedEvent.java new file mode 100644 index 000000000..80e85e527 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizDeletedEvent.java @@ -0,0 +1,129 @@ +package com.generated.microservices.answers.microservices.quiz.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class QuizDeletedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String title; +private final String description; +private final String quizType; +private final LocalDateTime availableDate; +private final LocalDateTime conclusionDate; +private final Integer numberOfQuestions; +private final Object courseExecution; +private final Object questions; +private final Object options; + +public QuizDeletedEvent(Object source, Long aggregateId, String title, String description, String quizType, LocalDateTime availableDate, LocalDateTime conclusionDate, Integer numberOfQuestions, Object courseExecution, Object questions, Object options) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Deleted"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.title = title; +this.description = description; +this.quizType = quizType; +this.availableDate = availableDate; +this.conclusionDate = conclusionDate; +this.numberOfQuestions = numberOfQuestions; +this.courseExecution = courseExecution; +this.questions = questions; +this.options = options; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getTitle() { +return title; +} + +public String getDescription() { +return description; +} + +public String getQuizType() { +return quizType; +} + +public LocalDateTime getAvailableDate() { +return availableDate; +} + +public LocalDateTime getConclusionDate() { +return conclusionDate; +} + +public Integer getNumberOfQuestions() { +return numberOfQuestions; +} + +public Object getCourseExecution() { +return courseExecution; +} + +public Object getQuestions() { +return questions; +} + +public Object getOptions() { +return options; +} + +@Override +public String toString() { +return "QuizDeletedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", title=" + title + +", description=" + description + +", quizType=" + quizType + +", availableDate=" + availableDate + +", conclusionDate=" + conclusionDate + +", numberOfQuestions=" + numberOfQuestions + +", courseExecution=" + courseExecution + +", questions=" + questions + +", options=" + options + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizUpdatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizUpdatedEvent.java new file mode 100644 index 000000000..bf0d5001d --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizUpdatedEvent.java @@ -0,0 +1,129 @@ +package com.generated.microservices.answers.microservices.quiz.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class QuizUpdatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String title; +private final String description; +private final String quizType; +private final LocalDateTime availableDate; +private final LocalDateTime conclusionDate; +private final Integer numberOfQuestions; +private final Object courseExecution; +private final Object questions; +private final Object options; + +public QuizUpdatedEvent(Object source, Long aggregateId, String title, String description, String quizType, LocalDateTime availableDate, LocalDateTime conclusionDate, Integer numberOfQuestions, Object courseExecution, Object questions, Object options) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Updated"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.title = title; +this.description = description; +this.quizType = quizType; +this.availableDate = availableDate; +this.conclusionDate = conclusionDate; +this.numberOfQuestions = numberOfQuestions; +this.courseExecution = courseExecution; +this.questions = questions; +this.options = options; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getTitle() { +return title; +} + +public String getDescription() { +return description; +} + +public String getQuizType() { +return quizType; +} + +public LocalDateTime getAvailableDate() { +return availableDate; +} + +public LocalDateTime getConclusionDate() { +return conclusionDate; +} + +public Integer getNumberOfQuestions() { +return numberOfQuestions; +} + +public Object getCourseExecution() { +return courseExecution; +} + +public Object getQuestions() { +return questions; +} + +public Object getOptions() { +return options; +} + +@Override +public String toString() { +return "QuizUpdatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", title=" + title + +", description=" + description + +", quizType=" + quizType + +", availableDate=" + availableDate + +", conclusionDate=" + conclusionDate + +", numberOfQuestions=" + numberOfQuestions + +", courseExecution=" + courseExecution + +", questions=" + questions + +", options=" + options + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/SubscribesCreated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/SubscribesCreated.java new file mode 100644 index 000000000..eba2313df --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/SubscribesCreated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.quiz.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class CreatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(CreatedSubscription.class); + +private final QuizService quizService; + +public CreatedSubscription(QuizService quizService) { +this.quizService = quizService; +} + +@EventListener +@Async +public void handleCreated(QuizCreatedEvent event) { +try { +logger.info("Handling Created event for Quiz with ID: {}", event.getAggregateId()); + +// Process the Created event +processCreated(event); + +} catch (Exception e) { +logger.error("Error handling QuizCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitCreated(QuizCreatedEvent event) { +try { +logger.info("Post-commit handling Created event for Quiz with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessCreated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of QuizCreatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processCreated(QuizCreatedEvent event) { +// TODO: Implement Created event processing logic +switch (event.getEventType()) { +case "Created": +handleCreatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessCreated(QuizCreatedEvent event) { +// TODO: Implement post-transaction Created event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Created event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleCreatedLogic(QuizCreatedEvent event) { +// TODO: Implement specific Created business logic +logger.debug("Processing Created logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/SubscribesDeleted.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/SubscribesDeleted.java new file mode 100644 index 000000000..6e999fbfd --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/SubscribesDeleted.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.quiz.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class DeletedSubscription { +private static final Logger logger = LoggerFactory.getLogger(DeletedSubscription.class); + +private final QuizService quizService; + +public DeletedSubscription(QuizService quizService) { +this.quizService = quizService; +} + +@EventListener +@Async +public void handleDeleted(QuizDeletedEvent event) { +try { +logger.info("Handling Deleted event for Quiz with ID: {}", event.getAggregateId()); + +// Process the Deleted event +processDeleted(event); + +} catch (Exception e) { +logger.error("Error handling QuizDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitDeleted(QuizDeletedEvent event) { +try { +logger.info("Post-commit handling Deleted event for Quiz with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessDeleted(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of QuizDeletedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processDeleted(QuizDeletedEvent event) { +// TODO: Implement Deleted event processing logic +switch (event.getEventType()) { +case "Deleted": +handleDeletedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessDeleted(QuizDeletedEvent event) { +// TODO: Implement post-transaction Deleted event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Deleted event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleDeletedLogic(QuizDeletedEvent event) { +// TODO: Implement specific Deleted business logic +logger.debug("Processing Deleted logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/SubscribesUpdated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/SubscribesUpdated.java new file mode 100644 index 000000000..6839acf94 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/SubscribesUpdated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.quiz.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.quiz.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class UpdatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(UpdatedSubscription.class); + +private final QuizService quizService; + +public UpdatedSubscription(QuizService quizService) { +this.quizService = quizService; +} + +@EventListener +@Async +public void handleUpdated(QuizUpdatedEvent event) { +try { +logger.info("Handling Updated event for Quiz with ID: {}", event.getAggregateId()); + +// Process the Updated event +processUpdated(event); + +} catch (Exception e) { +logger.error("Error handling QuizUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitUpdated(QuizUpdatedEvent event) { +try { +logger.info("Post-commit handling Updated event for Quiz with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessUpdated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of QuizUpdatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processUpdated(QuizUpdatedEvent event) { +// TODO: Implement Updated event processing logic +switch (event.getEventType()) { +case "Updated": +handleUpdatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessUpdated(QuizUpdatedEvent event) { +// TODO: Implement post-transaction Updated event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Updated event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleUpdatedLogic(QuizUpdatedEvent event) { +// TODO: Implement specific Updated business logic +logger.debug("Processing Updated logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/service/QuizService.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/service/QuizService.java new file mode 100644 index 000000000..1374283ec --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/service/QuizService.java @@ -0,0 +1,264 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.QuizCourseExecution; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.QuizQuestion; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.quiz.aggregate.QuizOption; +import pt.ulisboa.tecnico.socialsoftware.ms.exception.*; + +import java.util.*; +import java.util.stream.Collectors; + +import java.util.List; +import java.util.stream.Collectors; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.UserDto; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.AnswersException; + + +@Service +@Transactional +public class QuizService { + private static final Logger logger = LoggerFactory.getLogger(QuizService.class); + + @Autowired + private QuizRepository quizRepository; + + @Autowired + private QuizFactory quizFactory; + + public QuizService() {} + + // CRUD Operations + public QuizDto createQuiz(String title, String description, String quizType, LocalDateTime availableDate, LocalDateTime conclusionDate, Integer numberOfQuestions, Object courseExecution, Object questions, Object options) { + try { + Quiz quiz = new Quiz(title, description, quizType, availableDate, conclusionDate, numberOfQuestions, courseExecution, questions, options); + quiz = quizRepository.save(quiz); + return new QuizDto(quiz); + } catch (Exception e) { + throw new AnswersException("Error creating quiz: " + e.getMessage()); + } + } + + public QuizDto getQuizById(Integer id) { + try { + Quiz quiz = (Quiz) quizRepository.findById(id) + .orElseThrow(() -> new AnswersException("Quiz not found with id: " + id)); + return new QuizDto(quiz); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error retrieving quiz: " + e.getMessage()); + } + } + + public List getAllQuizs() { + try { + return quizRepository.findAll().stream() + .map(entity -> new QuizDto((Quiz) entity)) + .collect(Collectors.toList()); + } catch (Exception e) { + throw new AnswersException("Error retrieving all quizs: " + e.getMessage()); + } + } + + public QuizDto updateQuiz(Integer id, QuizDto quizDto) { + try { + Quiz quiz = (Quiz) quizRepository.findById(id) + .orElseThrow(() -> new AnswersException("Quiz not found with id: " + id)); + + if (quizDto.getTitle() != null) { + quiz.setTitle(quizDto.getTitle()); + } + if (quizDto.getDescription() != null) { + quiz.setDescription(quizDto.getDescription()); + } + if (quizDto.getQuizType() != null) { + quiz.setQuizType(quizDto.getQuizType()); + } + if (quizDto.getAvailableDate() != null) { + quiz.setAvailableDate(quizDto.getAvailableDate()); + } + if (quizDto.getConclusionDate() != null) { + quiz.setConclusionDate(quizDto.getConclusionDate()); + } + if (quizDto.getNumberOfQuestions() != null) { + quiz.setNumberOfQuestions(quizDto.getNumberOfQuestions()); + } + if (quizDto.getCourseExecution() != null) { + quiz.setCourseExecution(quizDto.getCourseExecution()); + } + if (quizDto.getQuestions() != null) { + quiz.setQuestions(quizDto.getQuestions()); + } + if (quizDto.getOptions() != null) { + quiz.setOptions(quizDto.getOptions()); + } + + quiz = quizRepository.save(quiz); + return new QuizDto(quiz); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error updating quiz: " + e.getMessage()); + } + } + + public void deleteQuiz(Integer id) { + try { + if (!quizRepository.existsById(id)) { + throw new AnswersException("Quiz not found with id: " + id); + } + quizRepository.deleteById(id); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error deleting quiz: " + e.getMessage()); + } + } + + // Business Methods + @Transactional + public Object getAvailableQuizzes(Integer id, UnitOfWork unitOfWork) { + try { + Quiz quiz = quizRepository.findById(id) + .orElseThrow(() -> new AnswersException("Quiz not found with id: " + id)); + + // Business logic for getAvailableQuizzes + Object result = quiz.getAvailableQuizzes(); + quizRepository.save(quiz); + return result; + } catch (Exception e) { + throw new AnswersException("Error in getAvailableQuizzes: " + e.getMessage()); + } + } + + @Transactional + public Object getCompletedQuizzes(Integer id, UnitOfWork unitOfWork) { + try { + Quiz quiz = quizRepository.findById(id) + .orElseThrow(() -> new AnswersException("Quiz not found with id: " + id)); + + // Business logic for getCompletedQuizzes + Object result = quiz.getCompletedQuizzes(); + quizRepository.save(quiz); + return result; + } catch (Exception e) { + throw new AnswersException("Error in getCompletedQuizzes: " + e.getMessage()); + } + } + + @Transactional + public Object searchQuizzesByTitle(Integer id, String title, UnitOfWork unitOfWork) { + try { + Quiz quiz = quizRepository.findById(id) + .orElseThrow(() -> new AnswersException("Quiz not found with id: " + id)); + + // Business logic for searchQuizzesByTitle + Object result = quiz.searchQuizzesByTitle(); + quizRepository.save(quiz); + return result; + } catch (Exception e) { + throw new AnswersException("Error in searchQuizzesByTitle: " + e.getMessage()); + } + } + + // Custom Workflow Methods + @Transactional + public void invalidateQuiz(Integer quizId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for invalidateQuiz + throw new UnsupportedOperationException("Workflow invalidateQuiz not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow invalidateQuiz: " + e.getMessage()); + } + } + + @Transactional + public void removeCourseExecution(Integer courseExecutionId, Integer quizId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for removeCourseExecution + throw new UnsupportedOperationException("Workflow removeCourseExecution not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow removeCourseExecution: " + e.getMessage()); + } + } + + @Transactional + public void removeQuestion(Integer questionId, Integer quizId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for removeQuestion + throw new UnsupportedOperationException("Workflow removeQuestion not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow removeQuestion: " + e.getMessage()); + } + } + + @Transactional + public void updateQuestion(Integer questionId, String questionTitle, String questionContent, Integer quizId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for updateQuestion + throw new UnsupportedOperationException("Workflow updateQuestion not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow updateQuestion: " + e.getMessage()); + } + } + + @Transactional + public void removeUser(Integer userAggregateId, Integer quizId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for removeUser + throw new UnsupportedOperationException("Workflow removeUser not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow removeUser: " + e.getMessage()); + } + } + + // Query methods disabled - repository methods not implemented + + // Event Processing Methods + private void publishQuizCreatedEvent(Quiz quiz) { + try { + // TODO: Implement event publishing for QuizCreated + // eventPublisher.publishEvent(new QuizCreatedEvent(quiz)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish QuizCreatedEvent", e); + } + } + + private void publishQuizUpdatedEvent(Quiz quiz) { + try { + // TODO: Implement event publishing for QuizUpdated + // eventPublisher.publishEvent(new QuizUpdatedEvent(quiz)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish QuizUpdatedEvent", e); + } + } + + private void publishQuizDeletedEvent(Long quizId) { + try { + // TODO: Implement event publishing for QuizDeleted + // eventPublisher.publishEvent(new QuizDeletedEvent(quizId)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish QuizDeletedEvent", e); + } + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/Topic.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/Topic.java new file mode 100644 index 000000000..b1b513707 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/Topic.java @@ -0,0 +1,81 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +@Entity +public class Topic extends Aggregate { + @Id + private String name; + private Object course; + private LocalDateTime creationDate; + + public Topic(String name, Object course, LocalDateTime creationDate) { + this.name = name; + this.course = course; + this.creationDate = creationDate; + } + + public Topic(Topic other) { + // Copy constructor + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Object getCourse() { + return course; + } + + public void setCourse(Object course) { + this.course = course; + } + + public LocalDateTime getCreationDate() { + return creationDate; + } + + public void setCreationDate(LocalDateTime creationDate) { + this.creationDate = creationDate; + } + public Object createTopic(String name, Object course, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getTopicById(Integer topicId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getAllTopics(UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object getTopicsByCourse(Integer courseId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object updateTopic(Integer topicId, String name, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + + public Object deleteTopic(Integer topicId, UnitOfWork unitOfWork) { + + return null; // TODO: Implement method + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCourse.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCourse.java new file mode 100644 index 000000000..2d779278b --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCourse.java @@ -0,0 +1,49 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class TopicCourse { + private Integer courseAggregateId; + private String courseName; + private String courseAcronym; + + public TopicCourse(Integer courseAggregateId, String courseName, String courseAcronym) { + this.courseAggregateId = courseAggregateId; + this.courseName = courseName; + this.courseAcronym = courseAcronym; + } + + public TopicCourse(TopicCourse other) { + // Copy constructor + } + + + public Integer getCourseAggregateId() { + return courseAggregateId; + } + + public void setCourseAggregateId(Integer courseAggregateId) { + this.courseAggregateId = courseAggregateId; + } + + public String getCourseName() { + return courseName; + } + + public void setCourseName(String courseName) { + this.courseName = courseName; + } + + public String getCourseAcronym() { + return courseAcronym; + } + + public void setCourseAcronym(String courseAcronym) { + this.courseAcronym = courseAcronym; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCourseDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCourseDto.java new file mode 100644 index 000000000..7e4365215 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCourseDto.java @@ -0,0 +1,45 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class TopicCourseDto implements Serializable { + private Integer courseAggregateId; + private String courseName; + private String courseAcronym; + + public TopicCourseDto() { + } + + public TopicCourseDto(TopicCourse topiccourse) { + this.courseAggregateId = topiccourse.getCourseAggregateId(); + this.courseName = topiccourse.getCourseName(); + this.courseAcronym = topiccourse.getCourseAcronym(); + } + + public Integer getCourseAggregateId() { + return courseAggregateId; + } + + public void setCourseAggregateId(Integer courseAggregateId) { + this.courseAggregateId = courseAggregateId; + } + + public String getCourseName() { + return courseName; + } + + public void setCourseName(String courseName) { + this.courseName = courseName; + } + + public String getCourseAcronym() { + return courseAcronym; + } + + public void setCourseAcronym(String courseAcronym) { + this.courseAcronym = courseAcronym; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCustomRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCustomRepository.java new file mode 100644 index 000000000..879dddbb4 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCustomRepository.java @@ -0,0 +1,9 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate; + +import java.util.Optional; +import java.util.List; +import java.util.Set; + +public interface TopicCustomRepository { + Optional findTopicIdByName(String topicName); +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicDto.java new file mode 100644 index 000000000..8135c9508 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicDto.java @@ -0,0 +1,75 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class TopicDto implements Serializable { + private Integer aggregateId; + private String name; + private Object course; + private LocalDateTime creationDate; + private Integer version; + private AggregateState state; + + public TopicDto() { + } + + public TopicDto(Topic topic) { + this.aggregateId = topic.getAggregateId(); + this.name = topic.getName(); + this.course = topic.getCourse(); + this.creationDate = topic.getCreationDate(); + this.version = topic.getVersion(); + this.state = topic.getState(); + } + + public Integer getAggregateId() { + return aggregateId; + } + + public void setAggregateId(Integer aggregateId) { + this.aggregateId = aggregateId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Object getCourse() { + return course; + } + + public void setCourse(Object course) { + this.course = course; + } + + public LocalDateTime getCreationDate() { + return creationDate; + } + + public void setCreationDate(LocalDateTime creationDate) { + this.creationDate = creationDate; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public AggregateState getState() { + return state; + } + + public void setState(AggregateState state) { + this.state = state; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicFactory.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicFactory.java new file mode 100644 index 000000000..0c96df18b --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicFactory.java @@ -0,0 +1,33 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate; + +import org.springframework.stereotype.Service; +import pt.ulisboa.tecnico.socialsoftware.ms.causal.aggregate.CausalAggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWorkService; +import pt.ulisboa.tecnico.socialsoftware.ms.sagas.aggregate.SagaAggregate; + +@Service +public class TopicFactory { + + public Topic createTopic(Integer aggregateId, TopicDto topicDto) { + // Factory method implementation - create root entity directly + // Extract properties from DTO and create the root entity + return new Topic( + topicDto.getName(), + topicDto.getCourse(), + topicDto.getCreationDate() + ); + } + + public Topic createTopicFromExisting(Topic existingTopic) { + // Create a copy of the existing aggregate + if (existingTopic instanceof Topic) { + return new Topic((Topic) existingTopic); + } + throw new IllegalArgumentException("Unknown aggregate type"); + } + + public TopicDto createTopicDto(Topic topic) { + return new TopicDto((Topic) topic); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicRepository.java new file mode 100644 index 000000000..c626bab7a --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicRepository.java @@ -0,0 +1,16 @@ +package com.generated.microservices.answers.microservices.topic.aggregate; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import jakarta.transaction.Transactional; + +@Repository +@Transactional +public interface TopicRepository extends JpaRepository { + @Query(value = "select topic.id from Topic topic where topic.name = :name AND topic.state = 'ACTIVE' AND topic.sagaState = 'NOT_IN_SAGA'") + Optional findTopicIdByNameForSaga(String name); + + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/TopicEventHandling.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/TopicEventHandling.java new file mode 100644 index 000000000..50104abd1 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/TopicEventHandling.java @@ -0,0 +1,192 @@ +package com.generated.microservices.answers.microservices.topic.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + + +@Component +public class TopicEventHandling { +private static final Logger logger = LoggerFactory.getLogger(TopicEventHandling.class); + +private final TopicService topicService; +private final TopicRepository topicRepository; + +public TopicEventHandling(TopicService topicService, TopicRepository +topicRepository) { +this.topicService = topicService; +this.topicRepository = topicRepository; +} + +@EventListener +public void handleCreated(TopicCreatedEvent event) { +try { +logger.info("Processing Created event for Topic with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Created": +processCreatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing TopicCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process TopicCreatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncCreated(TopicCreatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Created event for Topic with ID: {}", event.getAggregateId()); + processAsyncCreatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of TopicCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleUpdated(TopicUpdatedEvent event) { +try { +logger.info("Processing Updated event for Topic with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Updated": +processUpdatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing TopicUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process TopicUpdatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncUpdated(TopicUpdatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Updated event for Topic with ID: {}", event.getAggregateId()); + processAsyncUpdatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of TopicUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleDeleted(TopicDeletedEvent event) { +try { +logger.info("Processing Deleted event for Topic with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Deleted": +processDeletedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing TopicDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process TopicDeletedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncDeleted(TopicDeletedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Deleted event for Topic with ID: {}", event.getAggregateId()); + processAsyncDeletedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of TopicDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + + + // Event processing methods + private void processCreatedEvent(TopicCreatedEvent event) { + // TODO: Implement Created event processing logic + logger.debug("Processing Created event for Topic ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle creation event - might trigger welcome workflows, notifications, etc. + handleCreationEvent(event); + } + + private void processAsyncCreatedEvent(TopicCreatedEvent event) { + // TODO: Implement async Created event processing logic + logger.debug("Async processing Created event for Topic ID: {}", event.getAggregateId()); + } + + private void processUpdatedEvent(TopicUpdatedEvent event) { + // TODO: Implement Updated event processing logic + logger.debug("Processing Updated event for Topic ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle update event - might trigger validation, sync with external systems, etc. + handleUpdateEvent(event); + } + + private void processAsyncUpdatedEvent(TopicUpdatedEvent event) { + // TODO: Implement async Updated event processing logic + logger.debug("Async processing Updated event for Topic ID: {}", event.getAggregateId()); + } + + private void processDeletedEvent(TopicDeletedEvent event) { + // TODO: Implement Deleted event processing logic + logger.debug("Processing Deleted event for Topic ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle deletion event - might trigger cleanup, archival, notifications, etc. + handleDeletionEvent(event); + } + + private void processAsyncDeletedEvent(TopicDeletedEvent event) { + // TODO: Implement async Deleted event processing logic + logger.debug("Async processing Deleted event for Topic ID: {}", event.getAggregateId()); + } + + + // Helper methods for specific event types + private void handleCreationEvent(Object event) { + // TODO: Implement creation-specific logic + logger.debug("Handling creation event"); + } + + private void handleUpdateEvent(Object event) { + // TODO: Implement update-specific logic + logger.debug("Handling update event"); + } + + private void handleDeletionEvent(Object event) { + // TODO: Implement deletion-specific logic + logger.debug("Handling deletion event"); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/CreatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/CreatedHandler.java new file mode 100644 index 000000000..4341b8a17 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/CreatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.topic.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.TopicEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class CreatedHandler extends TopicEventHandler { + +public CreatedHandler(TopicRepository topicRepository, +TopicEventProcessing topicEventProcessing) { +super(topicRepository, topicEventProcessing); +} + +@EventListener +public void handleCreated(TopicCreatedEvent event) { +try { +// Handle Created event for Topic +topicEventProcessing.processCreated(event); +} catch (Exception e) { +logger.error("Error handling TopicCreatedEvent", e); +throw new EventProcessingException("Failed to handle TopicCreatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/DeletedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/DeletedHandler.java new file mode 100644 index 000000000..cccb05596 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/DeletedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.topic.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.TopicEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class DeletedHandler extends TopicEventHandler { + +public DeletedHandler(TopicRepository topicRepository, +TopicEventProcessing topicEventProcessing) { +super(topicRepository, topicEventProcessing); +} + +@EventListener +public void handleDeleted(TopicDeletedEvent event) { +try { +// Handle Deleted event for Topic +topicEventProcessing.processDeleted(event); +} catch (Exception e) { +logger.error("Error handling TopicDeletedEvent", e); +throw new EventProcessingException("Failed to handle TopicDeletedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/TopicEventHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/TopicEventHandler.java new file mode 100644 index 000000000..39696528c --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/TopicEventHandler.java @@ -0,0 +1,38 @@ +package com.generated.microservices.answers.microservices.topic.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.TopicEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +public abstract class TopicEventHandler extends EventHandler { +private TopicRepository topicRepository; +protected TopicEventProcessing topicEventProcessing; + +public TopicEventHandler(TopicRepository topicRepository, +TopicEventProcessing topicEventProcessing) { +this.topicRepository = topicRepository; +this.topicEventProcessing = topicEventProcessing; +} + +public Set getAggregateIds() { + return + topicRepository.findAll().stream().map(Topic::getAggregateId).collect(Collectors.toSet()); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/UpdatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/UpdatedHandler.java new file mode 100644 index 000000000..26fe2a82d --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/UpdatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.topic.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.TopicEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class UpdatedHandler extends TopicEventHandler { + +public UpdatedHandler(TopicRepository topicRepository, +TopicEventProcessing topicEventProcessing) { +super(topicRepository, topicEventProcessing); +} + +@EventListener +public void handleUpdated(TopicUpdatedEvent event) { +try { +// Handle Updated event for Topic +topicEventProcessing.processUpdated(event); +} catch (Exception e) { +logger.error("Error handling TopicUpdatedEvent", e); +throw new EventProcessingException("Failed to handle TopicUpdatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicCreatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicCreatedEvent.java new file mode 100644 index 000000000..c2839b193 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicCreatedEvent.java @@ -0,0 +1,87 @@ +package com.generated.microservices.answers.microservices.topic.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class TopicCreatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String name; +private final Object course; +private final LocalDateTime creationDate; + +public TopicCreatedEvent(Object source, Long aggregateId, String name, Object course, LocalDateTime creationDate) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Created"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.name = name; +this.course = course; +this.creationDate = creationDate; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getName() { +return name; +} + +public Object getCourse() { +return course; +} + +public LocalDateTime getCreationDate() { +return creationDate; +} + +@Override +public String toString() { +return "TopicCreatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", name=" + name + +", course=" + course + +", creationDate=" + creationDate + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicDeletedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicDeletedEvent.java new file mode 100644 index 000000000..cfb08086d --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicDeletedEvent.java @@ -0,0 +1,87 @@ +package com.generated.microservices.answers.microservices.topic.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class TopicDeletedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String name; +private final Object course; +private final LocalDateTime creationDate; + +public TopicDeletedEvent(Object source, Long aggregateId, String name, Object course, LocalDateTime creationDate) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Deleted"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.name = name; +this.course = course; +this.creationDate = creationDate; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getName() { +return name; +} + +public Object getCourse() { +return course; +} + +public LocalDateTime getCreationDate() { +return creationDate; +} + +@Override +public String toString() { +return "TopicDeletedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", name=" + name + +", course=" + course + +", creationDate=" + creationDate + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicUpdatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicUpdatedEvent.java new file mode 100644 index 000000000..ed662b6ac --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicUpdatedEvent.java @@ -0,0 +1,87 @@ +package com.generated.microservices.answers.microservices.topic.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class TopicUpdatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String name; +private final Object course; +private final LocalDateTime creationDate; + +public TopicUpdatedEvent(Object source, Long aggregateId, String name, Object course, LocalDateTime creationDate) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Updated"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.name = name; +this.course = course; +this.creationDate = creationDate; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getName() { +return name; +} + +public Object getCourse() { +return course; +} + +public LocalDateTime getCreationDate() { +return creationDate; +} + +@Override +public String toString() { +return "TopicUpdatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", name=" + name + +", course=" + course + +", creationDate=" + creationDate + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/SubscribesCreated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/SubscribesCreated.java new file mode 100644 index 000000000..7e998f78e --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/SubscribesCreated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.topic.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class CreatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(CreatedSubscription.class); + +private final TopicService topicService; + +public CreatedSubscription(TopicService topicService) { +this.topicService = topicService; +} + +@EventListener +@Async +public void handleCreated(TopicCreatedEvent event) { +try { +logger.info("Handling Created event for Topic with ID: {}", event.getAggregateId()); + +// Process the Created event +processCreated(event); + +} catch (Exception e) { +logger.error("Error handling TopicCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitCreated(TopicCreatedEvent event) { +try { +logger.info("Post-commit handling Created event for Topic with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessCreated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of TopicCreatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processCreated(TopicCreatedEvent event) { +// TODO: Implement Created event processing logic +switch (event.getEventType()) { +case "Created": +handleCreatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessCreated(TopicCreatedEvent event) { +// TODO: Implement post-transaction Created event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Created event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleCreatedLogic(TopicCreatedEvent event) { +// TODO: Implement specific Created business logic +logger.debug("Processing Created logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/SubscribesDeleted.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/SubscribesDeleted.java new file mode 100644 index 000000000..311bf4c25 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/SubscribesDeleted.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.topic.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class DeletedSubscription { +private static final Logger logger = LoggerFactory.getLogger(DeletedSubscription.class); + +private final TopicService topicService; + +public DeletedSubscription(TopicService topicService) { +this.topicService = topicService; +} + +@EventListener +@Async +public void handleDeleted(TopicDeletedEvent event) { +try { +logger.info("Handling Deleted event for Topic with ID: {}", event.getAggregateId()); + +// Process the Deleted event +processDeleted(event); + +} catch (Exception e) { +logger.error("Error handling TopicDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitDeleted(TopicDeletedEvent event) { +try { +logger.info("Post-commit handling Deleted event for Topic with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessDeleted(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of TopicDeletedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processDeleted(TopicDeletedEvent event) { +// TODO: Implement Deleted event processing logic +switch (event.getEventType()) { +case "Deleted": +handleDeletedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessDeleted(TopicDeletedEvent event) { +// TODO: Implement post-transaction Deleted event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Deleted event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleDeletedLogic(TopicDeletedEvent event) { +// TODO: Implement specific Deleted business logic +logger.debug("Processing Deleted logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/SubscribesUpdated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/SubscribesUpdated.java new file mode 100644 index 000000000..5fa84d132 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/SubscribesUpdated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.topic.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.topic.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class UpdatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(UpdatedSubscription.class); + +private final TopicService topicService; + +public UpdatedSubscription(TopicService topicService) { +this.topicService = topicService; +} + +@EventListener +@Async +public void handleUpdated(TopicUpdatedEvent event) { +try { +logger.info("Handling Updated event for Topic with ID: {}", event.getAggregateId()); + +// Process the Updated event +processUpdated(event); + +} catch (Exception e) { +logger.error("Error handling TopicUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitUpdated(TopicUpdatedEvent event) { +try { +logger.info("Post-commit handling Updated event for Topic with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessUpdated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of TopicUpdatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processUpdated(TopicUpdatedEvent event) { +// TODO: Implement Updated event processing logic +switch (event.getEventType()) { +case "Updated": +handleUpdatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessUpdated(TopicUpdatedEvent event) { +// TODO: Implement post-transaction Updated event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Updated event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleUpdatedLogic(TopicUpdatedEvent event) { +// TODO: Implement specific Updated business logic +logger.debug("Processing Updated logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/service/TopicService.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/service/TopicService.java new file mode 100644 index 000000000..fae4fb1dd --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/service/TopicService.java @@ -0,0 +1,178 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.topic.aggregate.TopicCourse; +import pt.ulisboa.tecnico.socialsoftware.ms.exception.*; + +import java.util.List; +import java.util.stream.Collectors; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.UserDto; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.AnswersException; + + +@Service +@Transactional +public class TopicService { + private static final Logger logger = LoggerFactory.getLogger(TopicService.class); + + @Autowired + private TopicRepository topicRepository; + + @Autowired + private TopicFactory topicFactory; + + public TopicService() {} + + // CRUD Operations + public TopicDto createTopic(String name, Object course, LocalDateTime creationDate) { + try { + Topic topic = new Topic(name, course, creationDate); + topic = topicRepository.save(topic); + return new TopicDto(topic); + } catch (Exception e) { + throw new AnswersException("Error creating topic: " + e.getMessage()); + } + } + + public TopicDto getTopicById(Integer id) { + try { + Topic topic = (Topic) topicRepository.findById(id) + .orElseThrow(() -> new AnswersException("Topic not found with id: " + id)); + return new TopicDto(topic); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error retrieving topic: " + e.getMessage()); + } + } + + public List getAllTopics() { + try { + return topicRepository.findAll().stream() + .map(entity -> new TopicDto((Topic) entity)) + .collect(Collectors.toList()); + } catch (Exception e) { + throw new AnswersException("Error retrieving all topics: " + e.getMessage()); + } + } + + public TopicDto updateTopic(Integer id, TopicDto topicDto) { + try { + Topic topic = (Topic) topicRepository.findById(id) + .orElseThrow(() -> new AnswersException("Topic not found with id: " + id)); + + if (topicDto.getName() != null) { + topic.setName(topicDto.getName()); + } + if (topicDto.getCourse() != null) { + topic.setCourse(topicDto.getCourse()); + } + if (topicDto.getCreationDate() != null) { + topic.setCreationDate(topicDto.getCreationDate()); + } + + topic = topicRepository.save(topic); + return new TopicDto(topic); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error updating topic: " + e.getMessage()); + } + } + + public void deleteTopic(Integer id) { + try { + if (!topicRepository.existsById(id)) { + throw new AnswersException("Topic not found with id: " + id); + } + topicRepository.deleteById(id); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error deleting topic: " + e.getMessage()); + } + } + + // Business Methods + @Transactional + public Object searchTopicsByName(Integer id, String name, UnitOfWork unitOfWork) { + try { + Topic topic = topicRepository.findById(id) + .orElseThrow(() -> new AnswersException("Topic not found with id: " + id)); + + // Business logic for searchTopicsByName + Object result = topic.searchTopicsByName(); + topicRepository.save(topic); + return result; + } catch (Exception e) { + throw new AnswersException("Error in searchTopicsByName: " + e.getMessage()); + } + } + + // Custom Workflow Methods + @Transactional + public void removeCourse(Integer courseId, Integer topicId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for removeCourse + throw new UnsupportedOperationException("Workflow removeCourse not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow removeCourse: " + e.getMessage()); + } + } + + @Transactional + public void updateTopic(Integer topicId, String name, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for updateTopic + throw new UnsupportedOperationException("Workflow updateTopic not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow updateTopic: " + e.getMessage()); + } + } + + // Query methods disabled - repository methods not implemented + + // Event Processing Methods + private void publishTopicCreatedEvent(Topic topic) { + try { + // TODO: Implement event publishing for TopicCreated + // eventPublisher.publishEvent(new TopicCreatedEvent(topic)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish TopicCreatedEvent", e); + } + } + + private void publishTopicUpdatedEvent(Topic topic) { + try { + // TODO: Implement event publishing for TopicUpdated + // eventPublisher.publishEvent(new TopicUpdatedEvent(topic)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish TopicUpdatedEvent", e); + } + } + + private void publishTopicDeletedEvent(Long topicId) { + try { + // TODO: Implement event publishing for TopicDeleted + // eventPublisher.publishEvent(new TopicDeletedEvent(topicId)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish TopicDeletedEvent", e); + } + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/Tournament.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/Tournament.java new file mode 100644 index 000000000..7658d7870 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/Tournament.java @@ -0,0 +1,169 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +@Entity +public class Tournament extends Aggregate { + @Id + private LocalDateTime startTime; + private LocalDateTime endTime; + private Integer numberOfQuestions; + private Boolean cancelled; + private Object tournamentCreator; + private Object tournamentParticipants; + private Object tournamentCourseExecution; + private Object tournamentTopics; + private Object tournamentQuiz; + + public Tournament(LocalDateTime startTime, LocalDateTime endTime, Integer numberOfQuestions, Boolean cancelled, Object tournamentCreator, Object tournamentParticipants, Object tournamentCourseExecution, Object tournamentTopics, Object tournamentQuiz) { + this.startTime = startTime; + this.endTime = endTime; + this.numberOfQuestions = numberOfQuestions; + this.cancelled = cancelled; + this.tournamentCreator = tournamentCreator; + this.tournamentParticipants = tournamentParticipants; + this.tournamentCourseExecution = tournamentCourseExecution; + this.tournamentTopics = tournamentTopics; + this.tournamentQuiz = tournamentQuiz; + } + + public Tournament(Tournament other) { + // Copy constructor + } + + + public LocalDateTime getStartTime() { + return startTime; + } + + public void setStartTime(LocalDateTime startTime) { + this.startTime = startTime; + } + + public LocalDateTime getEndTime() { + return endTime; + } + + public void setEndTime(LocalDateTime endTime) { + this.endTime = endTime; + } + + public Integer getNumberOfQuestions() { + return numberOfQuestions; + } + + public void setNumberOfQuestions(Integer numberOfQuestions) { + this.numberOfQuestions = numberOfQuestions; + } + + public Boolean isCancelled() { + return cancelled; + } + + public void setCancelled(Boolean cancelled) { + this.cancelled = cancelled; + } + + public Object getTournamentCreator() { + return tournamentCreator; + } + + public void setTournamentCreator(Object tournamentCreator) { + this.tournamentCreator = tournamentCreator; + } + + public Object getTournamentParticipants() { + return tournamentParticipants; + } + + public void setTournamentParticipants(Object tournamentParticipants) { + this.tournamentParticipants = tournamentParticipants; + } + + public Object getTournamentCourseExecution() { + return tournamentCourseExecution; + } + + public void setTournamentCourseExecution(Object tournamentCourseExecution) { + this.tournamentCourseExecution = tournamentCourseExecution; + } + + public Object getTournamentTopics() { + return tournamentTopics; + } + + public void setTournamentTopics(Object tournamentTopics) { + this.tournamentTopics = tournamentTopics; + } + + public Object getTournamentQuiz() { + return tournamentQuiz; + } + + public void setTournamentQuiz(Object tournamentQuiz) { + this.tournamentQuiz = tournamentQuiz; + } + public void addParticipant(Object participant) { + Tournament prev = (Tournament) getPrev(); + if (DateHandler.now().isAfter(prev.getStartTime())) { + throw new ProjectException(CANNOT_ADD_PARTICIPANT, getAggregateId()); + } + if (prev != null && prev.isCancelled()) { + throw new ProjectException(CANNOT_UPDATE_TOURNAMENT, getAggregateId()); + } + this.tournamentParticipants.add(participant); + participant.setTournament(this); + } + + public Object findParticipant(Integer userAggregateId) { + return this.tournamentParticipants.stream() + .filter(p -> p.getParticipantAggregateId().equals(userAggregateId)) + .findFirst() + .orElse(null); + } + + public Boolean removeParticipant(Object participant) { + Tournament prev = (Tournament) getPrev(); + if (prev != null) { + if ((prev.getStartTime() != null && DateHandler.now().isAfter(prev.getStartTime())) || prev.isCancelled()) { + throw new ProjectException(CANNOT_UPDATE_TOURNAMENT, getAggregateId()); + } + } + return this.tournamentParticipants.remove(participant); + } + + public Object findTopic(Integer topicAggregateId) { + return getTournamentTopics().stream() + .filter(t -> topicAggregateId.equals(t.getTopicAggregateId())) + .findFirst() + .orElse(null); + } + + public void removeTopic(Object tournamentTopic) { + this.tournamentTopics.remove(tournamentTopic); + } + + public void cancel() { + this.cancelled = true; + } + + public void remove() { + if (getTournamentParticipants().size() > 0) { + throw new ProjectException(CANNOT_DELETE_TOURNAMENT, getAggregateId()); + } + super.remove(); + } + + public void setVersion(Integer version) { + if (this.tournamentQuiz.getQuizVersion() == null) { + this.tournamentQuiz.setQuizVersion(version); + } + super.setVersion(version); + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecution.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecution.java new file mode 100644 index 000000000..430a9478f --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecution.java @@ -0,0 +1,89 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class TournamentCourseExecution { + private Long id; + private Integer courseExecutionAggregateId; + private Integer courseExecutionCourseId; + private String courseExecutionAcronym; + private String courseExecutionStatus; + private Integer courseExecutionVersion; + private Object tournament; + + public TournamentCourseExecution(Long id, Integer courseExecutionAggregateId, Integer courseExecutionCourseId, String courseExecutionAcronym, String courseExecutionStatus, Integer courseExecutionVersion, Object tournament) { + this.id = id; + this.courseExecutionAggregateId = courseExecutionAggregateId; + this.courseExecutionCourseId = courseExecutionCourseId; + this.courseExecutionAcronym = courseExecutionAcronym; + this.courseExecutionStatus = courseExecutionStatus; + this.courseExecutionVersion = courseExecutionVersion; + this.tournament = tournament; + } + + public TournamentCourseExecution(TournamentCourseExecution other) { + // Copy constructor + } + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getCourseExecutionAggregateId() { + return courseExecutionAggregateId; + } + + public void setCourseExecutionAggregateId(Integer courseExecutionAggregateId) { + this.courseExecutionAggregateId = courseExecutionAggregateId; + } + + public Integer getCourseExecutionCourseId() { + return courseExecutionCourseId; + } + + public void setCourseExecutionCourseId(Integer courseExecutionCourseId) { + this.courseExecutionCourseId = courseExecutionCourseId; + } + + public String getCourseExecutionAcronym() { + return courseExecutionAcronym; + } + + public void setCourseExecutionAcronym(String courseExecutionAcronym) { + this.courseExecutionAcronym = courseExecutionAcronym; + } + + public String getCourseExecutionStatus() { + return courseExecutionStatus; + } + + public void setCourseExecutionStatus(String courseExecutionStatus) { + this.courseExecutionStatus = courseExecutionStatus; + } + + public Integer getCourseExecutionVersion() { + return courseExecutionVersion; + } + + public void setCourseExecutionVersion(Integer courseExecutionVersion) { + this.courseExecutionVersion = courseExecutionVersion; + } + + public Object getTournament() { + return tournament; + } + + public void setTournament(Object tournament) { + this.tournament = tournament; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecutionDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecutionDto.java new file mode 100644 index 000000000..6c245b6c9 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecutionDto.java @@ -0,0 +1,75 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class TournamentCourseExecutionDto implements Serializable { + private Integer courseExecutionAggregateId; + private Integer courseExecutionCourseId; + private String courseExecutionAcronym; + private String courseExecutionStatus; + private Integer courseExecutionVersion; + private Object tournament; + + public TournamentCourseExecutionDto() { + } + + public TournamentCourseExecutionDto(TournamentCourseExecution tournamentcourseexecution) { + this.courseExecutionAggregateId = tournamentcourseexecution.getCourseExecutionAggregateId(); + this.courseExecutionCourseId = tournamentcourseexecution.getCourseExecutionCourseId(); + this.courseExecutionAcronym = tournamentcourseexecution.getCourseExecutionAcronym(); + this.courseExecutionStatus = tournamentcourseexecution.getCourseExecutionStatus(); + this.courseExecutionVersion = tournamentcourseexecution.getCourseExecutionVersion(); + this.tournament = tournamentcourseexecution.getTournament(); + } + + public Integer getCourseExecutionAggregateId() { + return courseExecutionAggregateId; + } + + public void setCourseExecutionAggregateId(Integer courseExecutionAggregateId) { + this.courseExecutionAggregateId = courseExecutionAggregateId; + } + + public Integer getCourseExecutionCourseId() { + return courseExecutionCourseId; + } + + public void setCourseExecutionCourseId(Integer courseExecutionCourseId) { + this.courseExecutionCourseId = courseExecutionCourseId; + } + + public String getCourseExecutionAcronym() { + return courseExecutionAcronym; + } + + public void setCourseExecutionAcronym(String courseExecutionAcronym) { + this.courseExecutionAcronym = courseExecutionAcronym; + } + + public String getCourseExecutionStatus() { + return courseExecutionStatus; + } + + public void setCourseExecutionStatus(String courseExecutionStatus) { + this.courseExecutionStatus = courseExecutionStatus; + } + + public Integer getCourseExecutionVersion() { + return courseExecutionVersion; + } + + public void setCourseExecutionVersion(Integer courseExecutionVersion) { + this.courseExecutionVersion = courseExecutionVersion; + } + + public Object getTournament() { + return tournament; + } + + public void setTournament(Object tournament) { + this.tournament = tournament; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCreator.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCreator.java new file mode 100644 index 000000000..385327825 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCreator.java @@ -0,0 +1,102 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.UserDto; + +@Embeddable +public class TournamentCreator { + private Long id; + private Integer creatorAggregateId; + private String creatorName; + private String creatorUsername; + private Integer creatorVersion; + private AggregateState creatorState; + private Object tournament; + + public TournamentCreator(Long id, Integer creatorAggregateId, String creatorName, String creatorUsername, Integer creatorVersion, AggregateState creatorState, Object tournament) { + this.id = id; + this.creatorAggregateId = creatorAggregateId; + this.creatorName = creatorName; + this.creatorUsername = creatorUsername; + this.creatorVersion = creatorVersion; + this.creatorState = creatorState; + this.tournament = tournament; + } + + public TournamentCreator(TournamentCreator other) { + // Copy constructor + } + public TournamentCreator(UserDto userDto) { + setCreatorAggregateId(userDto.getAggregateId()); + setCreatorName(userDto.getName()); + setCreatorUsername(userDto.getUsername()); + setCreatorVersion(userDto.getVersion()); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getCreatorAggregateId() { + return creatorAggregateId; + } + + public void setCreatorAggregateId(Integer creatorAggregateId) { + this.creatorAggregateId = creatorAggregateId; + } + + public String getCreatorName() { + return creatorName; + } + + public void setCreatorName(String creatorName) { + this.creatorName = creatorName; + } + + public String getCreatorUsername() { + return creatorUsername; + } + + public void setCreatorUsername(String creatorUsername) { + this.creatorUsername = creatorUsername; + } + + public Integer getCreatorVersion() { + return creatorVersion; + } + + public void setCreatorVersion(Integer creatorVersion) { + this.creatorVersion = creatorVersion; + } + + public AggregateState getCreatorState() { + return creatorState; + } + + public void setCreatorState(AggregateState creatorState) { + this.creatorState = creatorState; + } + + public Object getTournament() { + return tournament; + } + + public void setTournament(Object tournament) { + this.tournament = tournament; + } + public UserDto buildDto() { + UserDto userDto = new UserDto(); + userDto.setAggregateId(getCreatorAggregateId()); + userDto.setVersion(getCreatorVersion()); + userDto.setName(getCreatorName()); + userDto.setUsername(getCreatorUsername()); + return userDto; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCreatorDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCreatorDto.java new file mode 100644 index 000000000..50f7d49ee --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCreatorDto.java @@ -0,0 +1,75 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class TournamentCreatorDto implements Serializable { + private Integer creatorAggregateId; + private String creatorName; + private String creatorUsername; + private Integer creatorVersion; + private AggregateState creatorState; + private Object tournament; + + public TournamentCreatorDto() { + } + + public TournamentCreatorDto(TournamentCreator tournamentcreator) { + this.creatorAggregateId = tournamentcreator.getCreatorAggregateId(); + this.creatorName = tournamentcreator.getCreatorName(); + this.creatorUsername = tournamentcreator.getCreatorUsername(); + this.creatorVersion = tournamentcreator.getCreatorVersion(); + this.creatorState = tournamentcreator.getCreatorState(); + this.tournament = tournamentcreator.getTournament(); + } + + public Integer getCreatorAggregateId() { + return creatorAggregateId; + } + + public void setCreatorAggregateId(Integer creatorAggregateId) { + this.creatorAggregateId = creatorAggregateId; + } + + public String getCreatorName() { + return creatorName; + } + + public void setCreatorName(String creatorName) { + this.creatorName = creatorName; + } + + public String getCreatorUsername() { + return creatorUsername; + } + + public void setCreatorUsername(String creatorUsername) { + this.creatorUsername = creatorUsername; + } + + public Integer getCreatorVersion() { + return creatorVersion; + } + + public void setCreatorVersion(Integer creatorVersion) { + this.creatorVersion = creatorVersion; + } + + public AggregateState getCreatorState() { + return creatorState; + } + + public void setCreatorState(AggregateState creatorState) { + this.creatorState = creatorState; + } + + public Object getTournament() { + return tournament; + } + + public void setTournament(Object tournament) { + this.tournament = tournament; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCustomRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCustomRepository.java new file mode 100644 index 000000000..0ac999f31 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCustomRepository.java @@ -0,0 +1,9 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import java.util.Optional; +import java.util.List; +import java.util.Set; + +public interface TournamentCustomRepository { + Set findAllRelevantTournamentIds(Integer executionAggregateId); +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentDto.java new file mode 100644 index 000000000..7b82bb00a --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentDto.java @@ -0,0 +1,136 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Set; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class TournamentDto implements Serializable { + private Integer aggregateId; + private LocalDateTime startTime; + private LocalDateTime endTime; + private Integer numberOfQuestions; + private boolean cancelled; + private Object tournamentCreator; + private Object tournamentParticipants; + private Object tournamentCourseExecution; + private Object tournamentTopics; + private Object tournamentQuiz; + private Integer version; + private AggregateState state; + + public TournamentDto() { + } + + public TournamentDto(Tournament tournament) { + this.aggregateId = tournament.getAggregateId(); + this.startTime = tournament.getStartTime(); + this.endTime = tournament.getEndTime(); + this.numberOfQuestions = tournament.getNumberOfQuestions(); + this.cancelled = tournament.isCancelled(); + this.tournamentCreator = tournament.getTournamentCreator(); + this.tournamentParticipants = tournament.getTournamentParticipants(); + this.tournamentCourseExecution = tournament.getTournamentCourseExecution(); + this.tournamentTopics = tournament.getTournamentTopics(); + this.tournamentQuiz = tournament.getTournamentQuiz(); + this.version = tournament.getVersion(); + this.state = tournament.getState(); + } + + public Integer getAggregateId() { + return aggregateId; + } + + public void setAggregateId(Integer aggregateId) { + this.aggregateId = aggregateId; + } + + public LocalDateTime getStartTime() { + return startTime; + } + + public void setStartTime(LocalDateTime startTime) { + this.startTime = startTime; + } + + public LocalDateTime getEndTime() { + return endTime; + } + + public void setEndTime(LocalDateTime endTime) { + this.endTime = endTime; + } + + public Integer getNumberOfQuestions() { + return numberOfQuestions; + } + + public void setNumberOfQuestions(Integer numberOfQuestions) { + this.numberOfQuestions = numberOfQuestions; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(Boolean cancelled) { + this.cancelled = cancelled; + } + + public Object getTournamentCreator() { + return tournamentCreator; + } + + public void setTournamentCreator(Object tournamentCreator) { + this.tournamentCreator = tournamentCreator; + } + + public Object getTournamentParticipants() { + return tournamentParticipants; + } + + public void setTournamentParticipants(Object tournamentParticipants) { + this.tournamentParticipants = tournamentParticipants; + } + + public Object getTournamentCourseExecution() { + return tournamentCourseExecution; + } + + public void setTournamentCourseExecution(Object tournamentCourseExecution) { + this.tournamentCourseExecution = tournamentCourseExecution; + } + + public Object getTournamentTopics() { + return tournamentTopics; + } + + public void setTournamentTopics(Object tournamentTopics) { + this.tournamentTopics = tournamentTopics; + } + + public Object getTournamentQuiz() { + return tournamentQuiz; + } + + public void setTournamentQuiz(Object tournamentQuiz) { + this.tournamentQuiz = tournamentQuiz; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public AggregateState getState() { + return state; + } + + public void setState(AggregateState state) { + this.state = state; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentFactory.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentFactory.java new file mode 100644 index 000000000..e060f5015 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentFactory.java @@ -0,0 +1,39 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import org.springframework.stereotype.Service; +import pt.ulisboa.tecnico.socialsoftware.ms.causal.aggregate.CausalAggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWorkService; +import pt.ulisboa.tecnico.socialsoftware.ms.sagas.aggregate.SagaAggregate; + +@Service +public class TournamentFactory { + + public Tournament createTournament(Integer aggregateId, TournamentDto tournamentDto) { + // Factory method implementation - create root entity directly + // Extract properties from DTO and create the root entity + return new Tournament( + tournamentDto.getStartTime(), + tournamentDto.getEndTime(), + tournamentDto.getNumberOfQuestions(), + tournamentDto.getCancelled(), + tournamentDto.getTournamentCreator(), + tournamentDto.getTournamentParticipants(), + tournamentDto.getTournamentCourseExecution(), + tournamentDto.getTournamentTopics(), + tournamentDto.getTournamentQuiz() + ); + } + + public Tournament createTournamentFromExisting(Tournament existingTournament) { + // Create a copy of the existing aggregate + if (existingTournament instanceof Tournament) { + return new Tournament((Tournament) existingTournament); + } + throw new IllegalArgumentException("Unknown aggregate type"); + } + + public TournamentDto createTournamentDto(Tournament tournament) { + return new TournamentDto((Tournament) tournament); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipant.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipant.java new file mode 100644 index 000000000..3f4aa9754 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipant.java @@ -0,0 +1,110 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +@Embeddable +public class TournamentParticipant { + private Long id; + private Integer participantAggregateId; + private String participantName; + private String participantUsername; + private LocalDateTime enrollTime; + private Object participantAnswer; + private Integer participantVersion; + private AggregateState state; + private Object tournament; + + public TournamentParticipant(Long id, Integer participantAggregateId, String participantName, String participantUsername, LocalDateTime enrollTime, Object participantAnswer, Integer participantVersion, AggregateState state, Object tournament) { + this.id = id; + this.participantAggregateId = participantAggregateId; + this.participantName = participantName; + this.participantUsername = participantUsername; + this.enrollTime = enrollTime; + this.participantAnswer = participantAnswer; + this.participantVersion = participantVersion; + this.state = state; + this.tournament = tournament; + } + + public TournamentParticipant(TournamentParticipant other) { + // Copy constructor + } + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getParticipantAggregateId() { + return participantAggregateId; + } + + public void setParticipantAggregateId(Integer participantAggregateId) { + this.participantAggregateId = participantAggregateId; + } + + public String getParticipantName() { + return participantName; + } + + public void setParticipantName(String participantName) { + this.participantName = participantName; + } + + public String getParticipantUsername() { + return participantUsername; + } + + public void setParticipantUsername(String participantUsername) { + this.participantUsername = participantUsername; + } + + public LocalDateTime getEnrollTime() { + return enrollTime; + } + + public void setEnrollTime(LocalDateTime enrollTime) { + this.enrollTime = enrollTime; + } + + public Object getParticipantAnswer() { + return participantAnswer; + } + + public void setParticipantAnswer(Object participantAnswer) { + this.participantAnswer = participantAnswer; + } + + public Integer getParticipantVersion() { + return participantVersion; + } + + public void setParticipantVersion(Integer participantVersion) { + this.participantVersion = participantVersion; + } + + public AggregateState getState() { + return state; + } + + public void setState(AggregateState state) { + this.state = state; + } + + public Object getTournament() { + return tournament; + } + + public void setTournament(Object tournament) { + this.tournament = tournament; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantDto.java new file mode 100644 index 000000000..496a30ff6 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantDto.java @@ -0,0 +1,96 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class TournamentParticipantDto implements Serializable { + private Integer participantAggregateId; + private String participantName; + private String participantUsername; + private LocalDateTime enrollTime; + private Object participantAnswer; + private Integer participantVersion; + private AggregateState state; + private Object tournament; + + public TournamentParticipantDto() { + } + + public TournamentParticipantDto(TournamentParticipant tournamentparticipant) { + this.participantAggregateId = tournamentparticipant.getParticipantAggregateId(); + this.participantName = tournamentparticipant.getParticipantName(); + this.participantUsername = tournamentparticipant.getParticipantUsername(); + this.enrollTime = tournamentparticipant.getEnrollTime(); + this.participantAnswer = tournamentparticipant.getParticipantAnswer(); + this.participantVersion = tournamentparticipant.getParticipantVersion(); + this.state = tournamentparticipant.getState(); + this.tournament = tournamentparticipant.getTournament(); + } + + public Integer getParticipantAggregateId() { + return participantAggregateId; + } + + public void setParticipantAggregateId(Integer participantAggregateId) { + this.participantAggregateId = participantAggregateId; + } + + public String getParticipantName() { + return participantName; + } + + public void setParticipantName(String participantName) { + this.participantName = participantName; + } + + public String getParticipantUsername() { + return participantUsername; + } + + public void setParticipantUsername(String participantUsername) { + this.participantUsername = participantUsername; + } + + public LocalDateTime getEnrollTime() { + return enrollTime; + } + + public void setEnrollTime(LocalDateTime enrollTime) { + this.enrollTime = enrollTime; + } + + public Object getParticipantAnswer() { + return participantAnswer; + } + + public void setParticipantAnswer(Object participantAnswer) { + this.participantAnswer = participantAnswer; + } + + public Integer getParticipantVersion() { + return participantVersion; + } + + public void setParticipantVersion(Integer participantVersion) { + this.participantVersion = participantVersion; + } + + public AggregateState getState() { + return state; + } + + public void setState(AggregateState state) { + this.state = state; + } + + public Object getTournament() { + return tournament; + } + + public void setTournament(Object tournament) { + this.tournament = tournament; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswer.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswer.java new file mode 100644 index 000000000..1cca4d256 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswer.java @@ -0,0 +1,89 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class TournamentParticipantQuizAnswer { + private Long id; + private Integer quizAnswerAggregateId; + private Integer quizAnswerVersion; + private Boolean answered; + private Integer numberOfAnswered; + private Integer numberOfCorrect; + private Object tournamentParticipant; + + public TournamentParticipantQuizAnswer(Long id, Integer quizAnswerAggregateId, Integer quizAnswerVersion, Boolean answered, Integer numberOfAnswered, Integer numberOfCorrect, Object tournamentParticipant) { + this.id = id; + this.quizAnswerAggregateId = quizAnswerAggregateId; + this.quizAnswerVersion = quizAnswerVersion; + this.answered = answered; + this.numberOfAnswered = numberOfAnswered; + this.numberOfCorrect = numberOfCorrect; + this.tournamentParticipant = tournamentParticipant; + } + + public TournamentParticipantQuizAnswer(TournamentParticipantQuizAnswer other) { + // Copy constructor + } + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getQuizAnswerAggregateId() { + return quizAnswerAggregateId; + } + + public void setQuizAnswerAggregateId(Integer quizAnswerAggregateId) { + this.quizAnswerAggregateId = quizAnswerAggregateId; + } + + public Integer getQuizAnswerVersion() { + return quizAnswerVersion; + } + + public void setQuizAnswerVersion(Integer quizAnswerVersion) { + this.quizAnswerVersion = quizAnswerVersion; + } + + public Boolean isAnswered() { + return answered; + } + + public void setAnswered(Boolean answered) { + this.answered = answered; + } + + public Integer getNumberOfAnswered() { + return numberOfAnswered; + } + + public void setNumberOfAnswered(Integer numberOfAnswered) { + this.numberOfAnswered = numberOfAnswered; + } + + public Integer getNumberOfCorrect() { + return numberOfCorrect; + } + + public void setNumberOfCorrect(Integer numberOfCorrect) { + this.numberOfCorrect = numberOfCorrect; + } + + public Object getTournamentParticipant() { + return tournamentParticipant; + } + + public void setTournamentParticipant(Object tournamentParticipant) { + this.tournamentParticipant = tournamentParticipant; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswerDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswerDto.java new file mode 100644 index 000000000..f99731e57 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswerDto.java @@ -0,0 +1,75 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class TournamentParticipantQuizAnswerDto implements Serializable { + private Integer quizAnswerAggregateId; + private Integer quizAnswerVersion; + private boolean answered; + private Integer numberOfAnswered; + private Integer numberOfCorrect; + private Object tournamentParticipant; + + public TournamentParticipantQuizAnswerDto() { + } + + public TournamentParticipantQuizAnswerDto(TournamentParticipantQuizAnswer tournamentparticipantquizanswer) { + this.quizAnswerAggregateId = tournamentparticipantquizanswer.getQuizAnswerAggregateId(); + this.quizAnswerVersion = tournamentparticipantquizanswer.getQuizAnswerVersion(); + this.answered = tournamentparticipantquizanswer.isAnswered(); + this.numberOfAnswered = tournamentparticipantquizanswer.getNumberOfAnswered(); + this.numberOfCorrect = tournamentparticipantquizanswer.getNumberOfCorrect(); + this.tournamentParticipant = tournamentparticipantquizanswer.getTournamentParticipant(); + } + + public Integer getQuizAnswerAggregateId() { + return quizAnswerAggregateId; + } + + public void setQuizAnswerAggregateId(Integer quizAnswerAggregateId) { + this.quizAnswerAggregateId = quizAnswerAggregateId; + } + + public Integer getQuizAnswerVersion() { + return quizAnswerVersion; + } + + public void setQuizAnswerVersion(Integer quizAnswerVersion) { + this.quizAnswerVersion = quizAnswerVersion; + } + + public boolean isAnswered() { + return answered; + } + + public void setAnswered(Boolean answered) { + this.answered = answered; + } + + public Integer getNumberOfAnswered() { + return numberOfAnswered; + } + + public void setNumberOfAnswered(Integer numberOfAnswered) { + this.numberOfAnswered = numberOfAnswered; + } + + public Integer getNumberOfCorrect() { + return numberOfCorrect; + } + + public void setNumberOfCorrect(Integer numberOfCorrect) { + this.numberOfCorrect = numberOfCorrect; + } + + public Object getTournamentParticipant() { + return tournamentParticipant; + } + + public void setTournamentParticipant(Object tournamentParticipant) { + this.tournamentParticipant = tournamentParticipant; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuiz.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuiz.java new file mode 100644 index 000000000..8dc7e7490 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuiz.java @@ -0,0 +1,59 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class TournamentQuiz { + private Long id; + private Integer quizAggregateId; + private Integer quizVersion; + private Object tournament; + + public TournamentQuiz(Long id, Integer quizAggregateId, Integer quizVersion, Object tournament) { + this.id = id; + this.quizAggregateId = quizAggregateId; + this.quizVersion = quizVersion; + this.tournament = tournament; + } + + public TournamentQuiz(TournamentQuiz other) { + // Copy constructor + } + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getQuizAggregateId() { + return quizAggregateId; + } + + public void setQuizAggregateId(Integer quizAggregateId) { + this.quizAggregateId = quizAggregateId; + } + + public Integer getQuizVersion() { + return quizVersion; + } + + public void setQuizVersion(Integer quizVersion) { + this.quizVersion = quizVersion; + } + + public Object getTournament() { + return tournament; + } + + public void setTournament(Object tournament) { + this.tournament = tournament; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuizDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuizDto.java new file mode 100644 index 000000000..c6bedb0be --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuizDto.java @@ -0,0 +1,45 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class TournamentQuizDto implements Serializable { + private Integer quizAggregateId; + private Integer quizVersion; + private Object tournament; + + public TournamentQuizDto() { + } + + public TournamentQuizDto(TournamentQuiz tournamentquiz) { + this.quizAggregateId = tournamentquiz.getQuizAggregateId(); + this.quizVersion = tournamentquiz.getQuizVersion(); + this.tournament = tournamentquiz.getTournament(); + } + + public Integer getQuizAggregateId() { + return quizAggregateId; + } + + public void setQuizAggregateId(Integer quizAggregateId) { + this.quizAggregateId = quizAggregateId; + } + + public Integer getQuizVersion() { + return quizVersion; + } + + public void setQuizVersion(Integer quizVersion) { + this.quizVersion = quizVersion; + } + + public Object getTournament() { + return tournament; + } + + public void setTournament(Object tournament) { + this.tournament = tournament; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentRepository.java new file mode 100644 index 000000000..c21ec45d5 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentRepository.java @@ -0,0 +1,13 @@ +package com.generated.microservices.answers.microservices.tournament.aggregate; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import jakarta.transaction.Transactional; + +@Repository +@Transactional +public interface TournamentRepository extends JpaRepository { + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopic.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopic.java new file mode 100644 index 000000000..41f0febdf --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopic.java @@ -0,0 +1,89 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import jakarta.persistence.Embeddable; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Embeddable +public class TournamentTopic { + private Long id; + private Integer topicAggregateId; + private String topicName; + private Integer topicCourseAggregateId; + private Integer topicVersion; + private AggregateState state; + private Object tournament; + + public TournamentTopic(Long id, Integer topicAggregateId, String topicName, Integer topicCourseAggregateId, Integer topicVersion, AggregateState state, Object tournament) { + this.id = id; + this.topicAggregateId = topicAggregateId; + this.topicName = topicName; + this.topicCourseAggregateId = topicCourseAggregateId; + this.topicVersion = topicVersion; + this.state = state; + this.tournament = tournament; + } + + public TournamentTopic(TournamentTopic other) { + // Copy constructor + } + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getTopicAggregateId() { + return topicAggregateId; + } + + public void setTopicAggregateId(Integer topicAggregateId) { + this.topicAggregateId = topicAggregateId; + } + + public String getTopicName() { + return topicName; + } + + public void setTopicName(String topicName) { + this.topicName = topicName; + } + + public Integer getTopicCourseAggregateId() { + return topicCourseAggregateId; + } + + public void setTopicCourseAggregateId(Integer topicCourseAggregateId) { + this.topicCourseAggregateId = topicCourseAggregateId; + } + + public Integer getTopicVersion() { + return topicVersion; + } + + public void setTopicVersion(Integer topicVersion) { + this.topicVersion = topicVersion; + } + + public AggregateState getState() { + return state; + } + + public void setState(AggregateState state) { + this.state = state; + } + + public Object getTournament() { + return tournament; + } + + public void setTournament(Object tournament) { + this.tournament = tournament; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopicDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopicDto.java new file mode 100644 index 000000000..b4684efdb --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopicDto.java @@ -0,0 +1,75 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class TournamentTopicDto implements Serializable { + private Integer topicAggregateId; + private String topicName; + private Integer topicCourseAggregateId; + private Integer topicVersion; + private AggregateState state; + private Object tournament; + + public TournamentTopicDto() { + } + + public TournamentTopicDto(TournamentTopic tournamenttopic) { + this.topicAggregateId = tournamenttopic.getTopicAggregateId(); + this.topicName = tournamenttopic.getTopicName(); + this.topicCourseAggregateId = tournamenttopic.getTopicCourseAggregateId(); + this.topicVersion = tournamenttopic.getTopicVersion(); + this.state = tournamenttopic.getState(); + this.tournament = tournamenttopic.getTournament(); + } + + public Integer getTopicAggregateId() { + return topicAggregateId; + } + + public void setTopicAggregateId(Integer topicAggregateId) { + this.topicAggregateId = topicAggregateId; + } + + public String getTopicName() { + return topicName; + } + + public void setTopicName(String topicName) { + this.topicName = topicName; + } + + public Integer getTopicCourseAggregateId() { + return topicCourseAggregateId; + } + + public void setTopicCourseAggregateId(Integer topicCourseAggregateId) { + this.topicCourseAggregateId = topicCourseAggregateId; + } + + public Integer getTopicVersion() { + return topicVersion; + } + + public void setTopicVersion(Integer topicVersion) { + this.topicVersion = topicVersion; + } + + public AggregateState getState() { + return state; + } + + public void setState(AggregateState state) { + this.state = state; + } + + public Object getTournament() { + return tournament; + } + + public void setTournament(Object tournament) { + this.tournament = tournament; + } + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/TournamentEventHandling.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/TournamentEventHandling.java new file mode 100644 index 000000000..7d22172d2 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/TournamentEventHandling.java @@ -0,0 +1,192 @@ +package com.generated.microservices.answers.microservices.tournament.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + + +@Component +public class TournamentEventHandling { +private static final Logger logger = LoggerFactory.getLogger(TournamentEventHandling.class); + +private final TournamentService tournamentService; +private final TournamentRepository tournamentRepository; + +public TournamentEventHandling(TournamentService tournamentService, TournamentRepository +tournamentRepository) { +this.tournamentService = tournamentService; +this.tournamentRepository = tournamentRepository; +} + +@EventListener +public void handleCreated(TournamentCreatedEvent event) { +try { +logger.info("Processing Created event for Tournament with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Created": +processCreatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing TournamentCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process TournamentCreatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncCreated(TournamentCreatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Created event for Tournament with ID: {}", event.getAggregateId()); + processAsyncCreatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of TournamentCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleUpdated(TournamentUpdatedEvent event) { +try { +logger.info("Processing Updated event for Tournament with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Updated": +processUpdatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing TournamentUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process TournamentUpdatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncUpdated(TournamentUpdatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Updated event for Tournament with ID: {}", event.getAggregateId()); + processAsyncUpdatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of TournamentUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleDeleted(TournamentDeletedEvent event) { +try { +logger.info("Processing Deleted event for Tournament with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Deleted": +processDeletedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing TournamentDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process TournamentDeletedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncDeleted(TournamentDeletedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Deleted event for Tournament with ID: {}", event.getAggregateId()); + processAsyncDeletedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of TournamentDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + + + // Event processing methods + private void processCreatedEvent(TournamentCreatedEvent event) { + // TODO: Implement Created event processing logic + logger.debug("Processing Created event for Tournament ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle creation event - might trigger welcome workflows, notifications, etc. + handleCreationEvent(event); + } + + private void processAsyncCreatedEvent(TournamentCreatedEvent event) { + // TODO: Implement async Created event processing logic + logger.debug("Async processing Created event for Tournament ID: {}", event.getAggregateId()); + } + + private void processUpdatedEvent(TournamentUpdatedEvent event) { + // TODO: Implement Updated event processing logic + logger.debug("Processing Updated event for Tournament ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle update event - might trigger validation, sync with external systems, etc. + handleUpdateEvent(event); + } + + private void processAsyncUpdatedEvent(TournamentUpdatedEvent event) { + // TODO: Implement async Updated event processing logic + logger.debug("Async processing Updated event for Tournament ID: {}", event.getAggregateId()); + } + + private void processDeletedEvent(TournamentDeletedEvent event) { + // TODO: Implement Deleted event processing logic + logger.debug("Processing Deleted event for Tournament ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle deletion event - might trigger cleanup, archival, notifications, etc. + handleDeletionEvent(event); + } + + private void processAsyncDeletedEvent(TournamentDeletedEvent event) { + // TODO: Implement async Deleted event processing logic + logger.debug("Async processing Deleted event for Tournament ID: {}", event.getAggregateId()); + } + + + // Helper methods for specific event types + private void handleCreationEvent(Object event) { + // TODO: Implement creation-specific logic + logger.debug("Handling creation event"); + } + + private void handleUpdateEvent(Object event) { + // TODO: Implement update-specific logic + logger.debug("Handling update event"); + } + + private void handleDeletionEvent(Object event) { + // TODO: Implement deletion-specific logic + logger.debug("Handling deletion event"); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/CreatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/CreatedHandler.java new file mode 100644 index 000000000..4fe5f0c7a --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/CreatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.tournament.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.TournamentEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class CreatedHandler extends TournamentEventHandler { + +public CreatedHandler(TournamentRepository tournamentRepository, +TournamentEventProcessing tournamentEventProcessing) { +super(tournamentRepository, tournamentEventProcessing); +} + +@EventListener +public void handleCreated(TournamentCreatedEvent event) { +try { +// Handle Created event for Tournament +tournamentEventProcessing.processCreated(event); +} catch (Exception e) { +logger.error("Error handling TournamentCreatedEvent", e); +throw new EventProcessingException("Failed to handle TournamentCreatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/DeletedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/DeletedHandler.java new file mode 100644 index 000000000..94e207506 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/DeletedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.tournament.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.TournamentEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class DeletedHandler extends TournamentEventHandler { + +public DeletedHandler(TournamentRepository tournamentRepository, +TournamentEventProcessing tournamentEventProcessing) { +super(tournamentRepository, tournamentEventProcessing); +} + +@EventListener +public void handleDeleted(TournamentDeletedEvent event) { +try { +// Handle Deleted event for Tournament +tournamentEventProcessing.processDeleted(event); +} catch (Exception e) { +logger.error("Error handling TournamentDeletedEvent", e); +throw new EventProcessingException("Failed to handle TournamentDeletedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/TournamentEventHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/TournamentEventHandler.java new file mode 100644 index 000000000..a74017ba2 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/TournamentEventHandler.java @@ -0,0 +1,38 @@ +package com.generated.microservices.answers.microservices.tournament.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.TournamentEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +public abstract class TournamentEventHandler extends EventHandler { +private TournamentRepository tournamentRepository; +protected TournamentEventProcessing tournamentEventProcessing; + +public TournamentEventHandler(TournamentRepository tournamentRepository, +TournamentEventProcessing tournamentEventProcessing) { +this.tournamentRepository = tournamentRepository; +this.tournamentEventProcessing = tournamentEventProcessing; +} + +public Set getAggregateIds() { + return + tournamentRepository.findAll().stream().map(Tournament::getAggregateId).collect(Collectors.toSet()); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/UpdatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/UpdatedHandler.java new file mode 100644 index 000000000..65181d3e1 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/UpdatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.tournament.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.TournamentEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class UpdatedHandler extends TournamentEventHandler { + +public UpdatedHandler(TournamentRepository tournamentRepository, +TournamentEventProcessing tournamentEventProcessing) { +super(tournamentRepository, tournamentEventProcessing); +} + +@EventListener +public void handleUpdated(TournamentUpdatedEvent event) { +try { +// Handle Updated event for Tournament +tournamentEventProcessing.processUpdated(event); +} catch (Exception e) { +logger.error("Error handling TournamentUpdatedEvent", e); +throw new EventProcessingException("Failed to handle TournamentUpdatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentCreatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentCreatedEvent.java new file mode 100644 index 000000000..f20fecf4e --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentCreatedEvent.java @@ -0,0 +1,129 @@ +package com.generated.microservices.answers.microservices.tournament.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class TournamentCreatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final LocalDateTime startTime; +private final LocalDateTime endTime; +private final Integer numberOfQuestions; +private final Boolean cancelled; +private final Object tournamentCreator; +private final Object tournamentParticipants; +private final Object tournamentCourseExecution; +private final Object tournamentTopics; +private final Object tournamentQuiz; + +public TournamentCreatedEvent(Object source, Long aggregateId, LocalDateTime startTime, LocalDateTime endTime, Integer numberOfQuestions, Boolean cancelled, Object tournamentCreator, Object tournamentParticipants, Object tournamentCourseExecution, Object tournamentTopics, Object tournamentQuiz) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Created"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.startTime = startTime; +this.endTime = endTime; +this.numberOfQuestions = numberOfQuestions; +this.cancelled = cancelled; +this.tournamentCreator = tournamentCreator; +this.tournamentParticipants = tournamentParticipants; +this.tournamentCourseExecution = tournamentCourseExecution; +this.tournamentTopics = tournamentTopics; +this.tournamentQuiz = tournamentQuiz; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public LocalDateTime getStartTime() { +return startTime; +} + +public LocalDateTime getEndTime() { +return endTime; +} + +public Integer getNumberOfQuestions() { +return numberOfQuestions; +} + +public Boolean getCancelled() { +return cancelled; +} + +public Object getTournamentCreator() { +return tournamentCreator; +} + +public Object getTournamentParticipants() { +return tournamentParticipants; +} + +public Object getTournamentCourseExecution() { +return tournamentCourseExecution; +} + +public Object getTournamentTopics() { +return tournamentTopics; +} + +public Object getTournamentQuiz() { +return tournamentQuiz; +} + +@Override +public String toString() { +return "TournamentCreatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", startTime=" + startTime + +", endTime=" + endTime + +", numberOfQuestions=" + numberOfQuestions + +", cancelled=" + cancelled + +", tournamentCreator=" + tournamentCreator + +", tournamentParticipants=" + tournamentParticipants + +", tournamentCourseExecution=" + tournamentCourseExecution + +", tournamentTopics=" + tournamentTopics + +", tournamentQuiz=" + tournamentQuiz + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentDeletedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentDeletedEvent.java new file mode 100644 index 000000000..e9b40d115 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentDeletedEvent.java @@ -0,0 +1,129 @@ +package com.generated.microservices.answers.microservices.tournament.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class TournamentDeletedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final LocalDateTime startTime; +private final LocalDateTime endTime; +private final Integer numberOfQuestions; +private final Boolean cancelled; +private final Object tournamentCreator; +private final Object tournamentParticipants; +private final Object tournamentCourseExecution; +private final Object tournamentTopics; +private final Object tournamentQuiz; + +public TournamentDeletedEvent(Object source, Long aggregateId, LocalDateTime startTime, LocalDateTime endTime, Integer numberOfQuestions, Boolean cancelled, Object tournamentCreator, Object tournamentParticipants, Object tournamentCourseExecution, Object tournamentTopics, Object tournamentQuiz) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Deleted"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.startTime = startTime; +this.endTime = endTime; +this.numberOfQuestions = numberOfQuestions; +this.cancelled = cancelled; +this.tournamentCreator = tournamentCreator; +this.tournamentParticipants = tournamentParticipants; +this.tournamentCourseExecution = tournamentCourseExecution; +this.tournamentTopics = tournamentTopics; +this.tournamentQuiz = tournamentQuiz; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public LocalDateTime getStartTime() { +return startTime; +} + +public LocalDateTime getEndTime() { +return endTime; +} + +public Integer getNumberOfQuestions() { +return numberOfQuestions; +} + +public Boolean getCancelled() { +return cancelled; +} + +public Object getTournamentCreator() { +return tournamentCreator; +} + +public Object getTournamentParticipants() { +return tournamentParticipants; +} + +public Object getTournamentCourseExecution() { +return tournamentCourseExecution; +} + +public Object getTournamentTopics() { +return tournamentTopics; +} + +public Object getTournamentQuiz() { +return tournamentQuiz; +} + +@Override +public String toString() { +return "TournamentDeletedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", startTime=" + startTime + +", endTime=" + endTime + +", numberOfQuestions=" + numberOfQuestions + +", cancelled=" + cancelled + +", tournamentCreator=" + tournamentCreator + +", tournamentParticipants=" + tournamentParticipants + +", tournamentCourseExecution=" + tournamentCourseExecution + +", tournamentTopics=" + tournamentTopics + +", tournamentQuiz=" + tournamentQuiz + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentUpdatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentUpdatedEvent.java new file mode 100644 index 000000000..1f485e226 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentUpdatedEvent.java @@ -0,0 +1,129 @@ +package com.generated.microservices.answers.microservices.tournament.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class TournamentUpdatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final LocalDateTime startTime; +private final LocalDateTime endTime; +private final Integer numberOfQuestions; +private final Boolean cancelled; +private final Object tournamentCreator; +private final Object tournamentParticipants; +private final Object tournamentCourseExecution; +private final Object tournamentTopics; +private final Object tournamentQuiz; + +public TournamentUpdatedEvent(Object source, Long aggregateId, LocalDateTime startTime, LocalDateTime endTime, Integer numberOfQuestions, Boolean cancelled, Object tournamentCreator, Object tournamentParticipants, Object tournamentCourseExecution, Object tournamentTopics, Object tournamentQuiz) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Updated"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.startTime = startTime; +this.endTime = endTime; +this.numberOfQuestions = numberOfQuestions; +this.cancelled = cancelled; +this.tournamentCreator = tournamentCreator; +this.tournamentParticipants = tournamentParticipants; +this.tournamentCourseExecution = tournamentCourseExecution; +this.tournamentTopics = tournamentTopics; +this.tournamentQuiz = tournamentQuiz; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public LocalDateTime getStartTime() { +return startTime; +} + +public LocalDateTime getEndTime() { +return endTime; +} + +public Integer getNumberOfQuestions() { +return numberOfQuestions; +} + +public Boolean getCancelled() { +return cancelled; +} + +public Object getTournamentCreator() { +return tournamentCreator; +} + +public Object getTournamentParticipants() { +return tournamentParticipants; +} + +public Object getTournamentCourseExecution() { +return tournamentCourseExecution; +} + +public Object getTournamentTopics() { +return tournamentTopics; +} + +public Object getTournamentQuiz() { +return tournamentQuiz; +} + +@Override +public String toString() { +return "TournamentUpdatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", startTime=" + startTime + +", endTime=" + endTime + +", numberOfQuestions=" + numberOfQuestions + +", cancelled=" + cancelled + +", tournamentCreator=" + tournamentCreator + +", tournamentParticipants=" + tournamentParticipants + +", tournamentCourseExecution=" + tournamentCourseExecution + +", tournamentTopics=" + tournamentTopics + +", tournamentQuiz=" + tournamentQuiz + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/SubscribesCreated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/SubscribesCreated.java new file mode 100644 index 000000000..169075553 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/SubscribesCreated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.tournament.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class CreatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(CreatedSubscription.class); + +private final TournamentService tournamentService; + +public CreatedSubscription(TournamentService tournamentService) { +this.tournamentService = tournamentService; +} + +@EventListener +@Async +public void handleCreated(TournamentCreatedEvent event) { +try { +logger.info("Handling Created event for Tournament with ID: {}", event.getAggregateId()); + +// Process the Created event +processCreated(event); + +} catch (Exception e) { +logger.error("Error handling TournamentCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitCreated(TournamentCreatedEvent event) { +try { +logger.info("Post-commit handling Created event for Tournament with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessCreated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of TournamentCreatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processCreated(TournamentCreatedEvent event) { +// TODO: Implement Created event processing logic +switch (event.getEventType()) { +case "Created": +handleCreatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessCreated(TournamentCreatedEvent event) { +// TODO: Implement post-transaction Created event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Created event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleCreatedLogic(TournamentCreatedEvent event) { +// TODO: Implement specific Created business logic +logger.debug("Processing Created logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/SubscribesDeleted.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/SubscribesDeleted.java new file mode 100644 index 000000000..17cfad79a --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/SubscribesDeleted.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.tournament.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class DeletedSubscription { +private static final Logger logger = LoggerFactory.getLogger(DeletedSubscription.class); + +private final TournamentService tournamentService; + +public DeletedSubscription(TournamentService tournamentService) { +this.tournamentService = tournamentService; +} + +@EventListener +@Async +public void handleDeleted(TournamentDeletedEvent event) { +try { +logger.info("Handling Deleted event for Tournament with ID: {}", event.getAggregateId()); + +// Process the Deleted event +processDeleted(event); + +} catch (Exception e) { +logger.error("Error handling TournamentDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitDeleted(TournamentDeletedEvent event) { +try { +logger.info("Post-commit handling Deleted event for Tournament with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessDeleted(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of TournamentDeletedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processDeleted(TournamentDeletedEvent event) { +// TODO: Implement Deleted event processing logic +switch (event.getEventType()) { +case "Deleted": +handleDeletedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessDeleted(TournamentDeletedEvent event) { +// TODO: Implement post-transaction Deleted event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Deleted event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleDeletedLogic(TournamentDeletedEvent event) { +// TODO: Implement specific Deleted business logic +logger.debug("Processing Deleted logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/SubscribesUpdated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/SubscribesUpdated.java new file mode 100644 index 000000000..74a57678e --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/SubscribesUpdated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.tournament.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.tournament.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class UpdatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(UpdatedSubscription.class); + +private final TournamentService tournamentService; + +public UpdatedSubscription(TournamentService tournamentService) { +this.tournamentService = tournamentService; +} + +@EventListener +@Async +public void handleUpdated(TournamentUpdatedEvent event) { +try { +logger.info("Handling Updated event for Tournament with ID: {}", event.getAggregateId()); + +// Process the Updated event +processUpdated(event); + +} catch (Exception e) { +logger.error("Error handling TournamentUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitUpdated(TournamentUpdatedEvent event) { +try { +logger.info("Post-commit handling Updated event for Tournament with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessUpdated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of TournamentUpdatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processUpdated(TournamentUpdatedEvent event) { +// TODO: Implement Updated event processing logic +switch (event.getEventType()) { +case "Updated": +handleUpdatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessUpdated(TournamentUpdatedEvent event) { +// TODO: Implement post-transaction Updated event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Updated event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleUpdatedLogic(TournamentUpdatedEvent event) { +// TODO: Implement specific Updated business logic +logger.debug("Processing Updated logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/service/TournamentService.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/service/TournamentService.java new file mode 100644 index 000000000..c053f65f2 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/service/TournamentService.java @@ -0,0 +1,309 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.TournamentCourseExecution; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.TournamentCreator; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.TournamentParticipant; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.TournamentParticipantQuizAnswer; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.TournamentTopic; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.tournament.aggregate.TournamentQuiz; +import pt.ulisboa.tecnico.socialsoftware.ms.exception.*; + +import java.util.*; +import java.util.stream.Collectors; + +import java.util.List; +import java.util.stream.Collectors; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.UserDto; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import java.time.LocalDateTime; + +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.AnswersException; + + +@Service +@Transactional +public class TournamentService { + private static final Logger logger = LoggerFactory.getLogger(TournamentService.class); + + @Autowired + private TournamentRepository tournamentRepository; + + @Autowired + private TournamentFactory tournamentFactory; + + public TournamentService() {} + + // CRUD Operations + public TournamentDto createTournament(LocalDateTime startTime, LocalDateTime endTime, Integer numberOfQuestions, Boolean cancelled, Object tournamentCreator, Object tournamentParticipants, Object tournamentCourseExecution, Object tournamentTopics, Object tournamentQuiz) { + try { + Tournament tournament = new Tournament(startTime, endTime, numberOfQuestions, cancelled, tournamentCreator, tournamentParticipants, tournamentCourseExecution, tournamentTopics, tournamentQuiz); + tournament = tournamentRepository.save(tournament); + return new TournamentDto(tournament); + } catch (Exception e) { + throw new AnswersException("Error creating tournament: " + e.getMessage()); + } + } + + public TournamentDto getTournamentById(Integer id) { + try { + Tournament tournament = (Tournament) tournamentRepository.findById(id) + .orElseThrow(() -> new AnswersException("Tournament not found with id: " + id)); + return new TournamentDto(tournament); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error retrieving tournament: " + e.getMessage()); + } + } + + public List getAllTournaments() { + try { + return tournamentRepository.findAll().stream() + .map(entity -> new TournamentDto((Tournament) entity)) + .collect(Collectors.toList()); + } catch (Exception e) { + throw new AnswersException("Error retrieving all tournaments: " + e.getMessage()); + } + } + + public TournamentDto updateTournament(Integer id, TournamentDto tournamentDto) { + try { + Tournament tournament = (Tournament) tournamentRepository.findById(id) + .orElseThrow(() -> new AnswersException("Tournament not found with id: " + id)); + + if (tournamentDto.getStartTime() != null) { + tournament.setStartTime(tournamentDto.getStartTime()); + } + if (tournamentDto.getEndTime() != null) { + tournament.setEndTime(tournamentDto.getEndTime()); + } + if (tournamentDto.getNumberOfQuestions() != null) { + tournament.setNumberOfQuestions(tournamentDto.getNumberOfQuestions()); + } + tournament.setCancelled(tournamentDto.isCancelled()); + if (tournamentDto.getTournamentCreator() != null) { + tournament.setTournamentCreator(tournamentDto.getTournamentCreator()); + } + if (tournamentDto.getTournamentParticipants() != null) { + tournament.setTournamentParticipants(tournamentDto.getTournamentParticipants()); + } + if (tournamentDto.getTournamentCourseExecution() != null) { + tournament.setTournamentCourseExecution(tournamentDto.getTournamentCourseExecution()); + } + if (tournamentDto.getTournamentTopics() != null) { + tournament.setTournamentTopics(tournamentDto.getTournamentTopics()); + } + if (tournamentDto.getTournamentQuiz() != null) { + tournament.setTournamentQuiz(tournamentDto.getTournamentQuiz()); + } + + tournament = tournamentRepository.save(tournament); + return new TournamentDto(tournament); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error updating tournament: " + e.getMessage()); + } + } + + public void deleteTournament(Integer id) { + try { + if (!tournamentRepository.existsById(id)) { + throw new AnswersException("Tournament not found with id: " + id); + } + tournamentRepository.deleteById(id); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error deleting tournament: " + e.getMessage()); + } + } + + // Business Methods + @Transactional + public Object getOpenedTournamentsForCourseExecution(Integer id, Integer courseExecutionId, UnitOfWork unitOfWork) { + try { + Tournament tournament = tournamentRepository.findById(id) + .orElseThrow(() -> new AnswersException("Tournament not found with id: " + id)); + + // Business logic for getOpenedTournamentsForCourseExecution + Object result = tournament.getOpenedTournamentsForCourseExecution(); + tournamentRepository.save(tournament); + return result; + } catch (Exception e) { + throw new AnswersException("Error in getOpenedTournamentsForCourseExecution: " + e.getMessage()); + } + } + + @Transactional + public Object getClosedTournamentsForCourseExecution(Integer id, Integer courseExecutionId, UnitOfWork unitOfWork) { + try { + Tournament tournament = tournamentRepository.findById(id) + .orElseThrow(() -> new AnswersException("Tournament not found with id: " + id)); + + // Business logic for getClosedTournamentsForCourseExecution + Object result = tournament.getClosedTournamentsForCourseExecution(); + tournamentRepository.save(tournament); + return result; + } catch (Exception e) { + throw new AnswersException("Error in getClosedTournamentsForCourseExecution: " + e.getMessage()); + } + } + + @Transactional + public Object getTournamentParticipant(Integer id, Integer userAggregateId) { + try { + Tournament tournament = tournamentRepository.findById(id) + .orElseThrow(() -> new AnswersException("Tournament not found with id: " + id)); + + // Business logic for getTournamentParticipant + Object result = tournament.getTournamentParticipant(); + tournamentRepository.save(tournament); + return result; + } catch (Exception e) { + throw new AnswersException("Error in getTournamentParticipant: " + e.getMessage()); + } + } + + // Custom Workflow Methods + @Transactional + public void addParticipant(UserDto userDto, Integer tournamentId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for addParticipant + throw new UnsupportedOperationException("Workflow addParticipant not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow addParticipant: " + e.getMessage()); + } + } + + @Transactional + public void leaveTournament(Integer userAggregateId, Integer tournamentId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for leaveTournament + throw new UnsupportedOperationException("Workflow leaveTournament not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow leaveTournament: " + e.getMessage()); + } + } + + @Transactional + public void solveQuiz(Integer userAggregateId, Integer tournamentId, Integer quizAnswerId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for solveQuiz + throw new UnsupportedOperationException("Workflow solveQuiz not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow solveQuiz: " + e.getMessage()); + } + } + + @Transactional + public void cancelTournament(Integer tournamentId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for cancelTournament + throw new UnsupportedOperationException("Workflow cancelTournament not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow cancelTournament: " + e.getMessage()); + } + } + + @Transactional + public void reopenTournament(Integer tournamentId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for reopenTournament + throw new UnsupportedOperationException("Workflow reopenTournament not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow reopenTournament: " + e.getMessage()); + } + } + + @Transactional + public void anonymizeUser(Integer userAggregateId, Integer tournamentId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for anonymizeUser + throw new UnsupportedOperationException("Workflow anonymizeUser not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow anonymizeUser: " + e.getMessage()); + } + } + + @Transactional + public void removeUser(Integer userAggregateId, Integer tournamentId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for removeUser + throw new UnsupportedOperationException("Workflow removeUser not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow removeUser: " + e.getMessage()); + } + } + + @Transactional + public void updateTopic(Integer topicAggregateId, Integer tournamentId, String topicName, AggregateState state, Integer version, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for updateTopic + throw new UnsupportedOperationException("Workflow updateTopic not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow updateTopic: " + e.getMessage()); + } + } + + @Transactional + public void removeTopic(Integer topicAggregateId, Integer tournamentId, UnitOfWork unitOfWork) { + try { + // TODO: Implement workflow logic for removeTopic + throw new UnsupportedOperationException("Workflow removeTopic not implemented"); + + } catch (Exception e) { + throw new AnswersException("Error in workflow removeTopic: " + e.getMessage()); + } + } + + // Query methods disabled - repository methods not implemented + + // Event Processing Methods + private void publishTournamentCreatedEvent(Tournament tournament) { + try { + // TODO: Implement event publishing for TournamentCreated + // eventPublisher.publishEvent(new TournamentCreatedEvent(tournament)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish TournamentCreatedEvent", e); + } + } + + private void publishTournamentUpdatedEvent(Tournament tournament) { + try { + // TODO: Implement event publishing for TournamentUpdated + // eventPublisher.publishEvent(new TournamentUpdatedEvent(tournament)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish TournamentUpdatedEvent", e); + } + } + + private void publishTournamentDeletedEvent(Long tournamentId) { + try { + // TODO: Implement event publishing for TournamentDeleted + // eventPublisher.publishEvent(new TournamentDeletedEvent(tournamentId)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish TournamentDeletedEvent", e); + } + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/User.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/User.java new file mode 100644 index 000000000..e87ce1f22 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/User.java @@ -0,0 +1,52 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +@Entity +public class User extends Aggregate { + @Id + private String name; + private String username; + private Boolean active; + + public User(String name, String username, Boolean active) { + this.name = name; + this.username = username; + this.active = active; + } + + public User(User other) { + // Copy constructor + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public Boolean isActive() { + return active; + } + + public void setActive(Boolean active) { + this.active = active; + } + + +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserCustomRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserCustomRepository.java new file mode 100644 index 000000000..4b1b3eb2d --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserCustomRepository.java @@ -0,0 +1,9 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate; + +import java.util.Optional; +import java.util.List; +import java.util.Set; + +public interface UserCustomRepository { + Optional findUserIdByUsername(String username); +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserDto.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserDto.java new file mode 100644 index 000000000..8747296bd --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserDto.java @@ -0,0 +1,74 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate; + +import java.io.Serializable; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; + +public class UserDto implements Serializable { + private Integer aggregateId; + private String name; + private String username; + private boolean active; + private Integer version; + private AggregateState state; + + public UserDto() { + } + + public UserDto(User user) { + this.aggregateId = user.getAggregateId(); + this.name = user.getName(); + this.username = user.getUsername(); + this.active = user.isActive(); + this.version = user.getVersion(); + this.state = user.getState(); + } + + public Integer getAggregateId() { + return aggregateId; + } + + public void setAggregateId(Integer aggregateId) { + this.aggregateId = aggregateId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public boolean isActive() { + return active; + } + + public void setActive(Boolean active) { + this.active = active; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public AggregateState getState() { + return state; + } + + public void setState(AggregateState state) { + this.state = state; + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserFactory.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserFactory.java new file mode 100644 index 000000000..ae5ce5c17 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserFactory.java @@ -0,0 +1,33 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate; + +import org.springframework.stereotype.Service; +import pt.ulisboa.tecnico.socialsoftware.ms.causal.aggregate.CausalAggregate; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWorkService; +import pt.ulisboa.tecnico.socialsoftware.ms.sagas.aggregate.SagaAggregate; + +@Service +public class UserFactory { + + public User createUser(Integer aggregateId, UserDto userDto) { + // Factory method implementation - create root entity directly + // Extract properties from DTO and create the root entity + return new User( + userDto.getName(), + userDto.getUsername(), + userDto.getActive() + ); + } + + public User createUserFromExisting(User existingUser) { + // Create a copy of the existing aggregate + if (existingUser instanceof User) { + return new User((User) existingUser); + } + throw new IllegalArgumentException("Unknown aggregate type"); + } + + public UserDto createUserDto(User user) { + return new UserDto((User) user); + } +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserRepository.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserRepository.java new file mode 100644 index 000000000..d006a2e38 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserRepository.java @@ -0,0 +1,19 @@ +package com.generated.microservices.answers.microservices.user.aggregate; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import jakarta.transaction.Transactional; + +@Repository +@Transactional +public interface UserRepository extends JpaRepository { + @Query(value = "select user.id from User user where user.name = :name AND user.state = 'ACTIVE' AND user.sagaState = 'NOT_IN_SAGA'") + Optional findUserIdByNameForSaga(String name); + + @Query(value = "select user.id from User user where user.username = :username AND user.state = 'ACTIVE' AND user.sagaState = 'NOT_IN_SAGA'") + Optional findUserIdByUsernameForSaga(String username); + + + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/UserEventHandling.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/UserEventHandling.java new file mode 100644 index 000000000..33862a0c3 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/UserEventHandling.java @@ -0,0 +1,192 @@ +package com.generated.microservices.answers.microservices.user.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.user.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.concurrent.CompletableFuture; + + +@Component +public class UserEventHandling { +private static final Logger logger = LoggerFactory.getLogger(UserEventHandling.class); + +private final UserService userService; +private final UserRepository userRepository; + +public UserEventHandling(UserService userService, UserRepository +userRepository) { +this.userService = userService; +this.userRepository = userRepository; +} + +@EventListener +public void handleCreated(UserCreatedEvent event) { +try { +logger.info("Processing Created event for User with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Created": +processCreatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing UserCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process UserCreatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncCreated(UserCreatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Created event for User with ID: {}", event.getAggregateId()); + processAsyncCreatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of UserCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleUpdated(UserUpdatedEvent event) { +try { +logger.info("Processing Updated event for User with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Updated": +processUpdatedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing UserUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process UserUpdatedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncUpdated(UserUpdatedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Updated event for User with ID: {}", event.getAggregateId()); + processAsyncUpdatedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of UserUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + +@EventListener +public void handleDeleted(UserDeletedEvent event) { +try { +logger.info("Processing Deleted event for User with ID: {}", event.getAggregateId()); + +switch (event.getEventType()) { +case "Deleted": +processDeletedEvent(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} + +} catch (Exception e) { +logger.error("Error processing UserDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +throw new EventProcessingException("Failed to process UserDeletedEvent: " + e.getMessage(), e); +} +} + +@Async +@EventListener +public CompletableFuture handleAsyncDeleted(UserDeletedEvent event) { + return CompletableFuture.runAsync(() -> { + try { + logger.info("Async processing Deleted event for User with ID: {}", event.getAggregateId()); + processAsyncDeletedEvent(event); + } catch (Exception e) { + logger.error("Error in async processing of UserDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); + } + }); + } + + + // Event processing methods + private void processCreatedEvent(UserCreatedEvent event) { + // TODO: Implement Created event processing logic + logger.debug("Processing Created event for User ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle creation event - might trigger welcome workflows, notifications, etc. + handleCreationEvent(event); + } + + private void processAsyncCreatedEvent(UserCreatedEvent event) { + // TODO: Implement async Created event processing logic + logger.debug("Async processing Created event for User ID: {}", event.getAggregateId()); + } + + private void processUpdatedEvent(UserUpdatedEvent event) { + // TODO: Implement Updated event processing logic + logger.debug("Processing Updated event for User ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle update event - might trigger validation, sync with external systems, etc. + handleUpdateEvent(event); + } + + private void processAsyncUpdatedEvent(UserUpdatedEvent event) { + // TODO: Implement async Updated event processing logic + logger.debug("Async processing Updated event for User ID: {}", event.getAggregateId()); + } + + private void processDeletedEvent(UserDeletedEvent event) { + // TODO: Implement Deleted event processing logic + logger.debug("Processing Deleted event for User ID: {}", event.getAggregateId()); + + // Example processing based on event type + // Handle deletion event - might trigger cleanup, archival, notifications, etc. + handleDeletionEvent(event); + } + + private void processAsyncDeletedEvent(UserDeletedEvent event) { + // TODO: Implement async Deleted event processing logic + logger.debug("Async processing Deleted event for User ID: {}", event.getAggregateId()); + } + + + // Helper methods for specific event types + private void handleCreationEvent(Object event) { + // TODO: Implement creation-specific logic + logger.debug("Handling creation event"); + } + + private void handleUpdateEvent(Object event) { + // TODO: Implement update-specific logic + logger.debug("Handling update event"); + } + + private void handleDeletionEvent(Object event) { + // TODO: Implement deletion-specific logic + logger.debug("Handling deletion event"); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/CreatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/CreatedHandler.java new file mode 100644 index 000000000..d250585cb --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/CreatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.user.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.user.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.UserEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class CreatedHandler extends UserEventHandler { + +public CreatedHandler(UserRepository userRepository, +UserEventProcessing userEventProcessing) { +super(userRepository, userEventProcessing); +} + +@EventListener +public void handleCreated(UserCreatedEvent event) { +try { +// Handle Created event for User +userEventProcessing.processCreated(event); +} catch (Exception e) { +logger.error("Error handling UserCreatedEvent", e); +throw new EventProcessingException("Failed to handle UserCreatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/DeletedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/DeletedHandler.java new file mode 100644 index 000000000..3056f451b --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/DeletedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.user.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.user.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.UserEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class DeletedHandler extends UserEventHandler { + +public DeletedHandler(UserRepository userRepository, +UserEventProcessing userEventProcessing) { +super(userRepository, userEventProcessing); +} + +@EventListener +public void handleDeleted(UserDeletedEvent event) { +try { +// Handle Deleted event for User +userEventProcessing.processDeleted(event); +} catch (Exception e) { +logger.error("Error handling UserDeletedEvent", e); +throw new EventProcessingException("Failed to handle UserDeletedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/UpdatedHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/UpdatedHandler.java new file mode 100644 index 000000000..aed4c3b82 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/UpdatedHandler.java @@ -0,0 +1,42 @@ +package com.generated.microservices.answers.microservices.user.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.user.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.UserEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +@Component +public class UpdatedHandler extends UserEventHandler { + +public UpdatedHandler(UserRepository userRepository, +UserEventProcessing userEventProcessing) { +super(userRepository, userEventProcessing); +} + +@EventListener +public void handleUpdated(UserUpdatedEvent event) { +try { +// Handle Updated event for User +userEventProcessing.processUpdated(event); +} catch (Exception e) { +logger.error("Error handling UserUpdatedEvent", e); +throw new EventProcessingException("Failed to handle UserUpdatedEvent: " + e.getMessage(), e); +} +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/UserEventHandler.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/UserEventHandler.java new file mode 100644 index 000000000..87ca5a8a1 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/UserEventHandler.java @@ -0,0 +1,38 @@ +package com.generated.microservices.answers.microservices.user.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.user.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.EventHandler; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.service.*; +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.eventProcessing.UserEventProcessing; +import java.util.Set; +import java.util.stream.Collectors; + + +public abstract class UserEventHandler extends EventHandler { +private UserRepository userRepository; +protected UserEventProcessing userEventProcessing; + +public UserEventHandler(UserRepository userRepository, +UserEventProcessing userEventProcessing) { +this.userRepository = userRepository; +this.userEventProcessing = userEventProcessing; +} + +public Set getAggregateIds() { + return + userRepository.findAll().stream().map(User::getAggregateId).collect(Collectors.toSet()); + } + } \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserCreatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserCreatedEvent.java new file mode 100644 index 000000000..c6f8c6d74 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserCreatedEvent.java @@ -0,0 +1,87 @@ +package com.generated.microservices.answers.microservices.user.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.user.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class UserCreatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String name; +private final String username; +private final Boolean active; + +public UserCreatedEvent(Object source, Long aggregateId, String name, String username, Boolean active) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Created"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.name = name; +this.username = username; +this.active = active; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getName() { +return name; +} + +public String getUsername() { +return username; +} + +public Boolean getActive() { +return active; +} + +@Override +public String toString() { +return "UserCreatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", name=" + name + +", username=" + username + +", active=" + active + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserDeletedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserDeletedEvent.java new file mode 100644 index 000000000..fa81f847f --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserDeletedEvent.java @@ -0,0 +1,87 @@ +package com.generated.microservices.answers.microservices.user.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.user.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class UserDeletedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String name; +private final String username; +private final Boolean active; + +public UserDeletedEvent(Object source, Long aggregateId, String name, String username, Boolean active) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Deleted"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.name = name; +this.username = username; +this.active = active; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getName() { +return name; +} + +public String getUsername() { +return username; +} + +public Boolean getActive() { +return active; +} + +@Override +public String toString() { +return "UserDeletedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", name=" + name + +", username=" + username + +", active=" + active + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserUpdatedEvent.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserUpdatedEvent.java new file mode 100644 index 000000000..7be772fe6 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserUpdatedEvent.java @@ -0,0 +1,87 @@ +package com.generated.microservices.answers.microservices.user.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.user.aggregate.*; + +import java.time.LocalDateTime; +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonFormat; + + +public class UserUpdatedEvent extends ApplicationEvent implements Serializable { +private static final long serialVersionUID = 1L; + +private final String eventId; +private final String eventType; + +@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +private final LocalDateTime timestamp; + +private final Long aggregateId; + +private final String name; +private final String username; +private final Boolean active; + +public UserUpdatedEvent(Object source, Long aggregateId, String name, String username, Boolean active) { +super(source); +this.eventId = java.util.UUID.randomUUID().toString(); +this.eventType = "Updated"; +this.timestamp = LocalDateTime.now(); +this.aggregateId = aggregateId; +this.name = name; +this.username = username; +this.active = active; +} + +// Getters +public String getEventId() { +return eventId; +} + +public String getEventType() { +return eventType; +} + +public LocalDateTime getTimestamp() { +return timestamp; +} + +public Long getAggregateId() { +return aggregateId; +} + +public String getName() { +return name; +} + +public String getUsername() { +return username; +} + +public Boolean getActive() { +return active; +} + +@Override +public String toString() { +return "UserUpdatedEvent{" + +"eventId='" + eventId + '\'' + +", eventType='" + eventType + '\'' + +", timestamp=" + timestamp + +", aggregateId=" + aggregateId + +", name=" + name + +", username=" + username + +", active=" + active + +'}'; +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/SubscribesCreated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/SubscribesCreated.java new file mode 100644 index 000000000..f1aa5b607 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/SubscribesCreated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.user.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.user.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class CreatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(CreatedSubscription.class); + +private final UserService userService; + +public CreatedSubscription(UserService userService) { +this.userService = userService; +} + +@EventListener +@Async +public void handleCreated(UserCreatedEvent event) { +try { +logger.info("Handling Created event for User with ID: {}", event.getAggregateId()); + +// Process the Created event +processCreated(event); + +} catch (Exception e) { +logger.error("Error handling UserCreatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitCreated(UserCreatedEvent event) { +try { +logger.info("Post-commit handling Created event for User with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessCreated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of UserCreatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processCreated(UserCreatedEvent event) { +// TODO: Implement Created event processing logic +switch (event.getEventType()) { +case "Created": +handleCreatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessCreated(UserCreatedEvent event) { +// TODO: Implement post-transaction Created event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Created event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleCreatedLogic(UserCreatedEvent event) { +// TODO: Implement specific Created business logic +logger.debug("Processing Created logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/SubscribesDeleted.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/SubscribesDeleted.java new file mode 100644 index 000000000..8471e87f9 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/SubscribesDeleted.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.user.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.user.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class DeletedSubscription { +private static final Logger logger = LoggerFactory.getLogger(DeletedSubscription.class); + +private final UserService userService; + +public DeletedSubscription(UserService userService) { +this.userService = userService; +} + +@EventListener +@Async +public void handleDeleted(UserDeletedEvent event) { +try { +logger.info("Handling Deleted event for User with ID: {}", event.getAggregateId()); + +// Process the Deleted event +processDeleted(event); + +} catch (Exception e) { +logger.error("Error handling UserDeletedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitDeleted(UserDeletedEvent event) { +try { +logger.info("Post-commit handling Deleted event for User with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessDeleted(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of UserDeletedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processDeleted(UserDeletedEvent event) { +// TODO: Implement Deleted event processing logic +switch (event.getEventType()) { +case "Deleted": +handleDeletedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessDeleted(UserDeletedEvent event) { +// TODO: Implement post-transaction Deleted event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Deleted event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleDeletedLogic(UserDeletedEvent event) { +// TODO: Implement specific Deleted business logic +logger.debug("Processing Deleted logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/SubscribesUpdated.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/SubscribesUpdated.java new file mode 100644 index 000000000..3293f2ba6 --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/SubscribesUpdated.java @@ -0,0 +1,86 @@ +package com.generated.microservices.answers.microservices.user.events; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.generated.microservices.answers.microservices.user.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.domain.event.Event; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.*; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.service.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.event.TransactionPhase; + + +@Component +public class UpdatedSubscription { +private static final Logger logger = LoggerFactory.getLogger(UpdatedSubscription.class); + +private final UserService userService; + +public UpdatedSubscription(UserService userService) { +this.userService = userService; +} + +@EventListener +@Async +public void handleUpdated(UserUpdatedEvent event) { +try { +logger.info("Handling Updated event for User with ID: {}", event.getAggregateId()); + +// Process the Updated event +processUpdated(event); + +} catch (Exception e) { +logger.error("Error handling UserUpdatedEvent for aggregate ID: {}", event.getAggregateId(), e); +// Consider implementing retry logic or dead letter queue +} +} + +@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) +public void handleAfterCommitUpdated(UserUpdatedEvent event) { +try { +logger.info("Post-commit handling Updated event for User with ID: {}", +event.getAggregateId()); + +// Post-transaction processing +postProcessUpdated(event); + +} catch (Exception e) { +logger.error("Error in post-commit handling of UserUpdatedEvent for aggregate ID: {}", +event.getAggregateId(), e); +} +} + +private void processUpdated(UserUpdatedEvent event) { +// TODO: Implement Updated event processing logic +switch (event.getEventType()) { +case "Updated": +handleUpdatedLogic(event); +break; +default: +logger.warn("Unknown event type: {}", event.getEventType()); +} +} + +private void postProcessUpdated(UserUpdatedEvent event) { +// TODO: Implement post-transaction Updated event processing +// This could include: +// - Sending notifications +// - Updating external systems +// - Triggering workflows +logger.debug("Post-processing Updated event completed for aggregate ID: {}", event.getAggregateId()); +} + +private void handleUpdatedLogic(UserUpdatedEvent event) { +// TODO: Implement specific Updated business logic +logger.debug("Processing Updated logic for aggregate ID: {}", event.getAggregateId()); +} +} \ No newline at end of file diff --git a/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/service/UserService.java b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/service/UserService.java new file mode 100644 index 000000000..bf4e5cd5d --- /dev/null +++ b/applications/answers/src/main/java/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/service/UserService.java @@ -0,0 +1,138 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.*; + +import pt.ulisboa.tecnico.socialsoftware.ms.exception.*; + +import java.util.List; +import java.util.stream.Collectors; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.user.aggregate.UserDto; +import pt.ulisboa.tecnico.socialsoftware.ms.domain.aggregate.Aggregate.AggregateState; +import pt.ulisboa.tecnico.socialsoftware.ms.coordination.unitOfWork.UnitOfWork; +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.exception.AnswersException; + + +@Service +@Transactional +public class UserService { + private static final Logger logger = LoggerFactory.getLogger(UserService.class); + + @Autowired + private UserRepository userRepository; + + @Autowired + private UserFactory userFactory; + + public UserService() {} + + // CRUD Operations + public UserDto createUser(String name, String username, Boolean active) { + try { + User user = new User(name, username, active); + user = userRepository.save(user); + return new UserDto(user); + } catch (Exception e) { + throw new AnswersException("Error creating user: " + e.getMessage()); + } + } + + public UserDto getUserById(Integer id) { + try { + User user = (User) userRepository.findById(id) + .orElseThrow(() -> new AnswersException("User not found with id: " + id)); + return new UserDto(user); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error retrieving user: " + e.getMessage()); + } + } + + public List getAllUsers() { + try { + return userRepository.findAll().stream() + .map(entity -> new UserDto((User) entity)) + .collect(Collectors.toList()); + } catch (Exception e) { + throw new AnswersException("Error retrieving all users: " + e.getMessage()); + } + } + + public UserDto updateUser(Integer id, UserDto userDto) { + try { + User user = (User) userRepository.findById(id) + .orElseThrow(() -> new AnswersException("User not found with id: " + id)); + + if (userDto.getName() != null) { + user.setName(userDto.getName()); + } + if (userDto.getUsername() != null) { + user.setUsername(userDto.getUsername()); + } + user.setActive(userDto.isActive()); + + user = userRepository.save(user); + return new UserDto(user); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error updating user: " + e.getMessage()); + } + } + + public void deleteUser(Integer id) { + try { + if (!userRepository.existsById(id)) { + throw new AnswersException("User not found with id: " + id); + } + userRepository.deleteById(id); + } catch (AnswersException e) { + throw e; + } catch (Exception e) { + throw new AnswersException("Error deleting user: " + e.getMessage()); + } + } + + // No business methods defined + + // No custom workflows defined + + // Query methods disabled - repository methods not implemented + + // Event Processing Methods + private void publishUserCreatedEvent(User user) { + try { + // TODO: Implement event publishing for UserCreated + // eventPublisher.publishEvent(new UserCreatedEvent(user)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish UserCreatedEvent", e); + } + } + + private void publishUserUpdatedEvent(User user) { + try { + // TODO: Implement event publishing for UserUpdated + // eventPublisher.publishEvent(new UserUpdatedEvent(user)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish UserUpdatedEvent", e); + } + } + + private void publishUserDeletedEvent(Long userId) { + try { + // TODO: Implement event publishing for UserDeleted + // eventPublisher.publishEvent(new UserDeletedEvent(userId)); + } catch (Exception e) { + // Log error but don't fail the transaction + logger.error("Failed to publish UserDeletedEvent", e); + } + } +} \ No newline at end of file diff --git a/applications/answers/src/main/resources/application.properties b/applications/answers/src/main/resources/application.properties new file mode 100644 index 000000000..70650317b --- /dev/null +++ b/applications/answers/src/main/resources/application.properties @@ -0,0 +1,23 @@ +# Application Configuration +spring.application.name=answers +server.port=9115 + +# Database Configuration +spring.datasource.url=jdbc:postgresql://postgres:5432/answers_db +spring.datasource.driver-class-name=org.postgresql.Driver +spring.datasource.username=postgres +spring.datasource.password=password + +# JPA Configuration +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true + +# Event Configuration +spring.events.async=true +spring.events.thread-pool-size=10 + +# Validation Configuration +validation.enabled=true +validation.fail-fast=false diff --git a/applications/answers/src/main/resources/application.yml b/applications/answers/src/main/resources/application.yml new file mode 100644 index 000000000..99ad0440d --- /dev/null +++ b/applications/answers/src/main/resources/application.yml @@ -0,0 +1,26 @@ +# Application Configuration (YAML format) +spring: + application: + name: answers + + datasource: + url: jdbc:postgresql://postgres:5432/answers_db + driver-class-name: org.postgresql.Driver + username: postgres + password: password + + jpa: + database-platform: org.hibernate.dialect.PostgreSQLDialect + hibernate: + ddl-auto: create-drop + show-sql: true + properties: + hibernate: + format_sql: true + +server: + port: 9115 + +events: + async: true + thread-pool-size: 10 diff --git a/applications/answers/src/main/resources/database.properties b/applications/answers/src/main/resources/database.properties new file mode 100644 index 000000000..ebfb1934c --- /dev/null +++ b/applications/answers/src/main/resources/database.properties @@ -0,0 +1,37 @@ +# Database Configuration Properties +# This file contains database-specific settings + +# Connection Pool Settings +spring.datasource.hikari.connection-timeout=20000 +spring.datasource.hikari.minimum-idle=5 +spring.datasource.hikari.maximum-pool-size=20 +spring.datasource.hikari.idle-timeout=300000 +spring.datasource.hikari.max-lifetime=1200000 +spring.datasource.hikari.auto-commit=true + +# JPA/Hibernate Advanced Settings +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.properties.hibernate.id.new_generator_mappings=true +spring.jpa.properties.hibernate.connection.provider_disables_autocommit=false +spring.jpa.properties.hibernate.cache.use_second_level_cache=false +spring.jpa.properties.hibernate.cache.use_query_cache=false +spring.jpa.properties.hibernate.generate_statistics=false +spring.jpa.properties.hibernate.jdbc.batch_size=20 +spring.jpa.properties.hibernate.order_inserts=true +spring.jpa.properties.hibernate.order_updates=true +spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true + +# Transaction Settings +spring.transaction.default-timeout=30 +spring.transaction.rollback-on-commit-failure=true + +# Database Migration Settings (if using Flyway/Liquibase) +spring.flyway.enabled=false +spring.liquibase.enabled=false + +# Development/Testing Settings +spring.jpa.defer-datasource-initialization=true +spring.sql.init.mode=always + +# Performance Monitoring +spring.jpa.properties.hibernate.session.events.log.LOG_QUERIES_SLOWER_THAN_MS=1000 diff --git a/applications/answers/src/main/resources/logback-spring.xml b/applications/answers/src/main/resources/logback-spring.xml new file mode 100644 index 000000000..aa70cdd03 --- /dev/null +++ b/applications/answers/src/main/resources/logback-spring.xml @@ -0,0 +1,86 @@ + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + logs/application.log + + logs/application.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + 512 + 0 + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/SpockTest.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/SpockTest.groovy new file mode 100644 index 000000000..c2066785d --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/SpockTest.groovy @@ -0,0 +1,6 @@ +package pt.ulisboa.tecnico.socialsoftware + +import spock.lang.Specification + +class SpockTest extends Specification { +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/AnswersBeanConfiguration.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/AnswersBeanConfiguration.groovy new file mode 100644 index 000000000..40c6d7ef1 --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/AnswersBeanConfiguration.groovy @@ -0,0 +1,19 @@ +package pt.ulisboa.tecnico.socialsoftware.answers + +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.TestPropertySource +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Primary + +class AnswersBeanConfiguration extends SpockTest { + @Configuration + static class TestConfiguration { + + @Bean + @Primary + RestTemplate restTemplate() { + return Mock(RestTemplate) + } + } +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/AnswersSpockTest.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/AnswersSpockTest.groovy new file mode 100644 index 000000000..f089f3931 --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/AnswersSpockTest.groovy @@ -0,0 +1,27 @@ +package pt.ulisboa.tecnico.socialsoftware.answers + +import java.time.LocalDateTime +import org.springframework.beans.factory.annotation.Autowired +import spock.lang.Specification +import pt.ulisboa.tecnico.socialsoftware.SpockTest +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.functionalities.AnswerFunctionalities +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate.QuizAnswerDto +import pt.ulisboa.tecnico.socialsoftware.ms.utils.BehaviourService +import pt.ulisboa.tecnico.socialsoftware.ms.utils.DateHandler + +class AnswersSpockTest extends SpockTest { + @Autowired + AnswerFunctionalities answerFunctionalities + + def setup() { + BehaviourService.clearBehaviour() + } + + def "should create QuizAnswer"() { + when: + def answerDto = new QuizAnswerDto() + answerFunctionalities.createAnswer(answerDto) + then: + noExceptionThrown() + } +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/AnsweredQuizBuilder.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/AnsweredQuizBuilder.groovy new file mode 100644 index 000000000..32dcb93ef --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/AnsweredQuizBuilder.groovy @@ -0,0 +1,59 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.builders + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.default.aggregate.AnsweredQuiz +import java.time.LocalDateTime + +class AnsweredQuizBuilder extends SpockTest { + private AnsweredQuiz answeredquiz + + static AnsweredQuizBuilder aAnsweredQuiz() { + return new AnsweredQuizBuilder() + } + + AnsweredQuizBuilder() { + this.answeredquiz = new AnsweredQuiz() + // Set default values + this.answeredquiz.setId(1L) + this.answeredquiz.setVersion(1) + this.answeredquiz.setQuizAggregateId(1) + this.answeredquiz.setQuizTitle("Default quizTitle") + this.answeredquiz.setQuizType("Default quizType") + this.answeredquiz.setAvailableDate(LocalDateTime.now()) + this.answeredquiz.setConclusionDate(LocalDateTime.now()) + this.answeredquiz.setNumberOfQuestions(1) + } + + AnsweredQuizBuilder withQuizAggregateId(Integer quizAggregateId) { + this.answeredquiz.setQuizAggregateId(quizAggregateId) + return this + } + + AnsweredQuizBuilder withQuizTitle(String quizTitle) { + this.answeredquiz.setQuizTitle(quizTitle) + return this + } + + AnsweredQuizBuilder withQuizType(String quizType) { + this.answeredquiz.setQuizType(quizType) + return this + } + + AnsweredQuizBuilder withAvailableDate(LocalDateTime availableDate) { + this.answeredquiz.setAvailableDate(availableDate) + return this + } + + AnsweredQuizBuilder withConclusionDate(LocalDateTime conclusionDate) { + this.answeredquiz.setConclusionDate(conclusionDate) + return this + } + + AnsweredQuizBuilder withNumberOfQuestions(Integer numberOfQuestions) { + this.answeredquiz.setNumberOfQuestions(numberOfQuestions) + return this + } + + AnsweredQuiz build() { + return this.answeredquiz + } +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/AnsweredQuizDtoBuilder.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/AnsweredQuizDtoBuilder.groovy new file mode 100644 index 000000000..65928ac59 --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/AnsweredQuizDtoBuilder.groovy @@ -0,0 +1,57 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.builders + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.default.aggregate.AnsweredQuizDto +import java.time.LocalDateTime + +class AnsweredQuizDtoBuilder extends SpockTest { + private AnsweredQuizDto answeredquizDto + + static AnsweredQuizDtoBuilder aAnsweredQuizDto() { + return new AnsweredQuizDtoBuilder() + } + + AnsweredQuizDtoBuilder() { + this.answeredquizDto = new AnsweredQuizDto() + // Set default values + this.answeredquizDto.setQuizAggregateId(1) + this.answeredquizDto.setQuizTitle("Default quizTitle") + this.answeredquizDto.setQuizType("Default quizType") + this.answeredquizDto.setAvailableDate(LocalDateTime.now()) + this.answeredquizDto.setConclusionDate(LocalDateTime.now()) + this.answeredquizDto.setNumberOfQuestions(1) + } + + AnsweredQuizDtoBuilder withQuizAggregateId(Integer quizAggregateId) { + this.answeredquizDto.setQuizAggregateId(quizAggregateId) + return this + } + + AnsweredQuizDtoBuilder withQuizTitle(String quizTitle) { + this.answeredquizDto.setQuizTitle(quizTitle) + return this + } + + AnsweredQuizDtoBuilder withQuizType(String quizType) { + this.answeredquizDto.setQuizType(quizType) + return this + } + + AnsweredQuizDtoBuilder withAvailableDate(LocalDateTime availableDate) { + this.answeredquizDto.setAvailableDate(availableDate) + return this + } + + AnsweredQuizDtoBuilder withConclusionDate(LocalDateTime conclusionDate) { + this.answeredquizDto.setConclusionDate(conclusionDate) + return this + } + + AnsweredQuizDtoBuilder withNumberOfQuestions(Integer numberOfQuestions) { + this.answeredquizDto.setNumberOfQuestions(numberOfQuestions) + return this + } + + AnsweredQuizDto build() { + return this.answeredquizDto + } +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuestionAnswerBuilder.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuestionAnswerBuilder.groovy new file mode 100644 index 000000000..99a6af88d --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuestionAnswerBuilder.groovy @@ -0,0 +1,47 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.builders + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.default.aggregate.QuestionAnswer +import java.time.LocalDateTime + +class QuestionAnswerBuilder extends SpockTest { + private QuestionAnswer questionanswer + + static QuestionAnswerBuilder aQuestionAnswer() { + return new QuestionAnswerBuilder() + } + + QuestionAnswerBuilder() { + this.questionanswer = new QuestionAnswer() + // Set default values + this.questionanswer.setId(1L) + this.questionanswer.setVersion(1) + this.questionanswer.setQuestionId(1) + this.questionanswer.setAnswer("Default answer") + this.questionanswer.setOption("Default option") + this.questionanswer.setAnswerDate(LocalDateTime.now()) + } + + QuestionAnswerBuilder withQuestionId(Integer questionId) { + this.questionanswer.setQuestionId(questionId) + return this + } + + QuestionAnswerBuilder withAnswer(String answer) { + this.questionanswer.setAnswer(answer) + return this + } + + QuestionAnswerBuilder withOption(String option) { + this.questionanswer.setOption(option) + return this + } + + QuestionAnswerBuilder withAnswerDate(LocalDateTime answerDate) { + this.questionanswer.setAnswerDate(answerDate) + return this + } + + QuestionAnswer build() { + return this.questionanswer + } +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuestionAnswerDtoBuilder.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuestionAnswerDtoBuilder.groovy new file mode 100644 index 000000000..a10edd86a --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuestionAnswerDtoBuilder.groovy @@ -0,0 +1,45 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.builders + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.default.aggregate.QuestionAnswerDto +import java.time.LocalDateTime + +class QuestionAnswerDtoBuilder extends SpockTest { + private QuestionAnswerDto questionanswerDto + + static QuestionAnswerDtoBuilder aQuestionAnswerDto() { + return new QuestionAnswerDtoBuilder() + } + + QuestionAnswerDtoBuilder() { + this.questionanswerDto = new QuestionAnswerDto() + // Set default values + this.questionanswerDto.setQuestionId(1) + this.questionanswerDto.setAnswer("Default answer") + this.questionanswerDto.setOption("Default option") + this.questionanswerDto.setAnswerDate(LocalDateTime.now()) + } + + QuestionAnswerDtoBuilder withQuestionId(Integer questionId) { + this.questionanswerDto.setQuestionId(questionId) + return this + } + + QuestionAnswerDtoBuilder withAnswer(String answer) { + this.questionanswerDto.setAnswer(answer) + return this + } + + QuestionAnswerDtoBuilder withOption(String option) { + this.questionanswerDto.setOption(option) + return this + } + + QuestionAnswerDtoBuilder withAnswerDate(LocalDateTime answerDate) { + this.questionanswerDto.setAnswerDate(answerDate) + return this + } + + QuestionAnswerDto build() { + return this.questionanswerDto + } +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerBuilder.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerBuilder.groovy new file mode 100644 index 000000000..8f01cc105 --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerBuilder.groovy @@ -0,0 +1,61 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.builders + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.default.aggregate.QuizAnswer +import java.time.LocalDateTime + +class QuizAnswerBuilder extends SpockTest { + private QuizAnswer quizanswer + + static QuizAnswerBuilder aQuizAnswer() { + return new QuizAnswerBuilder() + } + + QuizAnswerBuilder() { + this.quizanswer = new QuizAnswer() + // Set default values + this.quizanswer.setId(1L) + this.quizanswer.setVersion(1) + this.quizanswer.setAnswerDate(LocalDateTime.now()) + this.quizanswer.setCompletedDate(LocalDateTime.now()) + this.quizanswer.setCompleted(false) + } + + QuizAnswerBuilder withAnswerDate(LocalDateTime answerDate) { + this.quizanswer.setAnswerDate(answerDate) + return this + } + + QuizAnswerBuilder withCompletedDate(LocalDateTime completedDate) { + this.quizanswer.setCompletedDate(completedDate) + return this + } + + QuizAnswerBuilder withCompleted(Boolean completed) { + this.quizanswer.setCompleted(completed) + return this + } + + QuizAnswerBuilder withQuizAnswerStudent(Object quizAnswerStudent) { + this.quizanswer.setQuizAnswerStudent(quizAnswerStudent) + return this + } + + QuizAnswerBuilder withQuizAnswerCourseExecution(Object quizAnswerCourseExecution) { + this.quizanswer.setQuizAnswerCourseExecution(quizAnswerCourseExecution) + return this + } + + QuizAnswerBuilder withQuestionAnswers(Object questionAnswers) { + this.quizanswer.setQuestionAnswers(questionAnswers) + return this + } + + QuizAnswerBuilder withAnsweredQuiz(Object answeredQuiz) { + this.quizanswer.setAnsweredQuiz(answeredQuiz) + return this + } + + QuizAnswer build() { + return this.quizanswer + } +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerCourseExecutionBuilder.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerCourseExecutionBuilder.groovy new file mode 100644 index 000000000..91b0e2053 --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerCourseExecutionBuilder.groovy @@ -0,0 +1,47 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.builders + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.default.aggregate.QuizAnswerCourseExecution +import java.time.LocalDateTime + +class QuizAnswerCourseExecutionBuilder extends SpockTest { + private QuizAnswerCourseExecution quizanswercourseexecution + + static QuizAnswerCourseExecutionBuilder aQuizAnswerCourseExecution() { + return new QuizAnswerCourseExecutionBuilder() + } + + QuizAnswerCourseExecutionBuilder() { + this.quizanswercourseexecution = new QuizAnswerCourseExecution() + // Set default values + this.quizanswercourseexecution.setId(1L) + this.quizanswercourseexecution.setVersion(1) + this.quizanswercourseexecution.setCourseExecutionAggregateId(1) + this.quizanswercourseexecution.setCourseExecutionName("Default courseExecutionName") + this.quizanswercourseexecution.setCourseExecutionAcronym("Default courseExecutionAcronym") + this.quizanswercourseexecution.setCourseExecutionAcademicTerm("Default courseExecutionAcademicTerm") + } + + QuizAnswerCourseExecutionBuilder withCourseExecutionAggregateId(Integer courseExecutionAggregateId) { + this.quizanswercourseexecution.setCourseExecutionAggregateId(courseExecutionAggregateId) + return this + } + + QuizAnswerCourseExecutionBuilder withCourseExecutionName(String courseExecutionName) { + this.quizanswercourseexecution.setCourseExecutionName(courseExecutionName) + return this + } + + QuizAnswerCourseExecutionBuilder withCourseExecutionAcronym(String courseExecutionAcronym) { + this.quizanswercourseexecution.setCourseExecutionAcronym(courseExecutionAcronym) + return this + } + + QuizAnswerCourseExecutionBuilder withCourseExecutionAcademicTerm(String courseExecutionAcademicTerm) { + this.quizanswercourseexecution.setCourseExecutionAcademicTerm(courseExecutionAcademicTerm) + return this + } + + QuizAnswerCourseExecution build() { + return this.quizanswercourseexecution + } +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerCourseExecutionDtoBuilder.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerCourseExecutionDtoBuilder.groovy new file mode 100644 index 000000000..9d8ff1438 --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerCourseExecutionDtoBuilder.groovy @@ -0,0 +1,45 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.builders + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.default.aggregate.QuizAnswerCourseExecutionDto +import java.time.LocalDateTime + +class QuizAnswerCourseExecutionDtoBuilder extends SpockTest { + private QuizAnswerCourseExecutionDto quizanswercourseexecutionDto + + static QuizAnswerCourseExecutionDtoBuilder aQuizAnswerCourseExecutionDto() { + return new QuizAnswerCourseExecutionDtoBuilder() + } + + QuizAnswerCourseExecutionDtoBuilder() { + this.quizanswercourseexecutionDto = new QuizAnswerCourseExecutionDto() + // Set default values + this.quizanswercourseexecutionDto.setCourseExecutionAggregateId(1) + this.quizanswercourseexecutionDto.setCourseExecutionName("Default courseExecutionName") + this.quizanswercourseexecutionDto.setCourseExecutionAcronym("Default courseExecutionAcronym") + this.quizanswercourseexecutionDto.setCourseExecutionAcademicTerm("Default courseExecutionAcademicTerm") + } + + QuizAnswerCourseExecutionDtoBuilder withCourseExecutionAggregateId(Integer courseExecutionAggregateId) { + this.quizanswercourseexecutionDto.setCourseExecutionAggregateId(courseExecutionAggregateId) + return this + } + + QuizAnswerCourseExecutionDtoBuilder withCourseExecutionName(String courseExecutionName) { + this.quizanswercourseexecutionDto.setCourseExecutionName(courseExecutionName) + return this + } + + QuizAnswerCourseExecutionDtoBuilder withCourseExecutionAcronym(String courseExecutionAcronym) { + this.quizanswercourseexecutionDto.setCourseExecutionAcronym(courseExecutionAcronym) + return this + } + + QuizAnswerCourseExecutionDtoBuilder withCourseExecutionAcademicTerm(String courseExecutionAcademicTerm) { + this.quizanswercourseexecutionDto.setCourseExecutionAcademicTerm(courseExecutionAcademicTerm) + return this + } + + QuizAnswerCourseExecutionDto build() { + return this.quizanswercourseexecutionDto + } +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerDtoBuilder.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerDtoBuilder.groovy new file mode 100644 index 000000000..55ee32ea5 --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerDtoBuilder.groovy @@ -0,0 +1,59 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.builders + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.default.aggregate.QuizAnswerDto +import java.time.LocalDateTime + +class QuizAnswerDtoBuilder extends SpockTest { + private QuizAnswerDto quizanswerDto + + static QuizAnswerDtoBuilder aQuizAnswerDto() { + return new QuizAnswerDtoBuilder() + } + + QuizAnswerDtoBuilder() { + this.quizanswerDto = new QuizAnswerDto() + // Set default values + this.quizanswerDto.setAnswerDate(LocalDateTime.now()) + this.quizanswerDto.setCompletedDate(LocalDateTime.now()) + this.quizanswerDto.setCompleted(false) + } + + QuizAnswerDtoBuilder withAnswerDate(LocalDateTime answerDate) { + this.quizanswerDto.setAnswerDate(answerDate) + return this + } + + QuizAnswerDtoBuilder withCompletedDate(LocalDateTime completedDate) { + this.quizanswerDto.setCompletedDate(completedDate) + return this + } + + QuizAnswerDtoBuilder withCompleted(Boolean completed) { + this.quizanswerDto.setCompleted(completed) + return this + } + + QuizAnswerDtoBuilder withQuizAnswerStudent(Object quizAnswerStudent) { + this.quizanswerDto.setQuizAnswerStudent(quizAnswerStudent) + return this + } + + QuizAnswerDtoBuilder withQuizAnswerCourseExecution(Object quizAnswerCourseExecution) { + this.quizanswerDto.setQuizAnswerCourseExecution(quizAnswerCourseExecution) + return this + } + + QuizAnswerDtoBuilder withQuestionAnswers(Object questionAnswers) { + this.quizanswerDto.setQuestionAnswers(questionAnswers) + return this + } + + QuizAnswerDtoBuilder withAnsweredQuiz(Object answeredQuiz) { + this.quizanswerDto.setAnsweredQuiz(answeredQuiz) + return this + } + + QuizAnswerDto build() { + return this.quizanswerDto + } +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerStudentBuilder.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerStudentBuilder.groovy new file mode 100644 index 000000000..545fabd26 --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerStudentBuilder.groovy @@ -0,0 +1,47 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.builders + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.default.aggregate.QuizAnswerStudent +import java.time.LocalDateTime + +class QuizAnswerStudentBuilder extends SpockTest { + private QuizAnswerStudent quizanswerstudent + + static QuizAnswerStudentBuilder aQuizAnswerStudent() { + return new QuizAnswerStudentBuilder() + } + + QuizAnswerStudentBuilder() { + this.quizanswerstudent = new QuizAnswerStudent() + // Set default values + this.quizanswerstudent.setId(1L) + this.quizanswerstudent.setVersion(1) + this.quizanswerstudent.setStudentAggregateId(1) + this.quizanswerstudent.setStudentName("Default studentName") + this.quizanswerstudent.setStudentUsername("Default studentUsername") + this.quizanswerstudent.setStudentEmail("Default studentEmail") + } + + QuizAnswerStudentBuilder withStudentAggregateId(Integer studentAggregateId) { + this.quizanswerstudent.setStudentAggregateId(studentAggregateId) + return this + } + + QuizAnswerStudentBuilder withStudentName(String studentName) { + this.quizanswerstudent.setStudentName(studentName) + return this + } + + QuizAnswerStudentBuilder withStudentUsername(String studentUsername) { + this.quizanswerstudent.setStudentUsername(studentUsername) + return this + } + + QuizAnswerStudentBuilder withStudentEmail(String studentEmail) { + this.quizanswerstudent.setStudentEmail(studentEmail) + return this + } + + QuizAnswerStudent build() { + return this.quizanswerstudent + } +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerStudentDtoBuilder.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerStudentDtoBuilder.groovy new file mode 100644 index 000000000..ec822ce4d --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/builders/QuizAnswerStudentDtoBuilder.groovy @@ -0,0 +1,45 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.builders + +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.default.aggregate.QuizAnswerStudentDto +import java.time.LocalDateTime + +class QuizAnswerStudentDtoBuilder extends SpockTest { + private QuizAnswerStudentDto quizanswerstudentDto + + static QuizAnswerStudentDtoBuilder aQuizAnswerStudentDto() { + return new QuizAnswerStudentDtoBuilder() + } + + QuizAnswerStudentDtoBuilder() { + this.quizanswerstudentDto = new QuizAnswerStudentDto() + // Set default values + this.quizanswerstudentDto.setStudentAggregateId(1) + this.quizanswerstudentDto.setStudentName("Default studentName") + this.quizanswerstudentDto.setStudentUsername("Default studentUsername") + this.quizanswerstudentDto.setStudentEmail("Default studentEmail") + } + + QuizAnswerStudentDtoBuilder withStudentAggregateId(Integer studentAggregateId) { + this.quizanswerstudentDto.setStudentAggregateId(studentAggregateId) + return this + } + + QuizAnswerStudentDtoBuilder withStudentName(String studentName) { + this.quizanswerstudentDto.setStudentName(studentName) + return this + } + + QuizAnswerStudentDtoBuilder withStudentUsername(String studentUsername) { + this.quizanswerstudentDto.setStudentUsername(studentUsername) + return this + } + + QuizAnswerStudentDtoBuilder withStudentEmail(String studentEmail) { + this.quizanswerstudentDto.setStudentEmail(studentEmail) + return this + } + + QuizAnswerStudentDto build() { + return this.quizanswerstudentDto + } +} diff --git a/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/webapi/AnswerRestApiTest.groovy b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/webapi/AnswerRestApiTest.groovy new file mode 100644 index 000000000..a566f9417 --- /dev/null +++ b/applications/answers/src/test/groovy/pt/ulisboa/tecnico/socialsoftware/answers/webapi/AnswerRestApiTest.groovy @@ -0,0 +1,100 @@ +package pt.ulisboa.tecnico.socialsoftware.answers.webapi + +import java.time.LocalDateTime +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc +import org.springframework.http.MediaType +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +import org.springframework.test.web.servlet.result.MockMvcResultMatchers +import com.fasterxml.jackson.databind.ObjectMapper +import spock.lang.Specification +import pt.ulisboa.tecnico.socialsoftware.SpockTest +import pt.ulisboa.tecnico.socialsoftware.answers.coordination.functionalities.AnswerFunctionalities +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.aggregate.QuizAnswerDto +import pt.ulisboa.tecnico.socialsoftware.answers.microservices.answer.webapi.AnswerController +import pt.ulisboa.tecnico.socialsoftware.ms.utils.BehaviourService +import pt.ulisboa.tecnico.socialsoftware.ms.utils.DateHandler + +class AnswerRestApiTest extends SpockTest { + @Autowired + MockMvc mockMvc + + @Autowired + ObjectMapper objectMapper + + @Autowired + AnswerFunctionalities answerFunctionalities + + def setup() { + BehaviourService.clearBehaviour() + } + + def "should create QuizAnswer via REST API"() { + when: + def answerDto = new QuizAnswerDto() + def jsonContent = objectMapper.writeValueAsString(answerDto) + + mockMvc.perform(MockMvcRequestBuilders.post("/api/answer") + .contentType(MediaType.APPLICATION_JSON) + .content(jsonContent)) + .andExpect(MockMvcResultMatchers.status().isCreated()) + then: + noExceptionThrown() + } + + def "should get QuizAnswer via REST API"() { + when: + def answerDto = new QuizAnswerDto() + def createdAnswer = answerFunctionalities.createAnswer(answerDto) + + mockMvc.perform(MockMvcRequestBuilders.get("/api/answer/" + createdAnswer.getId())) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpected(MockMvcResultMatchers.jsonPath("$.id").value(createdAnswer.getId())) + then: + noExceptionThrown() + } + + def "should update QuizAnswer via REST API"() { + when: + def answerDto = new QuizAnswerDto() + def createdAnswer = answerFunctionalities.createAnswer(answerDto) + + // Update the DTO + createdAnswer.setName("Updated Name") + def jsonContent = objectMapper.writeValueAsString(createdAnswer) + + mockMvc.perform(MockMvcRequestBuilders.put("/api/answer/" + createdAnswer.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonContent)) + .andExpected(MockMvcResultMatchers.status().isOk()) + then: + noExceptionThrown() + } + + def "should delete QuizAnswer via REST API"() { + when: + def answerDto = new QuizAnswerDto() + def createdAnswer = answerFunctionalities.createAnswer(answerDto) + + mockMvc.perform(MockMvcRequestBuilders.delete("/api/answer/" + createdAnswer.getId())) + .andExpected(MockMvcResultMatchers.status().isNoContent()) + then: + noExceptionThrown() + } + + def "should list all QuizAnswers via REST API"() { + when: + // Create some test data + def answerDto1 = new QuizAnswerDto() + def answerDto2 = new QuizAnswerDto() + answerFunctionalities.createAnswer(answerDto1) + answerFunctionalities.createAnswer(answerDto2) + + mockMvc.perform(MockMvcRequestBuilders.get("/api/answer")) + .andExpected(MockMvcResultMatchers.status().isOk()) + .andExpected(MockMvcResultMatchers.jsonPath("$.length()").value(2)) + then: + noExceptionThrown() + } +} diff --git a/applications/answers/target/classes/application.properties b/applications/answers/target/classes/application.properties new file mode 100644 index 000000000..70650317b --- /dev/null +++ b/applications/answers/target/classes/application.properties @@ -0,0 +1,23 @@ +# Application Configuration +spring.application.name=answers +server.port=9115 + +# Database Configuration +spring.datasource.url=jdbc:postgresql://postgres:5432/answers_db +spring.datasource.driver-class-name=org.postgresql.Driver +spring.datasource.username=postgres +spring.datasource.password=password + +# JPA Configuration +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true + +# Event Configuration +spring.events.async=true +spring.events.thread-pool-size=10 + +# Validation Configuration +validation.enabled=true +validation.fail-fast=false diff --git a/applications/answers/target/classes/application.yml b/applications/answers/target/classes/application.yml new file mode 100644 index 000000000..99ad0440d --- /dev/null +++ b/applications/answers/target/classes/application.yml @@ -0,0 +1,26 @@ +# Application Configuration (YAML format) +spring: + application: + name: answers + + datasource: + url: jdbc:postgresql://postgres:5432/answers_db + driver-class-name: org.postgresql.Driver + username: postgres + password: password + + jpa: + database-platform: org.hibernate.dialect.PostgreSQLDialect + hibernate: + ddl-auto: create-drop + show-sql: true + properties: + hibernate: + format_sql: true + +server: + port: 9115 + +events: + async: true + thread-pool-size: 10 diff --git a/applications/answers/target/classes/database.properties b/applications/answers/target/classes/database.properties new file mode 100644 index 000000000..ebfb1934c --- /dev/null +++ b/applications/answers/target/classes/database.properties @@ -0,0 +1,37 @@ +# Database Configuration Properties +# This file contains database-specific settings + +# Connection Pool Settings +spring.datasource.hikari.connection-timeout=20000 +spring.datasource.hikari.minimum-idle=5 +spring.datasource.hikari.maximum-pool-size=20 +spring.datasource.hikari.idle-timeout=300000 +spring.datasource.hikari.max-lifetime=1200000 +spring.datasource.hikari.auto-commit=true + +# JPA/Hibernate Advanced Settings +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.properties.hibernate.id.new_generator_mappings=true +spring.jpa.properties.hibernate.connection.provider_disables_autocommit=false +spring.jpa.properties.hibernate.cache.use_second_level_cache=false +spring.jpa.properties.hibernate.cache.use_query_cache=false +spring.jpa.properties.hibernate.generate_statistics=false +spring.jpa.properties.hibernate.jdbc.batch_size=20 +spring.jpa.properties.hibernate.order_inserts=true +spring.jpa.properties.hibernate.order_updates=true +spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true + +# Transaction Settings +spring.transaction.default-timeout=30 +spring.transaction.rollback-on-commit-failure=true + +# Database Migration Settings (if using Flyway/Liquibase) +spring.flyway.enabled=false +spring.liquibase.enabled=false + +# Development/Testing Settings +spring.jpa.defer-datasource-initialization=true +spring.sql.init.mode=always + +# Performance Monitoring +spring.jpa.properties.hibernate.session.events.log.LOG_QUERIES_SLOWER_THAN_MS=1000 diff --git a/applications/answers/target/classes/logback-spring.xml b/applications/answers/target/classes/logback-spring.xml new file mode 100644 index 000000000..aa70cdd03 --- /dev/null +++ b/applications/answers/target/classes/logback-spring.xml @@ -0,0 +1,86 @@ + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + logs/application.log + + logs/application.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + 512 + 0 + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/applications/answers/target/classes/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/BehaviourService.class b/applications/answers/target/classes/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/BehaviourService.class new file mode 100644 index 0000000000000000000000000000000000000000..1c4c09fb1e48ae378740c73a7ac8efd2dec41cb6 GIT binary patch literal 1691 zcmbtUZBG+H5S}fCw%0eTpd!8;h*(ta5~GOA%0qjf4ym1S zD&~aT5q2ybPW@QysY5E69mMtv)~7=*2C~cKkUOjdMFP{wd!L4${kFHy97Uko8wJWO z8WCuAl8AxgzKxDG0+lZJLaGLm5qNU;c70?fa9t&N=&g-=HV~uX`JzYtl#(PsJ`tE& z=OI@c1SZ-my9A0ag^MJc$l;XaD=CF6!zBVoA417u;SZT>IwFX;PZbxT8A;LeS@7I6 zDm#5Ztg~WSLA+?Xg2iSilo?Rv^i73O>~n+yodczd zVQHS$h42jn8gRJ=RhS_#+a6a*E70ulkZliw9+Nv1lOoXS2#5N+RB~;n&Z6q`m_VnK zX@mdl4PByGQK?RvhQM+j%W>jmxItjyuf-uyPx}lT5_QE^++wOP+;>#cfGH+1rFyJH zl%Y`ZnCdF*J-nM~uZ(}3K#jU?=Ebud(&Nv@JzD;1JQ|d3 zRAxR|Ly{Tu>4g~N=3;blS{?S9>(bAjV5ak%bQ^)W_k&RJfbH^_V^3^`*tE$cphoSE zke(e!dJ^}vtcOA#*fEOBg!&ROJB3<<1;DYx|Cs``U=pU#DkWAKJrywUcL{w}sNr5m z>oG5}yN<^wxC+-2-yB>=#AAelzDp41a}cI;5N_d% zNdh+$u-oU*$pf3u0V7~0i67HBO(+jxDHmZ8?qm`w{xP9;4uVdoJ^@R3+{Lwwi@?2q pNNhC+6e|xa8Ctrs=j;3^2SL-S6i#Rr;66GE@Bkj-sSCUc@EbOGnAZRR literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/TracesService.class b/applications/answers/target/classes/pt/ulisboa/main/java/pt/ulisboa/tecnico/socialsoftware/ms/TracesService.class new file mode 100644 index 0000000000000000000000000000000000000000..3b2a0ab5b4690a58788bb259f0dce600e6bc746e GIT binary patch literal 1853 zcmbtU>rd1`5T88{xI2#53HU-Sh|e+aUn7;K>ja2)y<+4IL45C8I7A9^GcUjAj-! zH$7oW8c0(xCy+h8#}ub-KsOaLdC=i%SD1VViUh{v%?|Ty`mVdpO-*1>$6f>`+VRg& z3x|FPdB~dt${k^QOoxgSc=PZ7mCV9A^HVbh?`S1FyO}s-!Zla%B2blaaNOi`MPRvY zN|*Lnpt;(29n8?&38Y6Yrdd~bR?0PT$L)hF!V`Lxz?J&q7J=dmX<>R}1jhcP_fjdT zN-#m-@RO%_Ae~)q85qYG4%0$<&g(V5MSIbSzY;Tx&O zjlVT+xWSxZNgf!U)P~En**7#o@qHhqJq0SNVI$(%I2V4U0;6!X3d1l-V5*)`G}Sm# zV5}`Xz7e`zt~MDaMWEJ}CUdr!5|N!Q7Ij|)1lHP_?zv1$MU}LN;o|~XxX;nHq)ree4c(M34b|<+eK3#Fv5-qZ7>%} zjjwavm)3ix(8LMA3YC~;#q(eCcPOoO8Gc8eoGv_cocA=0*zCSk&DsH z5g0Zd&eTy=tJu&|rGgSz$f1;$Jv;n}+LH>Wfz3a}d%zA0eFrEXU6VP<%&)eGKp$)DPW? literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/AnswersSimulator.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/AnswersSimulator.class new file mode 100644 index 0000000000000000000000000000000000000000..28bd917db0dc9d95e5b1af8e306e0561751bb615 GIT binary patch literal 1863 zcmbVN?{5=F5S=wnoR~BTX+sPB!6wiWXnR*EQmcTF92C?d2O^wCs`6=lH?}w2-JW*$ z9MXTRQjyy4{i7=0*$#H9YbpJ(-rL>TH*em|tpEJ$x8I4VMc+24CTKhNVUcLpH7fKR zWjZpUGm%z_Gd+KzEQcy{Cv08V8qXb_7K!r4HmELW^;iv6n5e8DKI@twTo{DhyH2g8s9$xe>u-U7A8Mz&3AeJ$c;&UpoS^U9Z}+I)TnJl*Hz(S14A}Vx8dM$%y0CTYP*DAmiMc^5 zf>x%X?OJQ>0$mc~&QFE<93>Dhmbr>vs6NY0 zSO6NxuvY}iS}B~#_2f%HJ)mpNpeMaq3JNm(#SS#UmOZ}7%k zbebFMOS)46G8)MtyDZP*X)ewRUSS};kxKg0oZrQ?Nz3&95?!PZ1g&qir=oUzSs(XH z(O23!O@8{}(M1UQd-qxn15msv@&Mp9tFc z_bemHfKj#&GOK&+2PWQEWu1HU3An2sKK8B2*?P^c!+wD_1hwCa`VJWo)a;nTM*LWp zdaeJxJA+baLElw2Mr1P2FFjiOtcSu0-P18BWSSeQqyVx+Av>2VKiF1zp6F;)@Bs>b z5}u&Pl}=*iRd}4M&~k2^9(6q&_jEtp&C0Ak$<_0-Bgh1wOI_u7f7~$;axO?+a2t`H z6?D1Su5{Kl2H~Ks#~up2Td^q<4PRdS(+=t#4t}V->@6A)?-Ck$R+2HxgX!U(%FxOw zUMWEBR6_lipj$ysM< zyUX}4?O{b9(npvV=?YziRf%x>9Ky#H2-ijkHQ0WJF(>F#OoFcc0|{9HakGNN1=<8o yjjoTvdxQXQ0QK`D5{|y0oA};3qMD;y4jjKczx)a}zs3l#9Zs$8Vylj=Z(aidCM`+; literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/AnswerEventProcessing.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/AnswerEventProcessing.class new file mode 100644 index 0000000000000000000000000000000000000000..f5e22481368a3bcc3980c085472454aa2ebdfca9 GIT binary patch literal 2902 zcmeHJZExE)5I#Cy{nBJ<({@|CH#U27x=q?EFwDaMA#sZUiWAhXhXKP1nxY*xk|>b0 z-TtQifB_q@ecz87c9NXPu9Y?&wlBjzkOEJ~=Z?=EkN3x)zx)OO9(-Pd3V|OI>rO&x zhKjjX@K|!?8pS0GjT+e*)52x3nF(!Nu9OaB%&b(gE2bj0M_O@VjEu*wmnn6Yr8TG$ zxblcinHw@xIvhR{+!ENxA$vkkB^LxX{e1ge&?a#6{v@_C5(8=EFce-KE1MZM1itc> z9=j&dXm_MpBxXvVxI=-U?uc=#^b_>?bc5?nteQzJ0))LfiKRUpeTN&)j9>E~JS{}c zH4#q<+$lB16|#c=-A+7}TE!7kfm_y7KSrUN^`9hosOx@Tqf6)z^2Ndf6^IbEE`1}> z+_z|o~t|18pPaUn5uEPydMc=eADx8Xdaup>q%vhj_R!B=^zNXDa|5(sK z@DO7a(1h_5HWqXnwVknug=SU+P9(WjCgy&4)q(ebZnfPCMnT@5(Q^FUuiZW_3 zE1Qj#Tz`)KY7YO?8+zV7uU7)vY@GKeKhKnD$?y_jHmuyU61YVYa>7jKF&9aCJD;Qe zpu5+(yWj2XJ?!82x;<~Vzkk^E{D%jJdmaDWy@h`T=jST_z2FqU^J?~N{3dL{tLv}{ zuMxP>@{4-YvuQ1Dr}b#lm$B$hqM^|JlwAT_zTzw#%-Nm`tM*u8DgM#_H=hrzmesUa z64*v(PnfoBv9KR0V|P_-te$Y&t^+0H%ZrrD7tNOQ((pf%pcrF(Ptun>zZlsoPM}?q zA`3~gA6qdNx?LjL8Pwrj0zX|K8bt~}J9l5Wa23E8x*G%<*#nrr-Iv*Tti>3g&iw#8 zn6uRY`yi|rC^``KSOrHc?LRC^2{e0ZqPe&$(@S$}fytsva?&eDpjFyc7=#fjEd=h| z<8}=`#%5`$yw||RaIIFn8hncF&B~#%D>;x^hn619z-_?B1KYnURNy7JgsU2^(r+EV z6=*eQy0LLwyQ#90q z*Wry!_9h}!&ir@_Wvgg!m!Q#7wA-+aXr~C()KccpJ0%FM5`-4+*jR)2@bf;74{)r( X4$c+0bLQlSxc&@B9shj<4t(+#{6dda literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseEventProcessing.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseEventProcessing.class new file mode 100644 index 0000000000000000000000000000000000000000..8fb617dccdab65a77a8d316d4ff29a18401993b3 GIT binary patch literal 2902 zcmeHJZEqVz5T12R{nCWAX$!QxUBjDG;Eq=bVT5!_+$z$=iE7sf2~n;0#?Cf(x2N5; z-To$i01`;>ogammJ3FymU)mJG7sQ9%)6UI4GyBZ!?ELZPFTVkR51&_{#Ndb2dZS31 zzT%!mkw~FDqlDyMq9!r4?HiPN*nV`ww=KWD(WsE&(WIsYbNtDeE8wOtm zN)J7gYO*`fJjRLACte@PPjA45Rr(3}e7eE&M^;Uw#*kpIjuL4P2j9_#Gvn8S2Tu!0 za}AR*gFA($gd$exzt>8}QmZ5;Drn1k=EopXlkSt04s|`~YI2DjBEEQZphApD>(V!p z%zaB18aq7(yv)YZ_xw%)XuWpcpXqtVTuV-ufU;rbo)y3?lHgA(TI;dEG`pS8 zQGd|hYu(*%xAq=(@B8hJzuVnEZ2Q5(gTuX6aPHo@e{;^yRsMUynFG(N*|YKMum!KK z!zR4O;6@{u*PD*bYH2g8N1K66usw?VsJj`v47LI#c+{J*JrkDgv7}P`WAJZ2A5<;N zS+QiWP0pThZTVthKT^i-s>E1561G_dhp{g&QZ8RKTh2?v|4f2XjOjheUh;)koI$f7 zMHZ4~Ke0GO-7FC8^s4YKgP$%Cjd=<`J9l5WaLs`)bT=5(@&|DGc3pv_>8Pq$})CjyQvrBVpfysH7;yPhn5}8z-^$$gWA6`l;9<}M5_v|vTv2X zC8$AtMRpn1S7aOTGU>08d=swFe3e#T1$@fW|GFGc?kI z*Wry^_9h{e&ir_bWUFXz7oa(XXt!aT&`uG`nWfyHcM1?11qcn=v9Sj4(dT^{AJABX X9hysU=gi3uY5f_ED*gKiT=?WKFo=&` literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseExecutionEventProcessing.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/CourseExecutionEventProcessing.class new file mode 100644 index 0000000000000000000000000000000000000000..025e0e3567b9468f0ad5389d2716b95423fc4f60 GIT binary patch literal 2974 zcmeHJS#KLR5FW~Ebi}C>=V;RO*f>4p9J}cS6jf0`u$98VS~enCMV|tSJCYgiE*B&v zIlrktpg;?>@BL9lXIHW%OY6ibiar#5SS~mmelz@LIGjKJ{N*2!prF0-;Zl#Jn9ARwtwGwEIj0fJ98ft^hV-&-5 zy?J)I1Vsi{U+@w4LQdLyy%#7fgIgloh9}$Fs7F#DgWLWB;B=+V;LfvQY-NO9X=E?N zMjR`f`D7S;?JGU-Orpu9zUC2*l|J%%NJsGcTv(-F(s52Uc#WY|W2rG9*lWXB+P(e{ zwBgi?>;A!_kTh2?9x-^3Yf300jMlwoJd#?)5wS#DR#QLvp&EByCUm+Ren*o_|34$%ovgcQcWNw_%T19Ub>+Z|wxnp+IqND8gYgm~jlgYC`-+g%9uh7%|> z4$9g)S->7jGN#Q(}? z*~q^|Im9_uIoz?7p4G}y(!WN3Ihp_I4eiGFjdl)bwQ}Cd1g7*w*i zaq_HR=7WL80llg_0ku1)vo3W{R6$6zi}p|j`#fz;EJ+zu+tdLGd@9pxbU%;MS(D|a zw~;|DxAQQBhbpZR-qiJa2_936wSYNYEP+SYcDX8+;4|ulmS~_BW>;pNUV2mm_kg+- zY9otKfVbch%}O*&|7H3YpaRt;*=1N=l3jtfNq>dpYjBmuYc%^Zn*&sTh4mlTZ~P3m ze$O!P(3n~P`rf56MI$Y^3-4vJ_X(kJ>c83_6Gno;PVm`82lJpZxl+? zQ{1yCA}N$-l#o0$s&6M;qsJpNL2W#tln!LXtyGbRV~p&9RsxNY(ZKs|geF;No|c!O z$l&TjKIUG?No~LP5QSy1_H(A+Mm?4S8Lay=h)h&xaP$5svT}%BX=E?NMieQVnl}u- z@|7NVCe~!VulW!sN*{YYByYVw7gp)VdnRC_DTIn*}V8zy9q|vBwjDaK6FqUD;f)i^+v<%JHPPNiG zLMK2G(v1a9%*8PuptDKZ?f@gy++yGkrO?Vu88>crukPZ#x@Ycn(+LzBhiG($+=?T| zk{Nsy(hW}u1f2Zl6yLay2obz*)|J~)xfwOw{Wc(^@ zz^kjU2Cp%=QS;{&r)`sJT2Cs{nlB@4jfOqcorGNm8@>`e>}G6d!lFHrRE&S}|KDeX zDrPY$m<%?_-D9pTUo7qi%Ghlc8LLOa*2~~9_T@!N=Cj7jd4c%cgebjN^rj?l`mCCy z?>U2dPM9nN&TeFJfV!R|+Ul0!T?W5g;6CPI{N(t3=HfL6zEFNJsHD%}^aa1n2Lp`* zdO&vr>Sa!5UFwObW{_wP?U4!&c+!Pfk}{~a)kq7xE0e^zb&AV*&*UbF$Do$mT^IsF zm6QW7^SNGvk0~!_V41W89;N1T0f{fRF`CzVRcD%1zslo6_T&PRT|f6_C-1esQwNcKWtq86<+x>#k@&lVgcxT zoyG)>wBQZ6mCD{Egu;m*Z;@;n?d=>iCl~EDY!ccGp_o`o{dp$`p_YSCqa9aP;63`i dPu~aht-uzI1-NtKjJ`6heFQFi@(=XimZbmy literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/QuizEventProcessing.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/QuizEventProcessing.class new file mode 100644 index 0000000000000000000000000000000000000000..f3587cd10942eee5dda9de8241849958698b1b8e GIT binary patch literal 2886 zcmeHJZEqVz5T12R{gQ;VX$vi{+wkTTxZ_nq7$Kb!w+gyAp>~WwNY#38?QC;*d+e_5 zmcPUgKmrNA^P>=RXUFH%mo`Q41@U3`v~#o1%sx9aJAeH7%WnYS!N(OS5%?~#?kJR| zub68Ek0n>GQCzansDYg@EnF6xiO|O7O6fqx%t{rzVk}~Ns1+B+$av^}J(54R&dMrK zCUE%y8#6a#sB+MMAh;#4`UC2AgdR&S2(0_**4eU2;O4ziY-J>R(#U=&yf{`iGieBX z<|{pPO`_4_K(k0pls<9$0zGsGj9aB2p|__STyJF6L~0Qr?3Gb0?ZMz1+%PwN)jxV% zh?*-R9uv4-Xo@T31OL0Ncr3MwBP0R0tfhVoLN)0=O7KY6{H{iq&>>`sh5ITHA!=Rv zLZZ1Z(L!UpN1(ie@(Nrfu#t1r(pu>%TqotPj$$p03dbU#Tt$ftGZtu~719!!&uP8Z zJr*<&Jj7cHXu|jj8w$FG+RjkKLNhA@Cz4z%GiBR2v%7Q_@6vhfF1Ki)grP_yjhN-f z)S?;jFu}`PG?Q3o7C6ome6O znww8iKWgu`?(DT&yZ5{IymrUi>Fyo0J^%jx!EVbxcW>@r&iT2@fA4Z~;8`tuGJYL4 z;l(xBfExs^H~hTXbZlBln`t%L@MSF8qo^-*H)WT=rmr{)do#9Y!m>S>g$Hmg7h`Sc>C@>yf$ydeC~geShu_?D#a_x$Z9 z&@2d#g@D6^aVt86&bVu+9C zUVz-pS3T@~uvVaGU)W<69I~|YuqY)^@2HXH;*Lxc=Ej`Iyf1RnWFych>?RC?2$hxq zcjj-i0`Fskv{2S7;9@E+SGo#(h)vDXfw33amsv-a9?HNiz;*-My)u;GIk<$Y3a-*` z6~85@L48TK3TsQUb$A~2S5UqImvO#=t532uK>b(P{C4x&&+x+U8Rku#QwxCOC7e?< z)Pk4cl}z?3B9!KSyoR!6wATyJXd&7y*g~{ZgmP*r^XH8Mghl~E19z;iz+3ov8^=31 ZR$v?F65O6U`7W+M!coP4?|}m!`~?^^i46b% literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TopicEventProcessing.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TopicEventProcessing.class new file mode 100644 index 0000000000000000000000000000000000000000..b7aca50498690f30778ed053b78a71a81cdf55aa GIT binary patch literal 2894 zcmeHJ>u(!H5TA8S{b<^>X$yqsHoTkycRVG85z;AfQN+cGYR3p6RIT^c&Nk=W)9%`C z|0ezbB#_`ce-vWw?Bv||(xwQ$RQ<4f+PRtE%+71)ufKo$0{{Z}yaFWxKPAo|$I|vS z^PS*Ia_w8qC5x>dxG6KjXUa~6u|C(@L{c#)werP8D0g5q7uHHO^t(Edy!AM-0%Zc% z9$1IC>;kI>)~0Y4Z!J(Wg82z!03q}w0-fC8twZ-j?uiO9Jk z)P%sDLRDO&OYpznQWI&k8lfLhWHq&85bJ68QG$268Fmetga)CrSiGkr5hK^7ZzQVw z7Bw`sdIZYbNUy*R0&BUKTE=Kog0#>Ubg|%JnFbapnb@7>#Zj*{^B6);$t5 z594i{bRngmaTdT3Cv1q$B3|5p}3W zJWMcri)s?*&2z__=Z^Py?p#dS>=tsAXyek0=B87g#gg^~J@Lc~<}=<{sHFmTQKu=R z4wJH8TT1n(@GqzEe=X1nz6&}9p!M2$e`fcYFe4e(04BpynH9h-Iw6>Kv(`f{lC(IV z!hYD^Y2Dpzw|4G#?*;8nu-)C=ZwKN1z5Sh5c&==2U+(jBng3mIa^QJ0doq3<*5Q>^ zSc6vy+-ih*yXm;Jl{V9Mv=&Mw+T&4Qm~QG_0_&mXEbh&{Jtvmkk;G2?bND|#9oQ?& zX{#i#iMAdy*SdR2Irz%LgFMxMY=&fI5iTRHHBDuX~Rd;Mk)_hmL58ZpFob2q}_ z<@D6U2?)CdlJc_LZRoFTo`|Rq&L4tN1NJ z4eCp@%domcy8>5{e+B7la1HnCc={qc1Jr+q^&i)7{t7SunPG0=o@xMGui>7eAs4(3 zZ)CJL5utQy$6H8SMti#ejTWNahD}5}K`5u1GJD=BKxh;oG*IHo3cQD(_i=rIYX!D& WFTtHtBR|CRXSk~P?<4TwlYan7H;csp literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TournamentEventProcessing.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/eventProcessing/TournamentEventProcessing.class new file mode 100644 index 0000000000000000000000000000000000000000..54ddb1c56e6a0b798400eed9ef571a944da3ed03 GIT binary patch literal 2934 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 GX#532!Xw-O literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/CourseFunctionalities.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/CourseFunctionalities.class new file mode 100644 index 0000000000000000000000000000000000000000..7f2e06343cb1d84a21ef8119538f72ff72760018 GIT binary patch literal 2690 zcmeHJTW=dh6h51}@r9JMX$!R622;RkEAI;rh#(|mI}mB(pxTXuKvd(|V`rN6%xY$A zCqn!#B#_{pAB8xx>ohjmhzhklARhL1_T0WX*Wds6`BwmF!Gkia1@K+!!f7JyQ1j5C zP*Uj7Y9V=I^~lY+!H_FEM`J^wwTY$TPHGj7rb_r1o=7LrhHX7H7I#;vWhe!3{VAXE zFyU$(9t@wNZ~*c1qtyhJBau6grpn0)9!e{R3AU8dF7wp}@JXSfAriE3#D&x5lwh6-gsrL5b7?Rp zwHw~%DADu5X-cl%iUx+HNG1cnuVYMzYyAvbCDIv4Py0+N={_egjqSq#N^RmT!%c#F zej;6Cw5h=D;OY;Ll|ieM8OBWLNh*`fd6pVYXeRbkR;vz zV^nCk!?-z-!f2bHtZgo0*IbTW^DnWhvsj~Lgq}^f6GzOE89YfTRLf>k*IXn@bCD>` z)kJwdV>t>rNwskv=(+1mWFcTfWM?PwKl8tJ8Y=}@5DDfq33|*Kf54dk4K%^kn~_ z(~T~bJvY}sRgRx9^ySRRn$yRqSpD1jLn_z=9a2$Ao0DtRW$uSeDk@jU>U%LYGC7iH zSflQp^KBrL-A8w%A~fB)S0G<6x6jCflgvpI76CwXRTFVB6kh8;PnmIgf{}X-H6WHbKm*Vv*X+MW+WB% zrjsF>flsjjwjwQfa=1vj1+nDg9KiR{%bcq;TuOen1W+eukGXMtd3=4Xt!ry#otX-^ zQvntPrRSI~|M@Bnpw_3wL*TCT@w;1`Z$A0~GzvEc>(HR~*&CkiDrNY97MhjzS%!}& zJ1%y~GHlaUke3E67W>j#$}YcGSa=U;PoTZ51Z!{=*6D2x%1|L{mEPCsS<7gzzy>`5 zuEDF>Hzg)qr@uGo>toUo(6janY<;tJ>nC{aw~Xfw-Mt2&>n`0laxQoi-pXiilZLft lcHSY{MQ!gEXsZ`!yFy63@AYT;-ly;TbXCZ^d+;GN{sb((W&i*H literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuestionFunctionalities.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuestionFunctionalities.class new file mode 100644 index 0000000000000000000000000000000000000000..8219c713bb94c8e46320e8f3f87563fc23c7c50c GIT binary patch literal 3037 zcmeHJ-ESL35T8p;{6QdXXn{h3t)bwQ%J;$pA_&RE4n(>*w00vQB+7bk>}+$ld+e_5 zolaTsTgo9cUgp z6iNymS}i0`tRA{4HyCndr)X>_v^KF++)1s%;aCa(!V~Ev+VK7ut^eLROD{tyfUA%B zgog=Nqi}!l7=;U9{l^)956wgh4B%QcS1}`P1#kt`B!Ih-HlxsHMyk=!@D!)o9FaHV z8vPGDY9ftRDJn-IHy@0ZlPMlZD+dX-l+rHu*aq zHA=y0RyxLLQ-K@7>XV1cpw-C)VYFJg?|%nB&Ty*tF{UZ?HmitJZ3Dhz8KQOZo9Ly+wHU;^&hmlz1DVrcfZ?; z9_{V7JJF@Gr{;Pm%JE}{9#6zd+;+mP{Zhv#FT6rE*nl_IU>)8J;6@`lopwFv zC;672hU<}3*d3<>G<|m`s5WU!Cg{F1M599G;GBYg#LU4{(40|IC)UBawGgW4laIA8zD zaEBUXu53mv!$;I37ZJYkreH)Pv3Xxs*radz=zQI6PFn15C8xG literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuizFunctionalities.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/QuizFunctionalities.class new file mode 100644 index 0000000000000000000000000000000000000000..8aa5676348188815c204706803699c01f1a6fea6 GIT binary patch literal 3069 zcmeHJ>u(!H5TA2R{Afzr&_c^|F?~3t@_pe0A_&RE4n(>*P`i}+%1J$Bc2 zg2dm21QLAbk3!7eIgLXuYK7oKfe&}LJ2%gr+271>zyI_L0JP!#3X}r)I(6YRk#?wg z=ujvrbZE7ZJh6J@=G3P)2V{DLRaNwndYQ~CY&Sy~0k0bG5; zXFN=}8i#wsCn#J1m%k(4KAM>n7{Hati!QdC0bD{g3*dgF%{a8Fk!n0Le1dar4#@*@ zhyI6MHIqiG2`Wb-w;oQFlM~#RRt^(vE2UlLr48W2LPbNQpu!OsPMc#2@I)YNPo17i zgE6UH^)^R|o)3;wa`bvMFeF7XS=hTe#)P<5PQt22It$R#K9)+lPbiq?#(n_hE#j@f zD*>#Xo<`RgZK`l1sFWTlgH|UqjG54rR3@48EH#>=P<9uFS2d;Rv-U}vw_ zjvnvsb-K}ovZvEIcctEI;phLRZ(&prPEyt+~)#j(%nWl*p z1T1VMF^O3Lz_)VftJhvM-sR(Zz@#G3bgZ!xQ#+9qtzpfE&yEM+x&}iKb)1@O1QP3>iGDD%Sav8;k8v*gPQ@|Xhx@9vhVx^ zu<5(#S|kfxlz{tVde#WPfq#7R0iz^Z>q%{<*=1I(*qT_y8K@>LE9dFV7HP zXzRALvd&C}+pGc$g7R~;_efDk^A&Sp@0L{Xo%{nxwJvxOa<6;Hw z&^)=+Ix29F>f(HdsK5q|WjXNCWVb7=rMmKiorQOS1~nRq%TR)sV1-sCs6dsdHCnIG zx1P~1!zz6NuE5LLo=Os~(%);e`+zhA^sWC4>tC&3{}EpOHRHKOZ?6I9d7a*?ITySE tZ)UW&NJHtVowtd0UfVkb+U^u+yF`(A-y2W$y+`}|^i;{a+i(|}e*jJ=@Ie3o literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TopicFunctionalities.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TopicFunctionalities.class new file mode 100644 index 0000000000000000000000000000000000000000..bdcafb98b6b11c0474af581bfd26f609294e6902 GIT binary patch literal 2770 zcmeHJTW=dh6h51}@deYgp)G{#;I`nD%KOp>L=cjR9f-7XQ0+!SRaK2=kDY1OGpm`g zojmZnkU)ZW-uOd^GrLaXkd3Gil`6!;-p-!eH|P4>A3yy90B!iJ3@ZUVNnJQiq#bG= zIuuF@9a=3UPplrfIX4(`W#?#YD6}@QRNP6e!qHR-|H2dLB-(JGQz>@N6U$Hv;QDhu z<6*+pINTpTN8tjfJI~xlGm`=XxE5u?i|?%fuArI)a6i&!9NN@KH69s0!MQd^A5r* zliIqsIZE_=aFUXzH=}_eDU!*M@97v5;#xfgR*7^5(bK+^O1iHIOmp)ffYLVcmf@`c z*3QnOYm7D(xD{Oe{joA=buz=42|Y<=k~z;(qY2H#e!*(h!6C93MM5EoS<1x`A0ulJ zcWaCa4R;u~CQ=w}^MkdmMc`V?fouIGa19n~w2aWR33uXjLY`kRJCs-JRWDxASc9sNL(gw+FlXy>|3$ zZ@<%xE|onq*FRB?A2al&&&Zn7N2pliN#g+(cY+S70Hw{zwdykW111%fwquRmn3|p( zNi?k4@Xq;`k;(3%JJj*F$YV4;YMGa5*enw3WX&aOJeSFB{X@AQ&dY5l+}f{od{V3C z4fE22%k!u+O%o}o!!ZJ##H?u8wDKlgtG*u0HCTtY>#zpz1aPYvoi+Ww^8;)+$!A0WPoq~js%c`C{J;vJLB1Ywlp!QiFp6)+oxJQe}@=8>O zPboDnHq$a}(xQ=95Q*$bYbmw-s$$_|pw)x+yb`RyRam9B6(~c6s8xDjrDrXpy#aN4 z0=x;=vTw>txK4j>(APuK5YV&sGpv8Ve)C7T{cFavL3ghK=z5p#^_&adgZDGq2c%); mg`E$Hc2V0$1={WuXuCp4yzh+{`tH#8eYz^--Cg(untuXQ-GaRU literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TournamentFunctionalities.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/TournamentFunctionalities.class new file mode 100644 index 0000000000000000000000000000000000000000..c0cd80820b31968b20d6b5f852427fea7ddb410d GIT binary patch literal 3590 zcmeHKTW=dh6h4!h_(C9UdVz8&gImA}mG`9&h#({rI}mB(pxTXu`p}MdkDY1OGpm`g zojmZnkU)ZW{s{j9ac0+vox~9pqCAv5?CtEi&Yb9!E zPm91|q-3N6t0O5AtNU&u3)Bti{>z1|Cq9D^I~)S`oCEF)xaGn~1Z3O5;CLp5fw5o$9CY-*$$^bJLt zXmdz8QG)b7XsNL@TKNRXySRA$f@k<;dwNv;wmQPA7g*q$N%f6CIy0592+WJ0& z!WQWk;Ud7c_g)eP;Iyi)ESAdfLhXoq5+ zis(=bkXK2&K0t*=IE?E<85wQ!)wT8MKGx^=v3_nJt31|dIaSYx!bJz%Q5ZZ*Y0Kur zq^>?~n)Svqg<$~o^$YH9D^M%e*=TWu=yodZ`C9agORwHZc*QYnmBjAF{xYJy4 zM|ea1e*ta&#-P*qw$WLj^Dnx}^BW5z1wm7#+{lMT>nOt1!{UPI^CdlLH(T2~?N;+y z_i>}$X>4_O_S%i`+3sGm6<%sP3)eYTP8@Oc(8(#9(}$>d^=b7Xfn$OW3BaVy#g%dn z$CtXzSGhS#6B!YH^1j3*=ChG>ZnD*LoUfFxEdGCMHQ#s-xl}Y|9k1@h#6KkUjp4Pb z?{h!Bvo_pCccA0%P{e53#9k`p#U9odnKiRHpK*Z8>^A?Q-H&JOwi03OH#$E4PrAAS zYw*qrtirntZr8#Y73w%o^EN$IS`DSb_Gs8c)AbO;U@g>3jf@CGc?-4Ya`MA9$=-w&x&pifZ)eZcq;Q?y-k_&P}dEbw-XD)G5jPyOAe=M6ea Ll-&dP6l#9~{Z6(J literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/UserFunctionalities.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/functionalities/UserFunctionalities.class new file mode 100644 index 0000000000000000000000000000000000000000..a17008e19b23ac9ff443ec0723f24818cede1807 GIT binary patch literal 2232 zcmeHI-A@xi5T7lTLPbIR{$B74eYkt^!H6-rKrtpqF|?3qOvrXSEh~4sm)&g(f0>CU z`tCpCe_@;}1*+8OgC@rKaQiVgAHSK|nfdhP-3I`O;9dy|3|?hE>?F!Hj0inSt)vN^ zkxC@aw0uuk425<*v@Vp!*rw9LE2G0!N6Y*m66F=TaKoWp`Ic6KB7>PXqAS8g=ytf) zc!Sb2IN2fI8rrUs$lz3*iw>@f48~A*89a!MZHF$iO1E29q}VfdhY%1P`X5$wS6QP| z)Sg5xuXnUpDQ+sK8VN>P8{bFi7(5!OsDb2E*b>qkyGITl2!v6`o1U`RB(+ny%~oQ1 z^}URMo{Q_2q)4Xk_Nr-OLR{ldlve&J0bjVk$)LDI^b(w5Fn#2C#ad%0;4B;ayrC^R zlXS7krAafD^nvos8bY4B$GkjQ-$vd)6M~8bS1Zd~_4TM)itj%W6EIGv0+e8a zsFQRar@h>#orJ0WPJKe$6?M97pq6vl>JYZFPuoYX4pvsgy?b|d@#B(+irYhR|Z*vmg^Q-UIanSIu0 zVa!w~eBRy{+!2^slS;aE0uznpJp#o|9f=ZD2uz>&J~c+0GF&2sg;&Z5tK+_iDA&D2 z##v&T7~PIV&#uyHWw$G6BzTM$j%dR80qY1_NAI8`lrYSRDCkLUv=yc=xv;?i)>vzB z0&lq0#@1=1g{4Y6+GCD)sl%k=C_zZ0NfQ?g7!3xD27fYooO+beVICy6p2Ly7L>uQ3 zpJ1SzDWAVB=#Y}!*?mj|J);t|ms&?LBoZc+%dRd}R~YH<&f3-)#u1lrT^-R)t-L`h zcU;P~Z1;DDmE5!*NSk>y^7G2L7#P-xBez_wv;^L_F0>_@1gc*A*kSS$ zUgcfmKjb^5Hi*6q*9k11-9Xqs1m^g0%{RQnvKGVU-Od#8f*FR|z%f9eG^jLz)kZ5X zWD|)$V8*f0xiCgV0|7Lb-<07Ffvtbu>s&%1Fxh8uie#1$gjX1HyrWaY#g_EtKC3YT zFBt-r9ofOrmKt>bJR+lfVx1S?+6C5fNcDQN1P^h9=FAhQZA*)zx3h~qV_^|+sN#GF zT#*-;!nK5_$JrU6`VGpjXRBYJ_5-a2n8wxn06sIg*0OySuEK0)y9RUUHH=U|TOMJ4 zOawY6!cF{RlE95D*e$p{63kyS54JES*wQJ%Dh0TUKil FzX2)rBfbCt literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerInvariants.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerInvariants.class new file mode 100644 index 0000000000000000000000000000000000000000..fe2232814899fb5193eb258d9e86f58421664f47 GIT binary patch literal 2178 zcmeHHTTj$L6h5;q?!{FQK?OmE^%Bseec?&egdilC1yoj|kF)K#JJ@!bnb`&W6aE|% zP4wL#WjwQ8+eI6aCOrDk>CBvOzH`ZU&d*<8zXQMqJgh*8z?;~({XpoBWUk>}Bs}SA z=?NBS`N14B#a$NZW3II8NvV7hF(YK;4p<=kWVZJo zn~bC%VNP)Z>~hnS{wtvoBP+E zM!uZdvRDpG-$!>TW;F-1Df>$EtrPC`?chqWOJi8Bttly&P7(AR#8w#F92ZhZ@|gQJ zxRhb$FY}=tngr?x7?a1hg;mIG@^U8~IF6N81@0p*%alN4S7?nwYhzPuSOy%UIK~0$ zxSN2>xK{AAhPTAteuL`!>G~IF{J>KQCULbGz;6oIMuLLtFpZ}g+<+NGS%ybv7-n-A znnQ*XVsGO&LEzSqVy*zi<9v$w0u*cc6n6?xJjh~?Lx=KK7g@}d1atZLdgqH3?WSe1X3x8l}bGD;5w%Dme`T)cJP-W zfdudTQHXQeEv*!IgNIylAO79l@6P%8>+5#_ID-3Cs1Puji-w7`J<1m%n zXe-P}a$&;>_oddbm;uIJa^9e^7M7w;Q|9=9I?O6QWytY#*35;IxrdXvhqH6P%soB# zsho{*KQhgEQ~+{_ObDMxxMil=Z&jS literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$AnsweredQuizValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$AnsweredQuizValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..69f2161111ec5813bec9a80bdce2c0b5c9fd644b GIT binary patch literal 1458 zcmc&!&2G~`5S|TflZKR({y~A#Z78U{*cVO|AypL!q*8ubB5~lbSr5s!_O7+NNr9K( zz{4Pc1a}?^F}BmjsUmKOgU6oP*>7gPnengR-+lnVF5Ig^g@I3f*pHR#7!i7uT1gW+ zBbA7qdGAk!#ZYK>iq?hF7#k@qyfQi*h*(9%=Wc%9J~!-YZG3*@Hj6WiUiHZO0cb*>cm(I#$bysoCxbhIX!8# z^HwPBU3g&pK|hWOW^Eki=wmt#HS}-gM|jvVUfXd)}^z~A9|~FcPFE`xvw-H^pg(SBSEZX z{Wns?ZDCcW4~zI`ESy7Eg*y!1 z{ZkqltdzMLrN*FkfX+WP&cg=KmeDVXK?SB^hGdnr8PaR?o~5S-Y%!BSepEDECfOkE zey#)5zr)$)V;pTQ`XTO>GW_Ex6`uZIJ4&Z(f3Jm(G5Bsrl9V0@IQY&dfXQUFb zGavn_uow#MPSLth8e=1+g;z$00}-p}YL$V>Tw|~}O8>dF z#+IPU*w&G@=uAAoh-bB^I46Rq)=-X#d&JB0tse3SWlY>gJQeapbdlG{JLsZDOBF^z zqNFtr?LbL%!LS2Ut-+`h6+fj!^LW(M2gB|M!|n%@-M@%E z-u<`mtg^xDo}=H1}j;Y&L`l|Tcx}E8O4hYrLoyh zI%r#hSYxo1_r5Kx%JiXe#`lzCaN77U6HJp+?V_(w&g=T_-d`uS1nUg8FP{$@55jeS z;BE}5nsN~`#;UK;_e}IwSdq|WvqVez(Dbdu7b@eAUvfo*tOpD#uQbhGJr>TPE5cm{ zAO0z?3|1$&8@&w-N=RtyR-3v(okq|K3DqW?U7C1A^$1oES*;VQ`r zX%BK8p!^LM-Y+eFf$KlWT7VlQv-26|3du^oF2hY&&26_}Z7Ra;DF_dzB5c4W#W~NR oK&WRqOhb4w1>p{zm}OLMR?-&90&pUNU??#LHQ1rT@8$N|Zw<(@k^lez literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$CompletedValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$CompletedValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..ce0f5ca1711377318d8446a6815b801f63fb7656 GIT binary patch literal 1443 zcmcgsO>Yx15FLlMNtcwCmKF+>x}l)jDjl_Y2;~f&WyY^x`Df}`d zkl@abLdZhQ7=)hZA9czY9&qNj8r0V z=951c79*kEIa(J$6-tx@1mYvYS0w_7)9mY~OYyj+Gk1}mqc zFQP=~PV}~YiqbP!l*7FY)*9pWXT~Hb^Z|p-_dV@ZiY?_-JHd%O4EE^6nXq0=Rwj*h z-U_9?i<-tadr3ktThlPdU$bebg(pfY|CGU%z5N!0xw?t54A&SekBfS4t+5qYVQl+E zTXZJrW6W~~Dk-S*%o?gOb&q+lc-%!EqfAK6m}f$si4O7_?G8Jr(F)QhOqI08q3tV) zE*xk+Ga8Pyr+p)>YdkjSIQcvkUUs>stoS7(aVN88J{t@0G4323IVJ!SkJpO?E8_oN_P%&iI*EnW3!jG(H;v@jlpWe zNRhOJRk=S1&iSr#3|!;C&n!(%wS&Gsp3wEZ{Ru9^8Lhw_20NEe1PuhCxhb@bNnwUy zxiTwn(05JzPFRuBHL^rMI5Ise@rBBD;+I^$FxQ$=z0x#e^+Y&_t_=4XeEg^S(d`}S zG`{r=Dou3$rEwm1fbJIkgBX-x9u~-!$y*@5LhnU-D!|rq3$%|I!&S0X@*WjFK=1>W zKCCW(gX_O2T7ny7^Yc08I@xOR9l!?MEMm7{b0)&=83?auBHV>7$}_B?M5q@v%tC0+ gK)6RI<{8n=DQ%N20Vg2{h6-a)gIyy2porK00^wSs#{d8T literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$QuestionAnswersValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$QuestionAnswersValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..45ad0fdfb4b9ef80411ae74853a3e58f912b4114 GIT binary patch literal 1530 zcmc&!%}*0S6n|4bS_%RdP*IVgpu%PM;z^7NAs9`teo&ft@GzYn+kxGgb!J-R;=zB) zL=(OHM;YI2TiTKh@nXEZ+4uI%?|uE={QCXv2LSBB!y*(2oTV=8#L_l33!UIfavfUD zC5x^8;Ley4Ayf8D7#nh}O(Yd_Qme4bVj1;5_p<+u3&WmL+GW4&)|-y7-oNK#hfYP9 zB(QkGx-5*DYK5N=WhPKQ(jCLaGwJ(Auh`+h=SX1gg+k8l z$IMz`i?BuD{Xgv|?)asS4Id8zr5e5~&$V^10l0Dapb;p*6ilNmqE^PUgy&554M?WK z5_k(rZ<|2>S(rn44Yfy^j<>$U{JYBHSE&3%YXMeJPNR=Uzk$;Gd+eKVE3>V_?OcRA zIS8L~5!T=y#u?;L@aS0%c?e%}5Z3X=6v4uMNfc!P8&buyjENE0g!@?TgUr792ko)K AX#fBK literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$QuizAnswerCourseExecutionValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/AnswerValidationAnnotations$QuizAnswerCourseExecutionValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..cd6b495ee94871e58a5ee56ec841f1c11ddc1cd1 GIT binary patch literal 1536 zcmc&!-%k@k5T30-ODz`pp`sv5K?5&$FFuJeAq1ld)*oOKAAH#EjqSqS?zy`y=$rA6 zGSNie{iBR?*FsxUcr!lS+|BIlw=>^-d%u2v`vCwuaK8uz2A@+Oc4Fn4MuZ-vR?>vd zNF`!t-un|_F%;UJpmm`%#zsmDuZ#}6B34oFb0=SKoCkKaHa=f+8?QR*Q*V9ObgaX@ zQCbA=*3VAGA$ZM1xv{0iJ z9-|;p(i(@ht0cOhKa#Q0U@#iWJ0C@jM+O}ypC`i0HurQYo~Fe0@TsW}`f>>RatKD{ zaDMi%9EQ%U_4-jsYOK#%9yGyaUJq{~KjWlah8dWvKpEy4EM`+Wlfi+vO1HK%imUrd zW4)6!(H;t7Et|iQB5nw)GQS_3@NMN7eA)l+k|q@`x6ofGeg+GhTSL9}B)?sPI}Fw? z7Yylzz}=t&rLZDFPgj~j`GBsa#3w3am0xm`gKRPkD$g`2ayJ&v zp)0~VgLnUwZ3gQXvN-sKFeug0`KQKtSOdD5^n+tifJvAlTO@Ca{1UyV=_vu5&n!?L zF#uP|R>*sh`vB$dF!Oe<`W3GKq-X&a$Y%R9%tf-5{JRV{;AS4X1xsTQZjV8T$097l t3Y~LSLxE6xHHiE zefN(t&Rq*_q2bN=aC0}aGryVn&FuaD^Zh3PY{UH`6d0VOK5WOzHH-*7O0A>`osmk! z&V2O8!eS`2J4Wk5X^f4O7G4=0c0{bA?&o%XUOzKzYi)df{vMMs3O(9Q5=;}E51F#G9z41>Ad^L|?lmfp0rR|(dYQ;ir0(lXeffFohO7%Ws8 z?YtFAdl%M>ueIZtU{;1<_D|AbsEJ2PEB~0mm5t3hgRvbGVG*t|nC{o~#9CuZFvHmD zfwt&O+`))TlcXxnnc%54RAb^E^77<<3weYxCUPU53V9@&$gAWXG*P1!9-|;p(i(@h zqa?bZH-eGYpg#)oJEOkJBZH2U&J*Efi+jq7pHt#^Fl*|A-uQ#w_=Dl`pXDAL|3J=a zw;z?H#`>(~eiK~g_3#GrQ%=fdn1ZN+ zW%D;u#C2g+ruU3vzNH+4ue<+kg=u>hn&>YqIhi*$2YTzS_Erg&8LVBtAhaJ8?M8wQ z@zul3_?mfvzGb3U!ioevT~!9hmX zWw3mKwf?VxL8*q$KQqq58qm$79~OfGjKc)UB54z(m*_o7PYKw3CV~8@bGS;fLfV5| z2Ppr5skd{}-(dC^Sqrd0GKoVBw)S2mmy!oq4 zG|_i|l<{_V-Btqnw7kse{g~c!&)iOb{`&eI0Jh^zu>x8TaBMhdFL|X)rh?~L7 z#t}-#VDzzym3zWqxVmw`V5Fu)^kAI9WS+ZYj5cF1!PxTa*r3%>8$&L2D^XGB?L0BM z5n;=2^3wSJA@UGqM1(`02>C%Ykyj|&Z(@u_I1K%kl15uJZ6(qAy|&Kv`gwbl*Keo7 zLyeY`&0E6BL++?5o+Q*$v1;P{o}_+HQvYvB`?ZUb7HU?y{is%=jmuijo8Yp;hc}S_ z(WF#@F}PZWD{zg$T(zD{xaW+Dn_HR0lXVs2ZrW;~*%zc5gXy}KB03O8Wp>Xw;trK% z@UA}4vDjiz+S93#_)KL~_&-L-&)Q*7ejd{?)*@jo(pW7pFaxEy)}{261}C3v2D9h6 zJ-ErhT?QQtU#04_NNB!G=xo4)+YHjdQmWCX7$HsmS&Ghpd2pA(=Rp%wr(IGt`V1}ZoS;lIrbUXY*{uC1%YzLD zpDv5+o=ZcFU6JHcPV$`S#4a7c3(Rgk3oAhX8~XJDA*0)JlvaLlp5ixP92NLt b5v~*7BdikMCwxHrr#kx=Y0om{twZ%U2pO0_ literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseBusinessRuleValidator.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseBusinessRuleValidator.class new file mode 100644 index 0000000000000000000000000000000000000000..f5965dbe3f6b6b9fa126487474a47dd1db3ef266 GIT binary patch literal 1766 zcmcIkTTc@~6h2dmy;ubiyy0c2VoE}GUwop31c^KqHG(9XsF}`=?ZEELW@fgaf5x9- zqKUryql{;FTUw+b5Av|HGiT3!XTEd3bAJ8)_5%Rc;c*EH1m4BYPeN(in)yy}CAs#k z=8}b0e{=`T2%jl?AdL07)+UgOIjNQ3W1$T4?S@W_6;Bf@m9X|z5{ljYqc$Zd5}4j& zJ?4i@b^I6YJ;5D;ORG{zw?<&1v9wE|xS<14f(n7Dq3<(ev?;@7QkZ|OjIcWFiGXq) z#WKtS)7a>CC?dN;tCgLupn>2aUO1pJ?fIR2si6=pZGzv-=badPXH^lvqa*5)&qr%dRd}R~VVzowcnqj3Y1Mx;j80 zl^UdSN2OfNb|)h&=cYAJ+RUSopO?pBAgmLIZmC+yoyM@CPE$rxHs^DyLK&{qU=pqp zm}|6#^4WGqs!lVN*HjCY`8JvqLH1?1L15wR2EzU!Fw2i>zMe`fYcXuztxORwnPI3690LSO{Yn#9 zX|(b}HZk%0%s6&@E}RfiUjWUew`I6XVDq2%I+sugO!ioqU@}WcB2^gXcpDp;i%pr9 z`>e+BQppggY|9Rgw#1BcO4e- JT|@>C{{UmWBhCN- literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionBusinessRuleValidator.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionBusinessRuleValidator.class new file mode 100644 index 0000000000000000000000000000000000000000..4ab4657dc039d51bd3a9409a0818740f7c9ee1cb GIT binary patch literal 1910 zcmcgtOHUgy5FUp>9!(3S(D%cnrAj2Ux%AW^B#P)Ml&Vmqa&x>UF>LIW?G3d5s;5e= z)ZY7}s*X1afspc$Dh}(lXU8-1&3rTa`_K2E0I&)#bC4mh7rR;#N!!${<^)%g>zdVE zvdHQ^cgT#WF=Y>hu{Ex>38i9AYE|p9NQS-bnof)r>qmko{?nVpN+qoQkVImqr=?8} zvIJ)KS(nu!rdqYl=Dy&Lz|^u-(yb7fs8n|dWY=^ka!?>JJ=9${Mw>j`Aer5d$_T5Y zt_UgDomfU`1T;3f8HtX4O^b!?wxFTl5k?-;nDGPF610rkK}#rMm=j^pk=$r2OjmMY zgFe0yDNj*}0jgjKc(B#RmT9PkrAj;6VUD+{L+|1!Mrd>Y-mz zy9jx>S%OKpMPRTVt$6WF{kz^k!}z+{(23FbM6uskm@ z{aZLIxOgi)C8{HFj~$1B=NEy(mTciXO$^@qdV+Jk>3CVh%hyrwGv!#R=imkQ#0Xph zr44EE5764iCbX~sINR}m1Gr-DU<%h9o?fMAfZ`9xf0`|RgVHaQW?&juuLJnZ;95%e zMYs*Ksq7BSq1M1c24y`9^J6T~F&6IQjY$IcQezL`;R$0t%$~7FV~j2S!&o5$Pw?VX Wd?w%-t{I?++P}-Ni0>>Sc>WjX=3l-5 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionInvariants.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionInvariants.class new file mode 100644 index 0000000000000000000000000000000000000000..2f7aaef48968432394cc23168289063b66c5f3a9 GIT binary patch literal 2650 zcmeH|%}*0S6u{q>LO(2m2nY(e)UTq+?uC=631Ue|3YfGKjqxzu9ovE3na#|!K;qTE z$wU*q`$rkywq>_$R?Qwf>0xJg=Dqp7kDd4C`;X6G0bm<83Xn74ZJ_O*$5dMoTT_QK zN7za@jCe}C*QZ2Mn{ahXrLr9%q{}$bOmMqTJmwBRcSKJrT0f&sFBI+ZK9P)YtqPDg zVD^~wiR}^Iu@BqF)X@e^Jz<>bX9i5vHjWI)?+BL`pk%;ItXr2-$Rb=ZCJtY5NtN*W z)HNO92h1CoH3KQy9`)5@vs`L*sp(S3Lw8*>AkGQtP_v5KR)=ybiKedQGe-(VWuG}z zS)-s5tIi-4dJ&2hM`^Jrtg7h>s!T4l=@adAO^w#+S%9&PTMx7~N~|?XtaUlDkq_gE z9Wz-S#>Mx; zf9l-5Bw37h4bu{Bi5+d~o{Kq9k!ajfB&z#Cn55XtM4FGnTf=~I6Z@<~cUgF{))G2< zQKPlOvsr)#cpeiv22}Q$Qg~cj*vu5H170S0s{u^o*Cb5gSispPt{V6*e}Uqgx$-Bd ze8X7|W^fF30H0YLD?=@~26H$o!F8BNt&s5W5@8{QP#q9*sJn#EB>wjSVmSliQ99yg z2E=AM;#LO4({#j22E=YUVl@NeFdea$0r5H=aXSNoq$A7>h~sp`dIp3_M^rN)KBOb= YWI%jOL*(Et9`hXBgBrf`SU>lF0@LiFlK=n! literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$AcademicTermValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$AcademicTermValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..3a31183152e7f716d9c6d34b4f00d92b1f886f23 GIT binary patch literal 1533 zcmc&!OK;Oa5S~pRNz>AlzMw$C4F#na`@#tjQmKkSYRfA`;<#B)lWpx?Yj=~D1L9{P zfdqGc6k^s*8n=p2PasQU&v^Em*Ejp?_qQJauniB(P-5^ob5Spmwyk;OP$(&MWVMhy zvHH-RaDx$7_5_WMgw`gOiaV)Q)aQwe&pvl_&sf|&MbY!WTIYYZmC`Qvv76h1$Cygd zM3as@$}q`b;fVKnlyKFFUbl}>I0kdP;Lc!iZ{+gc8L2u?7_7bPDJN5GNh{k47J@No z(1Bxa953c6w6e}{sho`tv^(e}3Bhb#!Sy*McTB$P7eh8bWf#LvKb1A;HuM?MKE6`K zww;7qi?$5+865tb1~OPJc4>5TFsK}$buYDbumQAjbf+;W!4yoBER$BHcZJ?F`8N=U zN=x7^B)x5d{AXc~w<&v60Z)#8Po5wTgN?k@5NGuI?I(d#5P6-qpD9>?oyO9%DCmgwg6O9Xg9J z$zbuA_jr_W)r#IVk5M=VMSdR1pt3*o9ylXaYn#E^hpuum#fG%9nczqY1~od+;l}aN z>V#I-87`HxQC+)wH%SO)>l&disjd#iu3t`hc$QsGJN-;5>7Fx~sBJYEOz!F!i?G08 zVOam3G1`=1iLu&8Wzg!RhjAcun#m-eP>>l-{iOD3P@Xw#V-TZAX!>!Gana!|45}0z zwoswr4&yMDG$)IuCk5KDzoRQwk#7bEn+b)o`8=8)6)JZZGxNrM~d^}cb^wWVe7dH;Wromf|7zT516wzxp;nt!p z!b1i}|7Kzgmj3ubFeug0y4Tt|*Z{g+^w(lgfGLp7QaB{2U!cSN^+WFJo-(NKHg*Bg4?-m4epFZxH|@6do02_ ttWcgo4F!*$*Dwy@#TbP9bYhB8fgc5SK+ZzY| z6B0;p=SLyNcG4tO)FT``_RQ|SdGlu0zyEyy2>`qBtO6wlXQ>POv9uk{Lx)01p+l>M zAtbJe~zM`EwwLKc9qgD-($CmP2;r+ z%rRIx;R7DVTy?|uof8y}L0RNz43-bBF77Z`|JYYfCfJr%c4C}Jz@SM7PPuV>lAF-V zI>V)MHf(9v>c=s`Y)r!(U8K`c<(E=P_lm)d=60LG+@6lG0=F2{$DH z3besc^()ar9UN&LjN3p5F4b=Mkw)v2?05N8)%MEW&aPJXm3{mbGit{6lA?>h5G(*AJw^RzK;W zIpQQ=HV9hqxXq2s{9$m;^`vERaqyqUmxgGei>{bz2CL2OiE@fr-l@U^2AkIp2h9c{ z*)h_TE<>P9m!)^;dOG^R4NuU~b!6Zj(p3@oT4pM37gVmlg+V4cgW6j~Gq)FWYtdHV z34`N*BO{ej&>a5<7*tzm-5YHkYy#al`ZqBs!90}7R>&)pU#0f~Jyl>UnFZQM0>DkO zHS(V4K7jWF7C$f7zrpQa6fMCj+3b9Vxkk2@e|vBT?&h(3us##v{tSdKGZ7xb2IU!X pC=qJGVHU#I83>Q)#5|*NvyvW@ErCx$5DXDx(10x}{AnI<`~@F^t+4Yx15FLlMNkdA%C{Wsh8wyG}8?WMPy%V^`XHdk!P6q4sJnB_~Vckd(X8=v|_9*tIr?WsyIn|DFq7s7| z<(&%a#blGxXy>g^+Pkn}e4`h~1haJ+ruja-3^n&yY2}|VxKi6~F_<|p5fZ*Ih&~1@xd>Xqs>~lQ&iJl!3_jHVmyStA?0BHW&WV5TB$ u!WPvT^-v(xK@Zaq+!Ta6lrhU_+-#(~WDCHF2!f%;7*t`02EU)LSAPJ5q_&Iz literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$NameValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$NameValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..190d23d55267baa1350907cc1e01eb0a50341cd6 GIT binary patch literal 1485 zcmc&!O>Yx15FMuAnegFjuZYU@@uzTSI2&q&>AeDfiRO0A(r-|E*y|TS&`DI8T z!JQw47;lp%sUp-9XrLzg%=7;G{p|+;?7*Wklo;fhkMmTymJzW>sg*RbGg67v znInHHEXG2+Q?xFY#@Iw@;g!*GPoye2|J*gXb+~tiG7npa7dtyz8(+k@?WXABaHkB@ z43>{YPsFLv?f6~m7^P=0ttpJb%KmWcqqj=8pE20@kZZ39MLE?%}N9 zX|(fJDD7R`G`^XqDZw*?8L7=SnO8g27b1aY*cUO@d`uVz4yi zzh|wn6}Zk={gbxnOxnYQOViC%TJ(lz)=)iN_mo%X4m!valqvN+;hB&pqK&*p(NP;U zTH!H?x=LE((Dsx>7xkxe$t#PQ;9w@va9niYVk$MB7<8Q0yequya8K#+ETdVBrq6uT zmq^r?NHi{y3lgIe8Od2YFHChaV|}Q7SPhp2AKpSfXil{Xi?F%?^RUKXJ&blBlfD*0 zub1|f#%A7ap*;}Pz91SSMS3W#3hTae+IN&=aJv6rWKI$)x6udfGgzw!>0K(N0(Ti~ z{aqNO3&L>&oaMLZJ0^KAEWvuZm<*~PY1|TDsnCbURqkUHm@rs)qe)e}sc;Tm86Ge= z`ZtYYusHrlU{GnI^RJEbumyCZ=pV(P1T!#8woI!ky({#dE53m=Qd))JAsc*C6h99O zWUtZcNwKC=-(m6d%JNrO`AOaqtdpIkn1FtRY={roH{n*{+ko2>5$;StXiP-dgf+@D p;7|(a1&2upk0&79qZ2cX3J)dSCtCteEeRwUJvG>-avv7{+8^nju)hER literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$StartDateValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations$StartDateValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..958f98e389bd699290e08b823cf50c8e92713374 GIT binary patch literal 1506 zcmc&!O>fgc5SgjAIxkU~H-MB;+9Sx=K~?OkhklNQAP zLIMfy{3yiO%_jQS)f#2?mC`O>V|R`mH|_~{ zI8GN~j=}N?@AEL`suR9!pP+CI7VU5?gY|lzcQV1St_6=Xh$eY^lzZJ%S)wJaY{xiJ zjX{m}ol-?U*{IOUI>V)MHf(6u=*2O?Y)!*7->1`13y-9d?lFT)wcQqjxdRaw9+G;*@vLuhQzE zg9;5z7zK$GMq4y}DbNOkBlsmMa^i7Fd@w!}+AyNN>PH%_PrjdUC%V3)@_3#S*OTQ_ z7YuX|40I4o>tNKxqz)!(RxbvVNve&@W*<-Ei@YDdjsB1v&x0jcDT4>A4A!#a8p`0v z8L2vZ8P}D%RM_YxZ8S|z^f6e;MbP3#X8s^J=ep7|_*nmcS|%NpJLpC#XRuM*{jGLO zu)$#a&q^Vo5T>2N+#K*U<2qz?Ex$n5)zK?%c!G|uECcU|G$Qb+%*fmQo0}SBCqb2; zE0W?t%&kRRgu4um{|&ngR!1z3{}l{M4Ycl=whp#|?jrrd7!+V07RVOKTOhwg??rk_ zz*aL0w2nA{%Vf*sJ;;3k?+YxwU8#J6tKTSEfNNy4{Tb#O*>e8v!F9Ni$8N&Yx15FMu*}ZV02&t-yKq>*z5{aYZohEK~?Un7Mt@sxl z_$x>t!JQw47;lp%sUp-9TxQqf@tc`9&;IrM+YbQPfhR>MFgVX#)JddmY92WhN(voW zEhJB@K60nrV8oR@MPnnOwTY$TPHGi(c_QPB-(B4?7Wd9jbo^H1a%V>=?eZAAdEh!R zDrX0~MVMr;aLl_rO1Nr8Z=1&`9D|bWg)&&&zq-B6VD)`RIhkTZTG>o+C<%ia9XR2} z@!|S}R@NCVDSK4cuHH!!g4w!8=w6z+bBbLXO?h~pji#M`E|qjI7);c*8Vn|Pb&N%r zXD~mgf6o|gN^q00+6QIO>ZFTtAat6^BJD ziVj<-&~S%wm`Y)^Mbni6ZP=UA6|cx=gZQVZDp(Y;-ZTaUn1X4tMOu~V zTcYnw{tu+3!V-87S?`;m_zKLDy+Ny|`I=6Bhq;f73twUJCwU97LUx*BJo+uNKHg*B zhC8`$74D8jxHksj^H_v6ctCmjH55F0Uc)$qFJllM(upZXjr*1YvIQcfO5ZXS#$W>; KQM-?G|HdEx#>4jj literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseExecutionValidationAnnotations.class new file mode 100644 index 0000000000000000000000000000000000000000..6d0bcc86795e41f5ec215c2059daffb34de0fc73 GIT binary patch literal 1962 zcmcgt&rcIU6n+E9La9)Sp!kcWf)+Kq7f)bJ8pOn;;-Qtq<8=1X4(`ruW~K#9{JTsv z(Yt??@oigZ3miP{WxjoH-t2teH!m}P{{Hv{05y2&!4!kz)P>zx+Lq>_L!qS5q18h2 z*y>|<$_<8G*;6z&6k3}|D(<9KVUNc$8hq~SuCaJ!d?!;h^K6pq2{YpJAr!(h6)bHrd~Uq|S{JcFfC?SV1c%)tU<)lbTx)o~9aU+5&2 zasTgrYIG~c#P0fq`DO?G2t`cHBR}Qh3vZ)eq3WQG3JrG{1&I_!TQof>&;~<&SF)n7 z3NKUzBf+%bQdh-~G+LixKjBVvd`H{kSxVx@w@+O#G&vZW988*gNn+gOu{|pTVKPaz zahdj!8ejDJ_$~A=%qbLL4sI9W7A!JYt=30gHk^^Fy_b2tRF?`5yGaX8laqZ6%5^Px ze8i2+;$d>eb);p`spr!udkhKdjnQ_|rpY94Wfeec86`k;Y%&kT0xVsF#sQ($P-8|vJeFkT_aWh(-PtaPfQU6cmJb1+5G$%%bBOUjtbT#FHpPxeq zDtb%z!Z@MjYdH^|FzDvw&>>GI=qyeqJb23BYffAmjvIH?pOqW39_%nU&y7_#LgnoE zp_Lo%dUD|qt^0u4HD_TH=%u084+t4ukFzv-6uqJ>BP{%cxzFXocPRd*XbP5S%xbdw z5{<<{6)Z!UqG?#6aSHChD&a2uX^rOZ!91)JJ|KKZ_?U2maFeh?xJ9^4_>8bh_?++s QtuOW0uhW_hs@sO@KTDRF@&Et; literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseInvariants.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseInvariants.class new file mode 100644 index 0000000000000000000000000000000000000000..82d80d7610b134f0731a65f0e7d681da7a129073 GIT binary patch literal 1947 zcmeHHU2oGc6umATZL`A0*kEh}OaWOs1SIYxL_Db+tVy?f-A{&U^SO4 zvihStVMYW@*%M)Gz_m7^RLn`Of)R^km`%6!&{(mnM$Aa2oUK8X!2A&#u^?iqAME#z z1a|~3Ka)zj7X&V?KRP5(-PWO~L4&~D)c3V9+SFl|RCeAeBdm@_BBWf$iHye4Xkv6P z60v`!C9R?Mj5-jO#(8T$ZHu{q``sdr2hd$fJGa*}9IWkVPacRZV8YpKf zjo%aWcT2oR*7X7tb(%8jP80FpNq8RA;Yt%`;3|Qo_4br$2hK><-%Ob}*Op3jhH+1r zE<zhU{l7v{=ZB{~%a9M#M~Q%UfO_U)N2XV~l$myOLHj8OYH%Mr zkeLWH_oTJRhBPw^_W++Ld?EoFIGcgXIM#5rfx86%-dCu9SnxhW^E<98Fo$F61NhD3 z*v!1(8Z6+d0oP#>y;6k7=Ma_(5L#n|3i>YNH-jA?L)@uV1Hr3A!AF~n*Ki08!+ qYb79diXm>7fY>jFxKje+eKEw{5)iBiq5>4Jwt{SXfd49Tru7Se-|N8u literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseValidationAnnotations$AcronymValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/CourseValidationAnnotations$AcronymValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..5891334feccb83a5c9950d99d46db873cafa41e0 GIT binary patch literal 1458 zcmcgsOK%e~5FV$Gq-p89K!H*>6x3dJFPta>l?nt>35d3lIB;;hL*jPVUTi0&zYGZ^ zxbveB<89icC91f9RvLT8<8NM{_vf##-vMA79+aWPz~nyeq{=moh&@WJq=}u8N~F$w z^vA+tEVMgD>tbn)O_Ua1869^;s*=;~j_Fv3jdR1c*2Wh{Zhc!?qffH_VHrjl%pQua zh*P0k@tfu$O3$DyPCXgS@Agmkyj8lj#bEheM|+iFLpjw8NvqOYp>?9z1CgS%0Kq~s*ha{I5~fIABkf_K z)2(kX{eFJ-3(Wr@YYCP}j+0M7zfLmv2kaYgv#>40t)U3FhahYXMOcAFigT7jDWDfQ k3`2N21mP~-7-Lj;C~1{s2{^SRB+2Nj!8(+5#_*naXRapRqUP*2y~?nmoNA^xl!(C&9XJ!#i{a9w z(au|;w0Cjc_Hyq%iP1_7|L1e^`o-fSRYy*G{I$24{svB;G|lG1z4GbSy*MT7H;%HBYg=3iWd)* z#(F1fqCFPGTF{D-B5eq(!n*IA^ljxB_=Ep1aFWe@3;jU*3|4o7=&p2AfjbPg{wxVn z1fjW6w53DzZIiqaR%GaDHfeZ{Xm}*PP=OHMOFecZqfiEexmTLRw4Vy+(3RmHgAf0d zD57A%$Dl9^z3gs_{xTCy z^xYq2oI46VNafA=(CN^C#t%-pZv-+lnV0o*S`iGfdjIEjMjzH7#k@qyfQi*iC9G^$EF!thut&7f!4<7S8k_iF+V*KzTi=Y83v1o zVkE*?=zjRBcZkw6sL6smgSA%f;#GoS%SaJtC>_%F>F&)?XBS=NR4>LtOa{C3?nqcK zidjmdowq`1@4~k6?O_}f%;tHR&X@E&)Z8Pbm4D3O%I;p5!A#RcScYp1>XYoBT5D_t z78%=mr!6`Yk1*oWB&mw?;(2Nf<(RmKygJ_*AdgVS#B9VVt9bgK_VJ^SwWd zUF>}!X5*wDm88b{tmH`%T;}=k9`aL8s#REkr5aRWnZZidrPBd;>#frL{fy#zOKEHm zlOEcRAl4Wx<-P9;t1^9TobdzY7>ru~V}5CH>V5RZ;bySDyZ6_5t-v~i?aQZwhJ#?; zMc9ooPgAxaqigXc`hkgF3o8=zw5ANIZ)r3nK2sTa{F3bqvIfxBz0fpx%~&{xt_*h> zeE6rNGFUA#H~DZdsI<}f=f-*12HH9LPBAFKEXCaI!|3uvXWXFUGq^?85&{`La^cHw>*N(|E6$K6!9mJzW>sg*RbGg67v znNR*ySd4{sr)XU)jj@T+!YiZWo=8=4w%s#b>+tB@u&cH4#gW@;ifrVh4ATs1$D$|V zROoj6rgeGop=Ywx<+D?(9DwNe}bXRu8dPK5PhG#zQQ^HwPB zUEDOj*-cY|+4&3CXOz?l`L2(LJp7W6hn;<*wDM0GOl|KRQTBT#!7?l{SQ_U4+*)HR zaFwx*_u8T}X%7=FO_r;)=nc=Up?Wg+h*#$hJIE81DYZM{xsWHKjl4nLQ5!W{;W3Fa zC9QF2drG2<`a`*3mBlEc;c&?Bd>9R$7<8O;o(V5I+*4HioYM$Kqvk&96C&ypA{r;e zdF)X_jKpl5)uXc9SRYCr7Qtna4{sqK^rTvaMOa>dd01hv8YVhmN1pjlvTHuP2B0UmT1-);a_8sLI=>7jmY9dUzjXp4)!OC_Z+Jy=#aGSyAIE#qRw6_oI_WJy9_@5)3z{J9DnT$ zDou3$rEwlMfnE#!b{Ldk24+c?NvqOYp>?j<0}-LL48cM&*rv#T9u`PmA?;zI)2;8Y z_@Q3=3iY34Ex{_uS@H?!*GLBcfPEcq6t*?EIT7L31caT528Ogh4ZOOxd)E#J;_Yx*f> z?imlO-BaWV%9I99crN5;F+kp+>S%x(t?-ycnUdBxv?C?aMH5)9CdQCV)RFDTDQa9Pg7`^f)l z5{9q}H*0VM)*0*^wr3I^d#m)|sE~N0tu%IqSs!gzkZKI-Z6ih66IK=eBv|pM$}#xV zUg%mJF$j-IXo;^?F$(|B3PlAS2DLYuK4U8t&LOSUZ3bqcBR+1@uIP(ww$==`{)q)} zmw{dEAY*O17x~mt46@yUQfm|^L_Kp{QULxI{>W1y%H1%yp64&gwk{s^R3{KS6L}R#h^%gr zXg4}pLt9Ay9@CahZ;KpJjiu0PzA3{E0#E)~*$E{>V0OsD1Su^c5T`a|dP^mmi^tNn zy`EK?ySh9l9s-pu*~6umX#C*wl%PhN?4%gmr`ppAR`b;{FFbksmmNX9?f#r|$sOZ%}@{Q2he6A80MW9G=bx@HdZVExlLa0xYDqi?E1Z zS%d=GMiG{BBG8-&SMZBj0+-WZSK-=JFjwX%*!7%XD@O#Y6yPR)cng0sa2wA8P(-u%+vC~95cmj7U*NHwCzZ#ToEuMWMKE1C)~m3>mJw9*-p@BO0dA_0^|wI9I`&M zJr;EAM*EOEhQO64A`s>ofwAgZi$K06U0#3^f$2ndODU;}aFvWLy$KZ8((7}VI?@k? zcWRG@O13@j>&LWQYIZqwx#OXqE)AJ;#5$Z-P}}P8fGcLWYx%-aQghW84%gN&nhU4z zhcQ@5T$YIAsZdvPO#^AD&y3Th2F>yhAqI8UY-p@uDy?BEt@Ejjjh{{B8IzSkK*A5D zGEvM)6x3lsWWUYnC@JOADDx>oY9wXUoP_+p+MtB~sf3iF2$K^q4pRi?s`aFO_Kgxj zXESQA>ADE`Ue9lH)nwQ)1ZL~fVO}fhQrr&@^QJ2_fmZ!*OoB~xFYcqJFdpBNX0PXY z1SYHXQT?L3MVKe>^74+48h&);@{-33G$?#HQXO#99}mDZzL78x-HomcPKn58UNo z8rMh%@R`ANVxR@rU>0{JxDGc^DMr6ljv<^PZe~C{N=MwvfY?Y! zEM-7EO-J0$fY?q)EN4J8(h+wuAl{}URx%)1I)Y|E9Ht{yGaz(2qLKmeF&%L?1L9LU Z;$8;C=QKnP?qjFq-~p`RJC7~+@HgG^g!upf literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$ContentValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$ContentValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..8d7ab300b629e9c604830bb8747b4415b3760025 GIT binary patch literal 1468 zcmcgsOK%e~5FV$Gq-p89K!H*>6x3dJFPta>l?nt>36GXYoRH%k61Thd)^<|(Wk?{w zogal5Z__3%5pV&mH0v3Uzj=N3_aC3X0>C!hDMN|DQSReTs$A2E*rU`+n%EhsMC#0Y ze=IDYxj@#+hkbYvYSex4vVvNA3HYWf)~Jdmy?Z zPK9p8FPaA^J%h45@nkT++uz;uR_WFjgXPy9?Nx>i`9HSS z*b2-sw)RF_bSCX$!llV_l@=Z1xiwTz=I-(8#C{ujf-}Y^YCJLMIO#kSUbeZXxcDKbISj_lebl2y)T2gp zo*HM-2dOa-vvyLE%5r0UD7s$;mqk9jiTt!H)hbNG!W2xxB7>zc(^HD{ND!Jlv#T`L zJ6RL$zM#GZwiqeWhOjE=J>#fvE62d?{&#v4Z>lZyJ=!x^+z6DrP)P-DF!%!@hz|ti z2B2o2qi>t!rLZDHPgj!m_e{r1e4;`pYQMdSQOJV9)H6*?+DV0T=*n=5!MlIlBL;K7 zJOT_Vb$T_P8s}jR=myb~#GnLYFix^eT9y74`cD*VAZnBrAXrES+X(qj!W79%q}?ra zI`svn-_Fl|g86S`Ex{7Waq^J$JW7ewm3T z`tFZ1&K)hiYNJoahfZ(i=DwNlo7w&TzPV{_7b@5|L5*O>HE*Q=}7|uSJoc%@h z;_M4G8|REXdKOV~<)1E#oSu7Ww^`W z-9Ofo!Rmx&<4c1V0GxN=ifByRV9RPOVUIj`Fvdl-lM7g#Rkw>YOG?6n> ziNu+A{zO=egmx!rT_laMvC_gTqock^R6JO|>Y>YRtuxb(*2Wi`Ze!Q1!fp5 z9ErY&5}`ZMi}n#p&!8-egA5k;&TnorSbg2oUZvPlPPG#pbHHGeE*uN%#duKCXy>g^ z+PkP}e6yD%1hX~?bMP^ngepH&TKPu|E^KbK7|iUN7%OmzL46d{V{47A!UALKhuWeu zNgrb_O`55s$eCx>P>iX2z=OGiF7g;Xb*g=g}c#OkTNoyS1zLMy| zA>HS!?}sFZBjV}g8O;qI8+4p>o(eC!+>>AYkP)@xels5qiyaP&9ZnYe%=>t;$9y&h z8L2cg*5^r&(%`a)hqsZRvJwO^4~sPjV2Qy>UZ>Nl?|Z9sXFDgkzNa)cdubc(fgs-U z4lq(AEn!uz4~;Xvs~iKn_rDvK+GehUe$dAZmN&P?@)&gQb`@?g_)9xbHxQB=Ax#i6 z1j-3nc#ghn;+MjT6g^EIgJ7SAL*f&a)6@9lz=b)<3~J9bwbyPUoI_WEI}G0bW8x_A z^Rv#N+N8JPsc|0Gfu@Qc9tI_tg)+$sX=T!@^q-@z3T!EtK<6lBxJa@_+WkTY2)@Dm zo5lJUxcq~xC0Hhz-_J2uNY;vV09W8@VY>#aQxUFDLHINk;U=t+pVJshggS^}8p7u( g2)F3QETeSulx~wO0Vg5|h61BP!UiRNx3D*U18!%ZYXATM literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$NumberOfOptionsValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$NumberOfOptionsValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..6454ebd2d09d84e4fc677ffc2975960efc209f33 GIT binary patch literal 1489 zcmc&!QBM;=5T30-%UJ}Aps46k(ZI{yi%()q2+>GV3o178$!zzAcHwUK+}#$5Kg>iE zefLKh=dLZil187551ro3&3-fUeY5xT*Vpd=unUjMP-1YNy08~Z+txgED3laBv|30W zTYc=#xWSMsdxpk_LTeLA#huhD?DJSggU8oBv{|fm5!zKsyZoiyZ1$2inuFtmG|RE$ zq%zDfSUTZ-9>!dC!h`k+3ddkkO`c$|+!$x=D~BC4I}Fy|^pukcwxpHq7z;HSY|)8R zZX7T6DX6?NTq=WOcKkA|%p@@mw$WOUA5CH+F+=_rRc{Y@4;9L%DiB{?nfG}PrjdUC%V3)y7(z2l8bdy7Yy|ehWZDS z`d?Hp>R+f?A50{ZR2!EKJs!gsc|U#|{Shmk2MbWEf(OeCRx+JN`oDEXs?JVE@?t|O z94yWeC*By;8d~tU#f{ATVQ|KErDgEB@!xis_NUfCTBV5{&j#BrOg7rt3fy7vmkpr> zAxt|3bG1m;2r^@9=@q)Jjt;ru2|Bu|47|6xX9T{G8F%$RUeF*@fI)R%(eCZV+*-6{ zc);MpKW3F~@Fm{H-v$PiCVf<1YU^MF=ygBkK^gmC(3b5tO0_D-{ zaFuM8yvMl@;C+LI_qD|@aQz2GOK^j1c0R*gAzRI#Jy?aCdF&RfO+~mp1)(t&;V!IG roly@ZLLKxl4dHMK!aX`M%V^wer2Aw`;1dx9LyeK5ut|eI%;WXn*qOFD literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$OptionsValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$OptionsValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..2cfa096894688702b9ba6c229b835454c9ca3355 GIT binary patch literal 1492 zcmcgsOK%e~5FV$Gq-p89K!Jjr7HBWK7fuu*RaFs4B?THHaYByQByM-@t?i`r_i*4R zAb|vTeiUN7*)&OsfD2s4p7Hpb*JpqK@%bwN?85yb6bPKAE^NorHZ%*J;7W2GTFoVk zt$ydum=PgU_DmQXa;;4y6?0Onu)|^*b+<3u!g^c%!nCWDcG;obJWTzW?H?9loWT4s z>##6psu>K* z4XfH!+i{FA+kfG@pQP>_ebZi6YDqnCs`iZ;Uo2 zSRiEmwKBr$xFaIUb&|?B>kv(i#(EO_kd~)vEkPr}V{CgwQ^rqNQ_u=}2Th@bVNOIr zBDv94n2zMa2KjI&TZ&8PF#rqWJFc>#=K~9Yzr$#VL zjf?1m)EJ0a=~g6@R2%1u?w3J%mJe+R+UrWW470E_1JkffV8ze0N0B3Eq-yT?CeIy6 zC93VDAxw>7-#lA1XK|ew>Gj+=?po3kum}I0-pHGBQ@EV=1eUiv<$9HL$-Yy9TLk_< z2;>7o*#W4bp5#crC-cvRYw73(Gb|AfpDNxv(rv@V6X`ot`Rxl0e0Bt8o+)J5Ud*f& zwg{U9-u~m>5m@-;b0AQv;&1X)TLkG`jS)Bg_i{H>%fEARJ=;P6^qV)bA`x;!&Y^!i%B*M)R2p>iw wtif%J)61dY(X$*zA$%Nxa0hQp5G>r6L{S#7Ar)N9m>7W#xQpf9%j_FJ0p?h}+S+{$}PkGyVDN>vsUyg~w$mF)*2rx`}cvBO;GdD`_HUq!NiU zAN{$o7zypp(Yi<)V`HU-S4KxYk*N4$`L>HLw>5{RU9F8THr?idjnR&_$}r7f@kI1Q zlnC994q7KDJ%bs-M$2HiKHA&Y9@}Vl7_7bPYOhjkDyLcrj^Q)dq8q2edNCf8G}?J9 zl=d!a7~kk73BjyihB^A2U51){rnK_U8BA?$HyKRtnHbA(ox#E=s2A26TY*K!HjcGL zXObSqT$(gfNs%(otf3fF_ml^7M;+ub%7l1~c_!qkXd|!D>9CC&t?(F!sgl+>v^^!! zg?+ZKSkL+#ha=wUh=;e34_FBTn1`h*1hC9tC9l(<>WAJc-QLMbUZ^XLjc(dPdnAZA z21|7#MbZ>j<$B*Z?K{dbIII8Xex>%%Hljsk`|QtlLMjyqq!JztkvJj8J0xy*?XB&k@XL@u zf;&G7G2W(0T1CJGw9MC?&&B~9#%R3dfe zgFhA)W1-zKS{F-WY@)RA%ILT!Qk9(izV4z6w&uXJqqXtHrrT^NpW<+<43i8N4@6JI zsnG5CW$OT?XE5c1AA_a6;oiQtO1B>{SbNjeUS-%+PPI}TfoHHq2M&exVl*XbwDVRd z?Oj|qzTQn!g4zBH*T##y`PID*)^dg6G j2zSRI+@KRv^ybkr0XIpOfKy9Cl8n9@Y*M+m3w!MsF36>a literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$TopicsValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuestionValidationAnnotations$TopicsValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..12781111db44880ca54b5c5c032943c8b704f5ea GIT binary patch literal 1486 zcmcgsOK%e~5FV$Gq-kkOA3%Y^HZ9Ozb}yVLLaM4FkV<$oMB;=T?~u6NwHMn->CfQ6 zZ$SbH?))gkc#||q6#*Bxj6LJ=H?PnB@$<_!0N8_jMJO=HG8eTIX&ahH4uz6JM^+2T z6RY33Q*JQg%ATUJkiaI=zad-Q&jn>=h=cYZSw960eR!wJ8*uhy5CK)Uq z^A3*^uA0&F#xV-VVA|!543-Wr9_}(&d(~D>rdXF&HWC~Pz+js$oN(j#a8g1m>kOC5 z*{G^rwVfmcv-20O2WjTc$aj4-WbG^)4Lki%D(N0EnAqN_Gnm}hF&1Hg!NMT_17ox) z!DYraUMquECmoCfq0>wzd4Gb;XsRc*4}$Vct%X61BB7?oLB_=iZ(>j(@34sq4R;ua zsT4+AG#x3>hP}~Tu)gmNBpeKhJkLi`31W>lAU#OA6Rp5eT>OyH7>46!F6=7dz zCB}L5VPXu$taK}qX{L?yMGwjdM4nI3z@XoiavA1fc@Ab_g~6(yX`dlS&PdhV^-W$l zlnSfuw1K9^sc)VwTJWUKjr4lYIO$r_GBAh#o7_a3nI^g}=NYVQd&2eW=rVn`1UDJ{ zfeyq5Lb3y-k%shud_xwWp=;^*1vflJN3WDY`AD~oz{k?Jr}Ep!8T#ZH%so{^ulPquuZ)g5B2jVgakq^wS}o3)Q5dp!@jKHsH~^E56e2VXSvsrYNf{dtm#1;Tz2c> z4dj1S$>m@MuIAwiTw}1XULA!EiyPBbD66ZqdjUrus4JG zb6fzo8Jvv26l=+7wRH4tU)ohG*SewEbCV-7gD5I`?GGdy-876=ymtfwy zNtGP2O7T23w?lW_JIszw2P;6=7Tuvh$mqzOrZJ%C8L^Bo_Z?Xe2ZZfYpJ4UE*>nGq6$$@i!YEWUx#ca?gnNjW0F~rzv2(E zN~`qlkFxB{?6NC0CbcfO6H1 zWth69u~w~6bj<@=t!#G$4FnG{+)GErT!*CjvDwJVr62{>? zf%$rKNSH0FWz=psLQOZ3i%*koOXzKe93e2N*BT|bgLBcLAuzcq4G!1#Hg<@C zMZmF%^BZu*e82>*B|P0r&j8hLP<}mI{Q{FeP+EX#T%8W!H-qbBy05|om`!CDVGgwh z778fKEX?Ovpg9(<;EQnrms4X`;o68X7iMPcdXBNB6O2^~a1$Rc;5P=ha4i5u)b3w{ KCHxl=!R_Ax8W%1A literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizInvariants.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizInvariants.class new file mode 100644 index 0000000000000000000000000000000000000000..1eb4e98400f23eb482133d1e9562e44b98bbc44d GIT binary patch literal 2849 zcmeH|OHUL*5P++f#eIl^;0sV3*4F}N_TtG`f`Ez10zOuv$KIX>8fK@*?%oBBSFc{Z z`Y%j0(Yrs&Sk11zi?*6cJQ)x3=&7o&9@AC*&+d`41puJL?X-`0z=ig4FdTk8Sw&?2#of1SCo>f2uI1#+>=CcE#nT40w!Cj zh!1=Psgli@xAcvmTxx9dAmS{>7^5JiY?p5Fpn}@r7EidMhDTvbFeNot9l^K`dzt+3 z^*P2L_VWpa%cT-TlItLmCTLN^wgZDM`F@HC4Z2KC*ehY!D`9xJgwF9`2?tzOy2*%E zDwVPM_A>|=O|1VW555 zlt{L2+GZQAi-fPWTTQMS6#Iw3cwI6YZ`hv2b?;-|Yzs}`QT=bvgqs+e>#Bw@F<+Br ztsTb%O4YjE%@}48P7_%Eec#)*|MTX@VZ#$6%nKYutK4kM=mAx<#SKm|0;hk9{fiu*@}6BDI6g1$yom(w^~IC${l5um_#xq)*f{JMUzR9g0H^f5tKnE@wOA3~=$+psbWuLS5q2vZb{Ly;KSr3v|TB)t?87Evn zC|BQUzaV`{&uH8k*;;MblY~I|F~-LDJz=-!gcaO(b2zlSx&ZTdY*+z-kyW8J?rvLW z4YPo!3Z7{IWB4`_-MGg>?84wG8#8d{vbw6S{1LC$HaV`Vmo*!{O17h8e zn8|>6>PKA2fS`UvkO8sdN6cnGXg{Ko0rAR@xR?R)+K;%D0rAFob|KP9h#KCx$L% zm?BU+WPKJTOm(AYokPJLfjP7c{Rylz3Lhs^5j8YtiHB*Swu^f&bLBVM(n<_45|zL< zo;za3vC%BKR@NCNm9tS(yJnsw2(vy8)B2c=L(SZiO1k?5E^P0#2~6$lSd`%sf%#$j z4~@~L0yRR`UnwK3PWmFIT&Ecphi!oqJA_WCb`Fs?@c literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$ConclusionDateValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$ConclusionDateValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..fbdba2a2a9478fbc9b244ef55468b93839ef0311 GIT binary patch literal 1481 zcmc&!OK%e~5FUrNNtg6Zp`{PDp`iA%d*MV8QdK}8h45$^32}0~L*jPVUTkj){4peu z;LeXi%x=>rfs`BKu(O`=_?ww;#{Tj1%Qpbnhr4AcF>t91vsl`;=AlEOq|l+&Lh{(^ zckYxM47swWXly97Hjz}^Nv*;jk7abWe3i*htqXNuDea04yM3UQh%-z6kGR8uPZ=f{ z%pda}4`Z%6;fwY$3ddkpj2vLFS}&ZOOfal#!Q&jKN!lLmz0Op=(UMlSV;qXhV293~ zaO3!Jo&;{4;ZivpHneMGaZE59qcF{n=_u6H1F58Y$l%J(Zi~UhfsU{Yvkc}2`9C&B zn+nV`wsE8kS{?T=@`X-P85dOesnL{UV(C>@jRG;#VUBP#9%q^(fI%zIwMtQFDH1e zE)_Piq>ZM@i8BU^Mdw@G$XxFmCtO!r2EF?Km|q&3g$}yW@n*2LvwP{hR$z_6*59Xt zhJ%3ZpTO&VuEyMhoU-}n=(;+3$qi4?(N$&O9qP;od?IrKH!r=RLEZue)n|$Z?;z&Z zqAkO125KEuLyvX8iTAZ*f!Nk--7CEX%f0-uN=7)p#m4YsN9JB7XW3%J>`7XSbN literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$CourseExecutionValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$CourseExecutionValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..16925c2d2f2f44860bd120c4ed69c009aa78444f GIT binary patch literal 1466 zcmc&!Ur!T35TC6;%UJ{qsHosk(ZI{yi%()q2*GHA^$*a*__W;{+l9N`b9b+x-@*q! zi-{)s?uRnYT?_q_>YMSQ)0?@O-^~1G=6?SA`W*mv;C>lO46@XPSuAZ!^U$GCQs~fX zA$e@|J9o+rhFsZGG&U4kn@B3|q*h^<$1>_YzRKjs=7qYWly=1nyRoY?V{z{cMV1E* zoXRl8VBwf|c^Grm4qvp6Q8)(kVl;w5ZGYTro59NKOgWigQ(D=IaVRB&O$s>S#_{12 zg;v%XE|s%kL%T*6#{{!F3Ul}&9fg{CB$afJ8C=@jYBHGG)e)BA3Y9siY0nsKDzL!V z>X9;Nb=<|s7dlC0T(ID$MpKQ6edv3$haL1I6fw~n`6(ACyp4XHyn{9>G~8hnBvKe{ z(R8Ii8}!K^v%V+FgTV;M>w@&UA8E8c>3+hU==hFu;#o@k4(Ci=&>wryAA2x5_KVEJ zV;{;{?{y=SR2!E!JZQleMLm8C{c}b<59Xj&1rHV(Eax*iAN_$dQnj~pg6H?8!bX;~ z&>V8&ET6s>JZ^F$bG>hzavfZ^`ZO^p!8FW}ER!}vdWHUH=~n@^m`fl(>KQJR ztdjPi&;h(}F!!c5{{^o8AZrPhNapc5<}%4@@$A7hxL(+9z{*5~n-dT|O+>g2tCZ)w ph7zInYM6xZc>=;3MNBhlH*e_<$rAWP1i?^Y4C=5!jo&Nm_201$t$qLi literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$DescriptionValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$DescriptionValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..29e65d8586371597c9ecc2326b5b429c2aeee131 GIT binary patch literal 1472 zcmcgsTTc@~6h2cfEd>F&sHosl(ZI{@i%()Sh#F0>Uce^Cr|Imm9oU^&W~N2`V#Lsp)H-#yl+rH$!>&C-D~!zifdWv4Q3kX7 zyw9VAt4{Qyy^q2%n2ZZFgSnjovFnUfolOQyuY1bL6kF2Dc7ml;4A$wu0XL49OA}gI zXSh_(MosOSy(A%;jlY0Cq}mP$yL>j~-pA~0+VOi*Nq3*Y$ofW$!RWS*u?jN`W{UbB z8>3APE;6?KMj5m^>0=xSon|u0ClzEyQ$MM_8`LNEx){VL5*mFRWLzBZ4h9WEhaFUC zxWhP1r7+r}=}Un&94zQu^pMsP7HcBZ>0%l|tkDMK2Pt==8#u~|A2QlQIcMg=fi%K_ zG{UpeIL%y^Mk#0GxEq;f+Bn~E(Lx~ddV)3vCljgHVH)PAU=kJ>Ec$((NMs-YKj4`i zsj%5g+i3PU&CDxB3!b#Nk=`E!M_pH126pGa%bPSa-9cB9K7)mIFS&Ew)ZjXU)jtb@ z^gwj>9Np}5bX^_4k@9Lfr_(b}Nto-&yhQ100Q_mDBX*=Q8qOHPB2Jimi zoM@S)X%rs<2DK(y_f%U4t3Wr1K1&QLFb3mftK`+`S)=Df{thIM$~^c8SsxoA{3J|~ zy+Gcb+^17tVfyXd>}Qz!PSFZ1k{u_EXTL<&<30CfxRS?~;ObC@YeN_|hcYa~0_8cW pq2k%|8ip}E7{ahZC&m~x?pwM+wgLf-#23lv*MK!@_f{Tn`~v+(tXu#9 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$NumberOfQuestionsValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations$NumberOfQuestionsValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..6141689fc264ac441cf471b4c3b9ccbace5648ae GIT binary patch literal 1481 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 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/QuizValidationAnnotations.class new file mode 100644 index 0000000000000000000000000000000000000000..35a1cea02e5b204b6587a4637a5f6ac994426a6c GIT binary patch literal 2195 zcmcIlTTc@~6g~sULa7$YT`n#awRqW8WoEV@f6PP^ zefLKh&$gAeP<-2$`Sx69<~!e>nfd+a`%eJafR_#o5cnKfKMsYdD(YL#q+rT7iU}GT zb!gA1=026?jBDdFrF0-9wL(e1K|>L=KKEjAad2sFNU3ZyU>q1CFn&xM)DNkw`McF) z&Mbl9H$n>gmcU?f?SQ~gNd?@2Q3AP6?v~a{kH8oqOCP1?MuiO?xJ=a}5jIzMBdw|- zubbCyX0&p|-GH+Y=>~2@*$J(2w}9DRjZ3bn<$+fhOe@26LojZJbWty9a6T#I}1^9U>I-ZQdZTTZVnr`Idz<<8x5cI{bcRU_(=(l#l$Q-aHy zeB3H`|0$BmzzE#V!Y#N%V7gfD2)J*xkhS$h;9OZqz7yB0TvsS+Mj&5SjD`nPi^Og_ zhwPCs1iqA0Y>9OOnSB*&#@~sA$nuTN<0YjK$iA2O14|(_hGRp`5-=&!Tis|->@U=0 z6MLr9)C6X4h&XVUfK8K9Nl6yQ1`pj;PIEVf99SX{r%NiU-+}uC&eEjQI&-^+doec&C)2Id!hfO;JS0$0liF@mq#i;O z*L)7F5m0H;=&Q_RAFl&X3CMJ*h>ObCsOQw@lk2J1>3~n*Buyd}VZ$C4xoe)W9c-v= zp4{jLRsdg8e9{4t;7c%!ql4Bf^b*9(4;cBB&wPXIFSG_AhhvhHW&;$y@oh|dr`#OH`FaJ?zS P8C+=*XBTir5wpC%6VK_t literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartDateEndDateRangeValidator.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartDateEndDateRangeValidator.class new file mode 100644 index 0000000000000000000000000000000000000000..0ab88691b2ddbaced4d77aaf41b96f1c5e6b09aa GIT binary patch literal 1868 zcmcIl+invv5FMv!b0N@DTJ9G&r6`rE%?nSYs#JyYl(vYLO7Mi^HHq7ey|SIO@JoCE z5=ijQM=3uCKXYZFSvoYbn?W04H|%RR@8d(NELQlXz-G1U?W{c~+{kR>p8 z$a<_A;X!q`c__FeFtaX|bQ=VwDys(svUMGb925x5j%>G#(IyX9NM`4?GQ#SpCql|~ zCzeqXo5n^rBhj(XXtA*07Bmz*!kj}IGk(Ncf|k)bXbB|@W)=n=$&I$c^duKH7$BS2 z<+^LE5GR6nJ=9E6zq5Oq!#KOc zI2gU$$0a+lHqJvIfuWo!AHON+kej}-`w$CyepP6vYaKbn${V{ z5t?vq9n!j1enTpETGsVsl^SHNo)EY-Byb~;HnFZu9IT}|##m0AxYc4|!a4;mb(%8j z;attmfZHmvd=LY`ek2`ru8dDwUa7Wk<^HoYX1-o&ooPUQ2F z8HPf_D}+F9AO!;TN@JqHHLSxCUL|b&a!HZiKq0l&w|TfvVE3;HUQ9^@rh6>vBEva^ z<+X^!@1dl*cp<$EmQz)qFv*-YfmaiO!k%p5UD-AG#iuca_3yub#kzjEHU6Un8?_uf zM(t0OhCpdsT8pi*kIiOb3Gm*=zX;%ne8CKkIedGXyaN=!LjKKs@iUaZ<8B6Kar8ET z&m4}WWL<>oFrVz*fCaP~ddT2j-@{^x2b$vHHhwWp;8x=74%{7c=F{vuyO-i@M6mn|?GsA3 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartTimeEndTimeRangeValidator.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/StartTimeEndTimeRangeValidator.class new file mode 100644 index 0000000000000000000000000000000000000000..212e635f0c72e0f3d72dec703d3f3ce61f4dcc00 GIT binary patch literal 1823 zcmcIlOK%e~5FV##^B~YtTHX&gr6`rE-3weu6{)Jq0i}RQs01e*uSwkQ+AG^h3qOdz zfCLiU`B8}3%@edOJ9*%YY>>LZ5|NF@9IDlphRGC>bqx*HbuBZa!=kUBdiVwBA{GH zu?*APG&Z^uipV~tJ-t*tVPtZA*0Ihoj?jeb z>45HPl^CRQlZtMqv&Suj8xrvBD}?>RN_TH>IlG}RBV*q6q$SX4UUa|I z36zu0zz&q3@HDLUA5xxPG31~K*9okh-C)>X1XlP&3_FSZ(iX(#-cP0SiWzJghc^X* z!l?EH8nx!MtaU8G5ndK-_WH zy$s{wDA%R5h%4}7cauT!5yDxbTz!(&I@zqjIxoXj zSWa!%U)o_+MG7n39mY7hkl51PMMBHBw15YO>v1+lA}yWp}Tj-^I^i zqKUryp^UTFmR2pwgFM{a?%dqW{APYL-+z4m3ILmMuK+m$uM-!hv9vACLMOPAT!&V3 z$zrSDxkF||$do-4#)e#L6G_FK)GF+;SVsM0Qzw!?O087F+UIF3_WIwnDL|gU+&=5E zFlMS9K5gv_?g-4ROC{X~fvMW+9)bLpjzj@U1ZGFJ+s0^9gmWaf@Io13b=(sX<+_{5 zIEzaYqg%1)+K04UYIX#T1dlP_h$f65u(qI8v<})r3B#O-g0AF7TVZ;V3mXhj{ibyu zOE3aAJZfXBG}6LSr5)`u$2-(vOmUPTqVbrC3kLNJ2K5Ynt>v;G_sp$ z<2>LI1j?E6@mqopE7CW1?-M~!?gQ6XM*7rQ%R0k2!V<2dBf6!P zS4icK8@ZmH{s^#^X*L3Bv+uR+x;DuH`gG#Rtzx>q!V`c{rzxWz$f<0~P=xapn1%}k z7Hf?WZ+4xLs$KWInr$E@pQPQEFinP3Au!+2oW*<0NPizF^R6Q;fwzsb9ECc8vL`(b zmi&O3ys7<%aFe@&*o$zP!1CYc2FHfL0zYo{rl(dmSva#h85N!}!;lm>`~(VvCKGs2 zYh+EVW7Q9sacq1UOd!vo_x06RMYu^|`=4o@h9d-~dn`_|k_Cj|xrGJZ)v4j)vGlE8 z%5t2zmIBWf0;OHq#%-1wwEj58p#GLRExPry>*Juz8}$O*MZ6gx0+k(Uao4t+I4TyF z0k^r+L!P&|Q&PqABg+JcLX9|{3=71t<|E|Ic JzVnFS&QJ4)9G(CG literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicInvariants.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicInvariants.class new file mode 100644 index 0000000000000000000000000000000000000000..b8f516f408b25e87cd071f0a208ccd0b8378048f GIT binary patch literal 1535 zcmd5+O>Yx15FLl4NkgEeUwjoXITet0FFjE}g3?Nr(g13R)T84a61Thd%63xv%aA~V zJ2(CiVs;xgZ54qcao~`|_Ke=ldowTJe}4W70573gg%X2zsSk&-ay=tLk5VgXLT98B zu`?h1iLe+7?M~3TP#R++rG-~Uha(ZI=T*!lB<=KJ~g zA(1Umd*x5bGW+NUCVDF@mGxvelIe0QugFoy4AzSGZ8NlP(&YLiU@BLE{_UO0Y1_1d1-wxp9^SH(-fw^RNsnl$9YozJRbg1)-TElqmNW yeJTuY<`B1MfOs+;;?4{Zo6{lg&H(XzI>h=65WCYLN^p-9DZzaj6F`}ye((#>S(AGJ literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations$CourseValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations$CourseValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..9242978d7e050057514021bca296f5592fe4ff42 GIT binary patch literal 1417 zcmcgs-%k@k5T32jma_=*Lq!FaiUwZpUVH*WLWo8atVU`R5?{7^W4my-d)eI<{mV== z(RcqS&L;pT2;XTO>GX6AnW`uZIJ4&g}!N(|D>N4-S3wh@s>sg*R5Gg67f znUDTLSd4^r7ie81jj^%P!YiYrzDQI&_-vX?$<~#AsI~FMiQ7IhJ?n6ER)HA?i)W%Q zqD1IU^rn4=(laQ_!jZw!@%6)f25awn+N%^>%BgmOV*wcK(1mkhy%^6(8tuFlN_!VI zjBoUkgkaVuVVa+_NvQHOrIkNmaARk;#bD;h#8`n_3>HQ?J-61_Dl9U#@m^bWCh23$ zrAafD6gBhA8p<(sdpwwHc9F*@6JjyunULqAgS<}OVFxu@;V}+VC9QF2`%0nr@#9(gstMGuq*53z#=75mg2x)?iAyCf7 z;%oF>6TcN!r08kk7z8J@77|~moSV%*?^&4B%b@m3(^MTL!Z~ymc*Nktzod*(zP{fK zstt7hrEwm%fHsN#Gz>~G3uTfO(#oV)={-kJ71(kvf&8dgxJj}`+S5V@2)@Dm>C(a% zxc!5yC0HSu-_J2uN!E(*0Peuu!gdeVrXt**g0MFg;UTP3oXZ?aggVG!8p6R8gblhe b%c$JEq)n0~;6wz$P+|<~uuX+OF6{N+Z2*`R literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations$CreationDateValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations$CreationDateValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..20310ead984ed2586cb6df4ca4feaffd68dd94d5 GIT binary patch literal 1474 zcmc&!-%k@k5T30-ODz^bP~_K9(ZI{yi%()q2x>IeVzf3f@nyR=whMQ=m)&jAzsy7v zefN(t&K`vxr1)lh=yYdx_M4e+X71PTZ$AKFA0CvU#K2}Q8YI$oHIEz$C54Wx7Lq4c ze{v_>V8oR@L1QDKwTY$TPHGhmc_QP}V_RoZbk6mCrL@a$?Dm1d{PdJN9D9^uioyI5 zAMz;Ssuvx0k5D)UHBoS9u+q$3oJ=ulYQYna(k5+>?%ocRzvxISy9pLzGT5Pa$J{tx zOj2lNo#9eB8@04+4U&Xl)-S@ezhoDoW*$o=-4h1ac6K`qrVezBWw^nhKFjfl zVYEdvlmcxyqW_ZBdD6pi7s&0L_C^qEv;pZs%AM#3j)LOnj7Tm9&0IL@d^qZSc(L>6 zp^Kd_#B7|FBhyS9=PMr95QsdVpo_s7CDkg-!9oqHu*hJ^H|ebZ@0^jUx91tIH>JYX zAnl@Qb0UqwLf-lgH`41P-Xv=Vq z!N-3|DTC!AZ{tq_gGvjnd!em^Euej)PZWa^Ov4PxGHElUSLlD1eidMgUIO`1$#9)y zjkJfk4p99La~~G!U*YCYvX(wzn|M1e*i70t_A=A literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations$NameValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations$NameValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..fc7aad034d0de4a89e5d4b2419238c477b9feeed GIT binary patch literal 1435 zcmcgsOK%e~5FV$Gq-kkOA3%Y&ZYZcduzTSI2&q&ckV-%hDj{)kyhGw<*IwC93cm~q zB)IdV5aVsqBvn9MKr4+s%&mu~=Y0C&nzV$jKb+)b5h8WDSxT1gW-Bb7*< z`QT55#aL)}iq^%_7@H_9yfQlOiBu(L+lI-NJU-VCv^Kstal3Vq;qask(+rkRL{G%2 z(5?7o^8}@5Fs;dr!OGF_=#95Zw;nLqc-_@r5sGrEnc@gIgFU*?5!Q>*Y^2f7TcNaf zaozZOH%$p<|1Vr0Q&JuB-5d|u_>_-_oqedZ@{bry?d=~^_J=0HGAuDz8s`7RT4O74 znX#?6+M+XQ4-+m;maDYr4bQEidNOyPSLYgSn6YYf)IKnLXLGaz($=}2j;ce5ti zhM=Yesu(HKV_{X$`^IVCR*pe-^dBirbSbyc2c9!n+Y2bHOY;-V0r!NPM*yg5vTbLh%&i^2PU*%Stg<1d{- zrH;-&GtR>f&`Y5|4ucZRz%0o!X;oS)w9XZKAQqHXAy`NT+Z6fF!ve`mq}?lYy7d(n z->ocvhL!JREx|g;S@H?!S4al`fPEFN6}AnyJ`v%@1cd#G2%E4*aRxb*0(z0dB!s&Y f5Vq*X45PwBN!uh#z^Nr6Nk(4{cB$Okg}wF*A@G~8 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TopicValidationAnnotations.class new file mode 100644 index 0000000000000000000000000000000000000000..c42eb69dbc65a770ea7bf77f6415fcb0fc881296 GIT binary patch literal 1262 zcmcIk&uc*W^m}=JlD@#!n_LfHH%fOEDI4D)bWkgi)*4%ZEyj*MXp7FIV@$X-S+3H_(|K-9KgG;F z=V7%wM4q5b32(x4A+N*$d6TxI0cy0uV-jUbTI0};l|&cKSgmw@!6jOdqqRAkCQl4H zPBqVjmqYI9D1OO_((=*VM>9dAnV`{4LFcEJ1zoz?oYGNQZmcgDFF0^H!NdE=|4I^u zP=VVuxCPq`c2C+11<$=zdT?4OTyHClol(|D+Z7}lgGSp(k@kdDMLlbl{ZKgupWEv+ z#VLdE+>ETmH>yB#{D1#M1sMjlx0>EzD;3TmdFnocY@Gm~v&CtE~VI7`XMK zusHGgD>Cmgmc7Y(%Qu&|kf$3XvY`qBVpM)usK7*O?$ zx{Px89V#Ci;a8~rq^bmUip5T`zeBM$-GV#NplSo|QY^te*rj|x^&ZV1Ko#~WKcsv} P`H1o{%`21nJ{j^av{)CTJD!^Xi+Lv_6rgcsUnpM9#9I1hDz|ZSue@9_O7+NP76Q8 zCm?|Y?|c+u>|7jBX!}4PyjhRNGjqnCGh?(V!$nef{8kxZbvO_K z{?d7RDEG&T0iSrXBbCp!u54P zceF|kQn^V>x3b&pEStHxjD|7eBm*&m7#xvj7xP Or@wVr$KN6%c<>weH9yV( literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentInvariants.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentInvariants.class new file mode 100644 index 0000000000000000000000000000000000000000..a9e276a4c0833ef1afe55bfce233f7cde32e0226 GIT binary patch literal 2638 zcmeH|TTc@~6vxjNu%(uZC<S|Q$Rv(@Nqgjw1c}do0(|=2|KJ4ty&VPUBl5@^}{r2%Q06d4)A{0Dm#kST9g=s5Vvz$r6 zRLv+RXlT@-J*Jx1s5Hl1n;KI}2SQRSl&tk>D1x)crs`=)BQEWh>{BhMw5AB79!ww6 zKCOjRc51urBhIV`m!1eI>@yEWR#sacjMi1ai!koNRHog~TImv8_C_XONzIK4`#c~_ zMX?A67D=pCJLHjhOv>ZUE++wJA$l5+n6fvt!$}p@euqo0spWwm38t0dx-S?v{xF`O z-r6(@Ka0&5X+%{LC~k;UmPFLDF0p8spTro}dBd^whw1c(>GUtAGc|uco##we&q5MW zth7y{&f*}9%EW$~lOJh8aq73@m{>(9v8OTrFZ}n@Q-%^uRA3AyJ(yi-WF50_wUC|l zq@$)9Lh_wn)aJTLu}M6bX(&d+R??WbAHE#5U12;pX#Ab_U-w`>ZJm8fwQY)s?YUHAu$9hQKPl}ZETc9%_MoyYjKM;0;yg332>2JlKN-LTK8?X8T#L9{!?zyZ z%b%e1dZzpVDqnC{fGJ!P4d6A6YvoJ@S6~Kr<8T$Op(-Ifyg-x~43f@Ptf0n)j6-%F_ literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$CancelledValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$CancelledValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..24f0682cd8688d0dd91f184be8b51faa517004bd GIT binary patch literal 1463 zcmc&!O>Yx15FLlMNtcwCmKF*WwrN4_W%t5~BBWA*KnelTG!o)~9Pg00_1Y`jN#U0v zfdqGc6k;~b#|A}kLL7E>XKcTj_h!65et!7|0EckD3?&A=%*Q3qtH^A*clUVi(<5w!CHN?{=^uCTpuvleACljrr45BwiOogFxaCLXWV*T ztWFs1yya4R7dMP=^pql)t!bF%r)(N(;gQtRKW1=gZ@Q+>37LDKmb|NZ?}T%tyn%N5j5H(|sT3 zFZR8Vvv$ErrkSxmr#&GOQXBL9Z45`c1OY6;Y83)lW3ZlgYSjB8=W&f^RPg+Ox^Fw(BXSn*Eq9wRSHb0+Zu9K|}z602R>x0-0*qn)Qa|Xh@nFzOGi}H+W pC=u#K4YLs5&p^0CC*~Q^%_-d_TY`{;AQ&o)K@E0^_`N~A_6wU$s^S0u literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$EndTimeValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$EndTimeValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..d848a323d64564878f521b925b29c521e109f366 GIT binary patch literal 1469 zcmc&!-%k@k5T30-OD&ckq9UM6K?5&$FTN;hLJ*@#tww4SAGdpByKuLA+1(cX%S<%U zcmF8k+_khtt3DYYZtiAg=bM>tX7AhgPhSAw0PYr{z`&(G?8VBpjR-wTt)vN^kxInQ zyz^(mVkopbL+e6mjE$5QUKt(sMXaL1XVdho771$K8W|6?Ha>T|og*DJRf1z@5oQ=H zo`}8(W1&0Y%k~LM&!C8dlML4Cxz|%zST|C{PlU$|WS@MmdpdKplvC{(Co(bErMOdJ zy_l>|8tuFlN_!VJjBoVfm|!+9!Zbgm7op}JDy{ru2A6jCS`20mO@u|b!k{`X{K#5k zOR&h;_8V={nYfP;mnKP7oHx%?YpBM=J>cc}W*2#cGA2$Vo(g#?I>>8uI_RKAOC3f* zqNFtrZC^=r!I1f%tbW9IFrEUP9Wh?xkwM2v=ZWyL%RObr_bHJ)nK|{raPq-$^1;Q) zkMd7Wej;aW(3471V|~{4xDhV%dUzZ8IWOfhEWmOF%CN#-JsPV zL^pxBF(hZo`ez(1zChnK(JNu;(s{a>6n;!QA@Qlo7~A>Fg$y$08C0HY+PcG7IEStX z+YH1%XOqFwFMk6Dr3O0x%s3BQK=+OQP7De#3v(okq|K3DqW3&KC15L=1Ug53!)1~c z((dItK>0H)yj`w-gr%=!Ex=WhS$u}MO0trFm*E;*&uuqgZ7RaeDG2wcB5c4WW*N1cwRD?g0XPvsFjN?W8thQxcXE5}2eVDAbpQYW literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$NumberOfQuestionsValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$NumberOfQuestionsValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..d875fcdcc5896630eb92b9695bbf8267d60fdc02 GIT binary patch literal 1511 zcmc&!O>fgc5SS1L9{P zfdqGc6k_arxg~-V;^47oJo{$m?c4qF^UF5?IDiL5C@}b#xS$(J+tNI6D3laBuv$nS zS$*m*xWRxcdx6FVLTeLB#huhD=FX}q zbgdny6=90O!Wr-JAmXYW9JkI;I0lRA>KX>iwQ=5&a@a<*&tUycS2-DDQ(D=IFjtqs zF0DA{#_@c&f*Np!OXX}(*RI};B7)hx4Ac0KT!xx?ER}Rm7+l-kYciNR)FBq(27~!= zQ%{Z2rUVO&ZNF6pt&VyadP2vEj4~>`#AxaY@>r-h$13}LoeatoVU@dQnuel zg@!u}{a6a4Et;MbX#K%HT#337`~G-OD0jqv)eALRk9;rYPINp+mGON-MCU6f&L8N& zALziptb%Qej^{4NlB4SgL8kqb4^p^#{Qz*O8XNr`mtjGO1{#jU=cmYEQ@8%gr~`ZV5IR zZ2eg&Bosom6H(Xlqzyq+))ro(>*(+mH*_c+om>Xx6HWKdG0kY)bfkM^3*ZqE1VfE6sKO2nem{#>e*xG`zpelP literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$StartTimeValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$StartTimeValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..88bf754dfc2ab365c5e253ad9b05d78a43618eeb GIT binary patch literal 1481 zcmc&!-*3}E4EB|6?Mg?-#(=R6ZrwnggpwDY7($u`2&7UFE0uV>UQByiau?lQJNV0x zK!SJvD1?*sCo1rSc!(3*cc1OgcE11k{1pI>;9e054Em`L`>}EzBSMc-D``S!q!O_+ z@BO*37z*vq(YjC?VkCayaF@sC>gEoVOV-sN!t}v)hQh#c# zu_ah#Z11hM=uAAoh)a{CD$WVusWp^i;vVwya;t|tLKzdU5l@9Y6J6vrIvsRTqooR? zAW_m9hjySOx?sruFIGS1JeYKX&W>5H@yMX#r1M00+2fuf@e8fw+3@fl!fikQ!*vL9Hnt&5;mF^y9G*=r+ zW3!)h(6$6I$6zh*e_L3U=|kg!?_O!Qh&eN zu*X3Bb50qokGY%t7Z{Y9==^izJnRDP9sQ{o6krjSNES(3BE3ZKWqL}$)-wrojw*-C zBrBvn$aR477g%|>R{aE5zmc^7*GOjfGt3Q=mHfL5*WpHPy9t|f5pK;vsLw^%h8>DC q%Ar807dgyBxIYKsHr-fcRBl$%9g+p$Lfgc5Sgj7{UAeDew}qOoM_|HvGYMZk@e%X(L3j*T7@}StU(o)7_6i>buNV?XQb-v zrZmqVN`;MH+(y&n#9XQYEqK)8MyCESIOV$1GWdM>Ur3wOwAMj)=@Kwl-rgz9JCOcv z1@1A}xLP=*8v?gif{$@I#7?fDdGwP?$*$$j~;^(Ov4P>GI=xPSLlD1 zeidL#sRha-Cg3{R8hMX1AE5dJ=H4&Pe}kL9C|ZJLvT1#axk9#fgc5S}C_gKK;HZ3Z((I>IX4V6a&9^voD-YOuuE z&O2q$>Ue;WFLaX1IA_97jiw$G``Gv9TRrq66fqGT`6(A?yo-K=vV$%vG~8hnBvKe{ z(F~+O8;q{uQnVl{D6WKZ$MiS+NTc=1_Y>|!&v#TAKc>WUxpL})kqm;741!4+jO&+W zP^#G&&Lopm8<&k;4C0HtAHReC1uvcl3$RiL4^|nhXE${rffHw>>K z>@eW}oLvT+SJ*3hWKe6ObuYAaunn|{^buoFfmxU%TP1If{2Kkw)2{|>HM2l@!~k3; zTPN>P?gMz=Vd4GC;#XMyNzn?dkJxoJLry$&;6SIuQ%|^OUwgNs8K`_)9g9hx<;1BY6;}02>zXAXN literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$TournamentParticipantsValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$TournamentParticipantsValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..2ccffcb088366b1cf9331051febd8e5e1b72f9c4 GIT binary patch literal 1592 zcmc&!OK%e~5FV$Gq-kkOUr?anrUlx|?u8RYNL5t?QVEELNE{unN!;$*E89uwg+s-E zLIMfy{3yhDn`AdtKwJ=)+4b!Bo7ea4kDp(@0l+>yEJKOFMe4$CENx4(&;@E(EnH(ljqBAU|5RxAny5ZJ*B zr>_MRW}!jV*>-c4Gj8jxQ>ocbtr3x+9yywMlo{nO!|QqB%RB+ zpt!)7OJMGWLQ)>Y%vxc~uuXvd^I;R9~Y&*2jD7 z8*nr8t;4Oc2)D-|d>o6g0e3OapoWr1&uSQla4`nq9^RND*tlTVFb3| LKDPTH^KbnETm0Ox literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$TournamentQuizValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$TournamentQuizValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..b0639dd93f6ba6ce5cccc9643802a9bb288d7d09 GIT binary patch literal 1490 zcmc&!O>fgc5SSieJS6 zB#_|Fk3x){G3i>>f;oz~Udq(jXm1~WH2TEy|y|z2~`&T`Ap68Td zioxOu@ADwysuR3upP+CI=G_P|J-}f3=yK9NgN@fcg=a9FC0mQjb7YF z)8xclssSx{)Z#{_{xCS@y3#WEc=Z27OcGk^pc~gXgVo)=!ej$w?^oa^gRQ?ygcL%E z_D{qys)m3mRg2Hjb#?fX8(Kj}d&;1CObaCNiA;$rtY5x*g>CWE3t&)trbv8;5w{j? z8MYble@-fc^>N`L4Q_MB8wd}bH*Wh{;qwH9+jL@@(YWbIcgU8&BO(Zf8e>q09UA;@7O(#X=n%M) literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$TournamentTopicsValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/TournamentValidationAnnotations$TournamentTopicsValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..402f7c639a8b1c019173a7db657121c34c5f195f GIT binary patch literal 1556 zcmc&!OK%e~5FV$Gq-kkOA3zHQH!aX!b}yVLLaM4FkV-%_jl|LMn#Ap{y|SH@N{Ii$ zf!}}x65RPwi1FsJNfaS2h|BDHcKprEH{a~fUthliz#%*=LV>`C)P?O>+Jw~b_Sx>{-)lOR z+~!e5m?p4%&N?iNnQDfw8s~yLbh!a!_X>fPlkuHJY+^m5!#JeIk`~PYF!z?biuRAL&RG zVTr&}UjMN%+LYirAv-01U{bpPtl`@R-3|&Zv}z1Juk$5 zcimk$D8U^9+kch`ssu6ZB+L=QdXS#5<(I;>bo81TTwI6SO`v>=OUK1C>FFC<+I(Gy z_ABs(5?FYlP=ZG>vsTz5>=I!AoLT~#~;=Iakibb-GvZ+G4VH< zXrk}_DB~GecZN&Qm))0sb1u_<=j-Woe*OOT0|1uci3dFlc;doFENxZu(4kOL=+J5* zd2IEL+vf&DuIxS<8w#yWBo%j3tFXyq867`Xbi*iKN9DHL&Sj;vI|^G5`WOuF@g@&r zu4>^%bq|GOaQV4Z(!F5NTVC8|(6^!^^k9I&P?o!Dj5hr+$k_ZlWzg!liIFdKJ(2Oz z?0#Z&HO9Jq<`)JkyXZ$KVj>;+2^SxD4gC^j2Q^e^xWg!@OJTG{)06^j&?@be*R=}@ zvN9>RT~f)9G+Li*zs{Z5^&PE@2MN`cUpa9>OWL3%ZE#ZB_WF5gb8D84Ym)Uu8<&=y z72=B{K7JMbzq%9(&<|INa0#w5m?*DhGHy8|Rc$Gid1y^4TyNB?XeykPV=%g=1&_D6 zk*VEs_PJeY8Hlxxx5pBL!WIcG@TE-YEc~OLg0w6K#aD{9bS382BDL5x291tWU0C5t zU>swVH9CW_bG#l*Gq9Z}w5pML{tEUq4)Xr$8Rm7D*MED!EAZ)vZkkO^1rL?ikc%kxt_c);MWn^ZR&@-Q#tR7UY+$b*o<$8J)q=tPPW z`b}r#&ISk9>FB?~^mef@2Xwd7uLck@x|J@|=#ll5yo|8$9s1vo7QRC9Cs{o(L}Qwh z<`2_YJkEj<7$vJ0#%Sz;ahM={4FfPq@hQUVgfoP*gf|Iq5zY~o2p0%%6W%2(6W$}d cPxz29AbdplnC2TNoTQm%>FpA|QKqcm4`Wp+&Hw-a literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserBusinessRuleValidator.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserBusinessRuleValidator.class new file mode 100644 index 0000000000000000000000000000000000000000..9eaa6a3bc81a7c961518a6a396efb18ef9d6d862 GIT binary patch literal 1734 zcmcIkOK%e~5FV##^B|?Aw7e;9peU89-3tdqs!FB6DFs9y60d`^YZA8`du4mm!e8Ns zAb|vTeiUN7n>3-?LJ#z?UVA*AnQ!Ks`T6VXcL3Oc`z0t4cpF}5h&lN zK(NTD5B890?lWl)xi)^Fln#YtRw(KBStP=2yKA_9oERawF|U${?`2m?m!L>scAxc` zA2HeSU$pmmUQdYdRJ<0RMh7o4OA}{ zQR<;{TOq4Y87ZNF_xl1Ocs|m4P3}B*TO~4+Qw=YAS!OVDx{lA zI)RY(xQuJ*?xg)yCs_A{NpBnOw3_!C4O)I=SE?1Kl7oR-MHzJ%&Z1I|BUJk^dK8FokOgPY=>FK=m7xU(Z#)KXeql@d26=p|`n2}65TY@5ig=02iVa!xN-0K|+ z?g-33mrA;q1g^CncL@|<>qwNKLZCjeeQS(1Ww=hrk0WJ-)$vF~l-w1|P(9E?!^O7rJy`ULoor$P-?|d4lq#?YOK9a0o?Jp$&OvCN zBNWhf71tbr+vgB#GeA7ehqyBX#6~{E-5DTW0b)BJ;{FT}dwCEAcz{JMz(Z)_ IS;TJ9Uk+=he*gdg literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserValidationAnnotations$ActiveValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserValidationAnnotations$ActiveValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..5b5366ab779b9b2f3890e228e5890587c3580184 GIT binary patch literal 1415 zcmcgsQBM;=5T32jmRbZsP*h;4XyE1U#V0W)NHm(%N(7o1pSF8ryKuLA+1(cXWhR>F zyFbb}cWvoGs&D#mb2qcQ-^};T?EU=p^*aFU!=nn67{sX$`>}EzBSMc-D``S!q!O_+ zAN{$o7z*vq(YjC?Vuhde?VlZp{fg*+2oXEeMtDMj%e)@mK|WTbR)cw1szVKy8LVVI8n^t&Tcx{unZOH8rLomdI%tmtX~tlw zX{3nT!m7+41!sIuIR?7VHvV|ef=pfp_1Bt)>L3=*p{u|H1|R;RWK{6g z>&>9rLg!x@=V1%z9?^e>K?!D|OtwN^nfxlf=jf>dTh1)dKB5ZO$=1nxocjQ^Z!rIU zY2gdp{6WzY+#;Ku&oEcW*7NTgtitU)b_dp`BHW#V(42~JAJ!?)xP}s;4r`c(a5x2F egHFscqMK3LBwGSbLJ$lU#-IV)MEqeMZ~O*GCYKZd literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserValidationAnnotations$NameValidation.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/UserValidationAnnotations$NameValidation.class new file mode 100644 index 0000000000000000000000000000000000000000..9533b7c6cd770bdce0e59d493fb0adc045c56dc8 GIT binary patch literal 1430 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 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidCourseBusinessRule.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidCourseBusinessRule.class new file mode 100644 index 0000000000000000000000000000000000000000..23ee78ca569040b2f7482b6d865b69a9b016c243 GIT binary patch literal 1358 zcmbtUZBG+H5S~S#P*4y-M0q)?VhhQgiQj~nKq--=yrh&Sgb!xBw;l_3yO-U)Vt<*5 zCi>kUWt_VfTBXJhUb1(yv(L=z^X%;X`1$1<0BplX4u%Mv#nwwgVVa71mNO}s@{D4F zhDLp~*Hm+lN^{M%@t9KD7m`|`q}QUM@H=KBHOnf|hVLXsNN!9m33(2N2~1zo7WG0Z z1MjGL$(hZ;D1nupmrAK@C(rh+NnmV?g+dDZj=;TQsX<`4tb9y4L13yET+v$TF}P32 z@~PC^sIbL-hp8wQVV2X0wQ7buGH;#yc>RJqK4&3v@|~EnD;jX8fM_@1l51+Y??!@Y zWw>q$#*N!${)d-kRNbBeH>T32;P{Fg2RS(rwd}&NSd@Q?k$Hd7*t%WO-LB~F@1oz9 z?ic;04(ea%&=FCrv`rcJI5;fR)M;|(Mv{CU@-Q_C)9{eM=vG(Qxni{^<%!io2AiqW zQ&l{}!z5~QU8krXftjjeG;B~U(s|dJi}0XPLIShJ(%np0D9|d2O}2jC2PlWsm`wuX zCnCUZN;IaJFP82=w$|SvDi_mssHjii%^=JMCcdKD(*BM-?u2Zn@%a-xF2?snN@Vdb zz3Qey5Ll=s(u#;Tgb_%z*N{kJwU0qx)9xykL|o$OVy%@yn<*=4tfoR*L`-zMhWk5h z0w1dXfBWuUyo@8YN@KmH!Mm* zAEDt4j;e8sLD;1jv-S_ju?huiBQRGxJ*@8^RMrUKoyRePf2m;@gB%WQ7M!i4KYj9F zA%8YA`57L3$Jr3f;CmD?fa6i-Nq;@FFoy{UJcfDnKgs+HIDgtVe}>VE*~$_u`n7TNWVEq^BBb>M)@uD9HD|p`e{rj(yHNOVBR~~@!B~$9txkpc}~Q|C2yjWBWt&bA!=^X za|0=~GN@ZppmDnt{G}BsX1u}d_Bgo_l{PuYQ)nCl=LFn}bH`G4ypD*1{_K%;yJc{@ zWpMv2!|fUR%W#v27|(TZ$skhNCeV9e9FdaiG|;)>CYyyUOijWxJZ3Pu)n#}tU+Hmr zVzmsLn+fMr6)NV(IB1}*aiWpIOhpOq*SVI-x~t3tRB_^x!EC;8HxeakY86K&-9PIy zDEZu&O$Os9vPu0FYYH)+FWfQNTHhwCOvKy1;vR!{gKRb^@Fmxl_xI>=$7M6|&z?}- z1n$cO$jW`)>n4maSg6LKl>yeJk%YL{qJ(C-4@zGf?C&K6l~ib~<}$Qo zG0|-w^mf|}&MW_aG5>KVQ`FolO^Fu=AHT+R6O)6D(7|G&DmU1!OxKD~*4hy!#`_25 z${v;auw46aM1nKetBRJKsBg(<^&XRB*syz7v%ji p-C2ZJG{2>cOEf#=v3v$hKn`A${N@aX5LRF{`K>`dQ6Usy{Wl?&tK9$q literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuestionBusinessRule.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuestionBusinessRule.class new file mode 100644 index 0000000000000000000000000000000000000000..3c2bb0c8e4cff9663270adaa93d4d7915e5ffdb6 GIT binary patch literal 1366 zcmbtUZBG+H5S~S#P*4y-L8I`9S{Ckh&)vOZf0>CU z`rRL8oNEiMQsW0N*}K`P%7!R2;9jRssx62b$}@+2u$^Yi^ga(26qXe zXUYhx!=?x**HJ9Pc1{`_T@OWM-_Y!MWoy4ja2NZ z5}x8w64iyNFw~F0Oi6PVR+*9Myz9(Fc+@B&f!TcFb|x&;Fgl5Cd%e~N*bSMr8wAEr zWdl1ZF_>aLU$_O?T-!oaCT6Wrvw*Fv!^;STkbS+QY*=ELPqg zqd^Bpm4(9~>{N_d{RiYkhmyAtm?@tfRSph|s|4`o;~2re)i8`f1_w3_&eqVMKH0C3 zeLpk#8SZ_@*$~X&dlWH%<3Zb#{(xp-4igY~2=nNF)b=mn{Bhs>2}VC{uPnk6eqUk5 hW&Bc%Sg8Twkb`GvKd-@%fETcuzFtB;bqOfI+AmsvpltvE literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuizBusinessRule.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidQuizBusinessRule.class new file mode 100644 index 0000000000000000000000000000000000000000..4ccba37fe78cf31c65aaa9eeb2a74d22a67d5d55 GIT binary patch literal 1350 zcmbtUZBG+H5S~S#P*4y-1VNUn*g|q=;x{2Ayp%{%UM!`?5I=19*0ON7d+zQP@sF8k zqTl^d#<{l83QhdrC3`nJ`^?Nf&(7Yj-`{=!zy_>jppU@2*!f8)ZA~-Z39cm9zSUf^ z(CUxwf*Ij6WiN!WKG)g=QZXmB@*6CaLCdVB=G#Pm*-Wfd!rF2YiVXA<7(ZhT=7&tx z{e#+>;4TA$1Qt48rj&NAEF0G*fuS`XN+sPofjjv^l|cWN4lv~ifw4|-(HL!p;4UF^ z$I1w+!-fbb*HJ9PW=@DLdVG-mvq)dkHV+N%pC409szBFT-m z!Zaio)@$>=;r)a}y$<^;QyZJ3ffkk`BN{QsPpQL_;&Y6=yGzE-YYXnR1^50I{JLp>_l&N}98<%8X3sZD%IJV@3f9Oy&!>Ghv~+(MfEZ>nB}+t&mx}N?`a% z*0GZkgDIx-gj6_vhi8MRSh!hsP_TSa8+nOhlPm!?`~eXCO_u1!D{op_grD7F4El$;BFEWCD$8Q#9M>ZDwnZH!8^Of(l#FIUa|7# z01etZsw^A^VTWSO(mx=FI+Xkpftm90er0#BxI_SNJB|VTJN3g5WN=`k;A|QF>685q z+4mEpU*X&S$&kk1=|#xiSw=@cR-g iF5s79#Nr7M4mo&=_Olb{6Yw0C($@>fr!D~nSpEYcESaGI literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartDateEndDateRange.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartDateEndDateRange.class new file mode 100644 index 0000000000000000000000000000000000000000..3e87dcb84c8181d8500ba647054b2c1f350a319f GIT binary patch literal 1376 zcmbtUZBG+H5S~S#P*4y-e8V19p@pP1@tY75gc3>0OWM-J;Fs;*SQhSfFS~n%{xTCy z^t(UGIM)_hrN$3la<{W{^Y%P5yFY$@`33;nu%3bu1|K6=j00&Knim}kC50|pEhG=D z{^TyX!6H}o5{)eit&J}gcT%fjiwDy0iuFXSI&R!9cUV$>vQ)TgVhTnX%%1ZWF9x(P z9yQKUxD<>t$n>>bDebzUwr@lR6PqHCO1doucXIhUgV7!BQ^+X>GkxchG1^SPUB*^V zltHV57W$shVI+f2OfNFJ5nyQFdg;m98G1g7fMEGv#Ki?~qL(G>LK77ls@pGwQW$N~ zw4^{==z;%3D|Bou;uVTGVWZGz<66|lW<6h{^$467awpC_N7?ZzA_@kxN3PIoK%v)w z!tV{ZsbR1I*Lf&=wu?)Kkv1-Y-Us7}4!K?fy=!jLX-LD&G|a+%2IHGOh8J?>KBrY@ zq-t&?oX?b{!oxUhps8`9k-=P93m(+Dk;%TNOoeDIamiplm%kl}5;cvEBis3ZI$*F9 zaBDXhOjc!+CMz}+VlkJ$WwN=pMOLYZw*$?625*MhtW)3%8bdxfoyT35jl@4)rM?N= zlL?T_-{-cFaKYelC00&`SeI53*8Xr3j^zOm0|UIPMI54{22;AsuzGg1vd(a+99c~D z1_%9jZ3d0<{~yMo=dgp6J8dZC((uA-+%z#MX^cvWiJt6myAmBG!dGiYm>BQvm&&`; z-h)!@{SgV?aIXqFa-vx!pOt@@9P2=eHW{WWCx^BDgVG8Ex&r7OqkC%G= literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartTimeEndTimeRange.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidStartTimeEndTimeRange.class new file mode 100644 index 0000000000000000000000000000000000000000..e192cb163e38895a2b47cb5402a26056bfdf9d44 GIT binary patch literal 1371 zcmbtUT~8B16upB$p`ajwh>BgRVhhQxiElzk5K1H|LfX>A;LCLOS_XD!HZ#*Azsy7v zefLKh@3w_jsqw*0X6Me``!VOtnf>we%QpbnhV=}LF!+$TpcP5m&^&M`loUF!T1XyQ z{n1@;g8^6e0*wuX)+UsSJE>LB=8+7$W<52ljvH5#F_u)AP8F`2n1N9SvuC``gNW9_ zVdD&i%fL8;T;I!;(ym)-``TnMu_+>{q}yU}t5B>n7~Rn!m7HQQ(@!oLqs;`|W-NcK z3|bwvG4zCv6B%`CdWq4E2xI%&%TCr#(F;*T1S|9sF3x!qy*x$xO;l*O!_beVFxsMN zOM%w!f&atnbjv6nqjG+qh$~YYoA*ME)+1bA%$+#(95u#I385csoH)P7z2D>B|DF5m z_JiDC^`T?PcOl6*(Z;2y`#3z&;ni!PcSTD!3t5<%hFQ4FV0^Pj?n0s5r?cveRLzZ) z?wPVw_^uT<(9}3F$Y8Fl1&`|7$aLLvW@5CKXk;*7DBi3@jhaTc65IKHG7zv6acegi zOjc!+MAb4>VzE%X5wf|qMNyfAUq+gT3|%%g6~Mw*|k(vb<=lPQqg z-zDp(TrgOww3H(m*QJ$&wXaLUu`&Q+K)btIv{FT2NbPdN_Oqjvb%smjC}OH791P!F zGH8_l{~iv#f*qvXX+tfShi|;bO%u~CjnS4`L{ENrT#0TG;j3LHmH==ReXqhHo2OhASPNrqNyee7J<3=)0VW_1Psx6E0wV~|VKsfdfI{j*D8kw=$j6~0 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidTopicBusinessRule.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/validation/ValidTopicBusinessRule.class new file mode 100644 index 0000000000000000000000000000000000000000..1f340a744f9c67853c2820f5e3ae1901e7a8e3d9 GIT binary patch literal 1354 zcmbtUZBG+H5S~Tkpr9avh=?pzv4!N$#BV}Kcqx&jyrh&yLt?VsTg$@T?qzqc*k5L% ziGKG-8RyzUt2FV0m+al_>@zd_JUe?oe|`N909&x;!4QG>i3`$5+PY?e6I@BI1FN}Y zk=38v6*D4W%3cX$1Fp3RrD9HM6*O5S!;V?Y%&JZ#-%hPm!rDq22@i$|OkJ=h3nHc( z!BPD}aOc4Yfu)|8DWzQ}&DM=cV042=Qc1T-;7*}fBQU(9LrghNV6qonGDe$GxJSt1 zsWQUqs3}6qb)3kkos%X;*CP?zw=_3aJr^_-JVHt#O&Gsq4MFpW_8US8!<-2HSaPGS zFipvY^}EFX^16(v-{XI6YGd;>)WTAvL}TXoIdxc5d`^&ff6c`CUCI5f-1tgXH<~DOrdx?6BcS1ohG)ue%1%riI}zP1jbHe z1A8eom}0I_yam}<-9(g^uvVm5NZ`#N%o--XWX7@nemw4ktY`7L6P+3^_GCt6VQ{DV znGOWzE2(la7By)l^6a%Ea#-#o(AToNny0Z)c(_<Vbxy8Yot}@dQ*>>*Yc2bljKNwfSi-X*%twdztz5P;o7mxLzRDE}Z z1|1w#5e|c}M=@sQACO}mN!~)BS2;bb?jMv^2;hasaTotk!!QaS4r~>it)f4Ba^E2L zVS3^V-2Z{IA(+PZ2x0)o!?q{;@65m~CLr(#=FtDR?Vrc_lfL-^Mn7$@EW$JVzQ&46 h_@x-Jd@zd_JUe?oet!7|09&w@gCPPR66dFpv~|sVC%BSa`&M(w zBC9{TD`texl)Vzh`dn)hO2wSi%5Snrh8?q(nSoA?VzE$eJGD{?YpZD_axhF_`hqo? zA2HSNkLnkKyBv%XSn7G1QrdNLZQYmz#x{5)m2{f~?i5Qk0>e8x#FP^Rrh36;W3(BA zyMz=@l@V4)O%YPA<3vX7oHQ}I9*Nk#rTOvTT+mSP2ziAxVf>Oc1T7%iYX~I_b0YL& z$&I$cG$j|->$3mD>u%_JJteM9ZES&tT3Cv_Xv`cxrw$8?PYJT`FPu29E56qi-}_zs zoAUkQU)RBND0C>vIMK#stb05tZ|h3yf?ms#&qE%jCSe-x6Bym-iaS@V^yEBoMykeo zCiheY&+#yg>%s&K6(lfI(VRs!W@I++I&(1|Hp)m~wphBI2@5rhP7~W+KkEbRM9kWC z0^=vLfnAjvOfg?9-GXeaZXzm|uvVm5NZ`#N%o--XWX7@n&OGjftY`806Ff35_GCtc z{-tNVObh}G)l@kdi<-0&$@Ur(i7fZA=xg3x&C~1#h!E>74cgF-R@NCNl|#ftw{t|e z-6Fs%|9=w)U&1z5=Cr|@i-UI_Fw+p(o@PFX zM8Er^jB{*!f8)ZA~-Z39cm9zSUf^ z(CW|bni=6UWv_*?KG)g=QZXmB@*6CaLEEgR=BX8CJF!v;Ys*O}GB8MB@{%=}A2L<< zk7}2Ky9^8ySn7J2Qrfk%Y~7j!MmBgTm2{f~9^?yE0)sm`z?5SICc42zW3(B8hlDJg zDkH288zP`wN3jfBIcaQkEfkS`PqU+y3qb?HLu3@tnDHxC7c_@xuP&4@%!$B@BsUs~ zG$a?+>+rtgO>o`ovcEC4u{j!OVJR}A5p(>4IxH!^#K^n1WbC|-;9f^??{C3x3-=0s zQwO!rwaLgR*2blLy9_9ADN1XC-UyP-LKY^*VGvxc!~C_a~Kft8dlQR8_1_F0R>q71EO`Ac>n+a literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/AnswerController.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/AnswerController.class new file mode 100644 index 0000000000000000000000000000000000000000..4d93c5f3aff293d99eb9acfd48cf6138d4be8296 GIT binary patch literal 3814 zcmd5<-EJF26h7l7@lTVsq@)mj%2J>>rF8g7+fauTH%Ym1N=S+&#MO9rY)@J5Y-eU; z0^*8CK#1qysuD4 zM1c_l-WFPPeBKNKX{^PXz)DN19@U`|L9eUWfDfhmnszwaM!U>4QthMqOn^2cBZoq9 z57o}Bia?k(0%z(=Z30v4(&IHa19P)bg&KkLEfMes(V)ZCLx#8n=3CNbew!%~pVJSg zOkbcO+Y@*{PSnxBxgIx7-%s*^5bxBN3cc+c6Ve^7Lm6nkiJ>z4C|%tZJxpw*(D0wf zmt1YxOh-oev?a7zoxrcctg>ED@g6grejbT$6K6JzY^>oQf#pJvkcqS-0xzGsPr2D* zVTgG~vFSbLN9b53V{#c@!i*h|bWAW1M2vk=I$$ z=y$^Vp1_sE^}WsFIyPJ#D)d{0;VB}@HZ%RKMjI8lb`lEMEY;(@j86dqCE;PG+_Fp> zd4YvH>ne@~kmde|`f1hFBpmwd|F_~)S$1=L!bqKKE#u4W@W&C?=AQKSk!K$gcsx<7 z$&;dfs5ef+xhk_<#g2+Ri$X8QvmcMt|Fgimzn?v;PL+q_zi`{ph=vuoLtr=&o$|T2 zo$I=DVp7d|+{X@_HLR^8DI2@dq~nBr3foe6_R+sRc4=-{JF<7b0(S}YCyGkmk%{|x zZC6Ih<@ZHA{4FHWIGBrX#E_c5XUK|cz7)qwXG%#1c9?O9@UfDht~mz zSo{HiFAz+_46bE7#cL&9aiGQ1Sv*l(39i%Ep1|xcxGBN8^b+9iJnmK|6(K>MYf_S#6hgXYh` z6}&rz@^7R#Zx!HNg}3omIkvx!o7kHew1jH{x>Nvqxd8M!N=)NCjSGQy;YI;I9pS%+ zcQQXm`0u0WQTz`I@UI?^zijbu!mR@Q+avsy5q{$TTq*URK;DWt|49@+DzJhcV@0)O xqmWqAD9ps_$dZr$W64u8u~uNi$}u*inJB?0=t2p9#m3~F$K5?#*GHHe{{r^8e>?yH literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/BehaviourController.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/BehaviourController.class new file mode 100644 index 0000000000000000000000000000000000000000..337ccfa18d289329ed5ace3fb7839d1fa2b92303 GIT binary patch literal 2254 zcmd5--)|E~5S}$j92+O02FlO2OIjd-_LdeZAp%qZL0_CU#32&mY42`gFPv|$yL&eI z%aB^Byz@sP=FZMJX_qKc`@n;5e|UH1oB6((egEOlzX4z!Zr7p4;92HGo+uk?DI7+r zinOpgQZli6*Nvn>AyYd-V@0I3iB&3{(y16>C^IDWO&w+!oO&gP zQY13%iQmIl7&+Ps_s@3G3{`{-+MVkDWN(eZg{?ewYJfY+sxZOzG}W&3X&Kz@Xwwrm zGb-)v8acp`Hm^m91X1kD$Z4}jaQ82W_1x)^G8mKDx%(#)FJ zX$tj;G1|<-5@YY4rv|M~h8Xim4>FaM5O`)ZVH((byxG|9BabmkC`U2RWb|70kT28e zpob|M=`aokDl*!RJD1ZTrgoPn6MQb zX6r~FW6WxhY2%8pra*aArj3Wl_v3Ch{z;DK5OC>q;XZE89eZT z^{T$_Tqd^AW;(U_gi6Nkk-D#((&HfJX~S0d2|IA1ze?l z(tM8AH-m++>R?ot)X_H3^>ti9CzUK%r?bi~gPSX>-h}zd`{nqjKS9qdrB`R9-$SUE%A*7y{hg)hs!b8wz)U!ZvnE|&He z>BJ@aU49A7!dLJ${a0tS@eNJ(UDaqlcEvre+8$Tm(v4XvEgBen2UiL2*y}Zq*Xxy| xX-qyxmpzUctaz=jRx#Z;ruAzc>nGqQMNot9OVn+e{XpxWvMt~zS{K~h`UvwgmYV@gWoTL|fJ&r)1?bdNPHpVK7~ z%785iPf6jrOcr67z}Q`D*P^cFwdtMaU1lo+RXduv$>c7#8G-Fa?{+)fmLiB(Y82Wc zO~wg;WZyc0(;K0uc!zECfHz&X=y^g#hyw!i4I$e!@Fn-!EopVwo{;xwlc9sOW!Xx| zeRQ_FfG&ng>~YB)+;((}}R4Y&hgTRRf_t;w4X)?KGp`jY;RS33L#6g{ zb$OGwF|naU;9vK?a-k7rI#k@H4IZe4KK#nfs?}~w*0vN&^17@<{W|^;n9KDBSxB4Q zbFyi>#nh_h`EXj=k?aV^Ow(4=iJCHbV1}NgQp zgn6x@ZxEQs=3-N6m9A^CEpP|fx*8cuokxkQ*pi`Cbm{VWZ4m0y59NM;ya)zH$VmDOaJ(PAsQHK zd#@PQ!<4ioxJ}@jzQ~u&ckQ;PSewb&MCLR<{w>oqX|nqv-zpAs@doHB-|8b7?OC;w z3N9)pwgq>hTI)*B=Y2WC(Ji-1utwmEzJix^q#L~dBx3ql8k=}5ZFZCEdVC^|*2AaD z1%2xs^(e?IEU{|F+NAr^!QwLiJ(0gO(zkA)2ppsO^%TnnmV@}Nz&9CQb-FOcwumF$ zmXa3V7&tnHBZ@7-c4YbiRKCYf0gfk=0DC8}Ux_Ec7(9U^EJ5KUZvSL7cOu&QIDVbP z|5G0U3E-({;}39Lz&X|FAK>(Tc&0J)BbeXehoQ)T5&bxch)zXDCgaJ8krLuQ2j_8% z8m`a);xAL!BQOm!=*=wtGK`~F5-2nW%B%1i&MN9fuVY6$Jcj@@F5_po_~H1a2XN(A z9A&*elkwU}0M&8M2!X5E5Lkd~Irguo_TR)g-B6_V-@>Jj+F#7Ef9`1eOOgF$Sjn+} zGqry!wIBOmEhPRE$fD2`iKFm#4hnh%igF=}LX6~{+)TWiB552#@;8}S&A~8#1coFN W1-OGQ6kt8FH;%pcaJ-S)-24mJA`5B& literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/CourseExecutionController.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/CourseExecutionController.class new file mode 100644 index 0000000000000000000000000000000000000000..06ddf66a366a6bac62fb5ca323f7d1df6174b97f GIT binary patch literal 4697 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 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TopicController.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TopicController.class new file mode 100644 index 0000000000000000000000000000000000000000..237eb1bd1567268a54774362a64630d3c269c372 GIT binary patch literal 4198 zcmd5@W;c^`f_5QM(Q0LT#`gKnIp2If{{63?e+7U! zST90>z*4Aa?DMED9IBWba97YsxZLq0v8i?($*2=VJ4{B@6+(JEa1<8--C=bn2aI(e&b}w$$nOt|!E`gDn za2mfD$u}0Wh~d%Eu})b%A!yN5nIGSsa;$= zu*Msh(pVzwZ#rK&Q!`19758b4M{1@EukteLG#ZjM9L18HE+|o*#UFvGTwjoVw9W%B zo3cAhEjeL``Nd^@+m0WjOT~mS(Et#5t_*$9Z@~+2z6dW8SUq|YmSF^5!u(?(_s6Gd z!OQSU5iSt8lt;vzU+m@b4WXH;s zR%ND2*bv+^71Z^Pzv~49qg6ze;2wc5yNX-Jk5=4{B3k-8wAMr{U3Q!6`uem9)!wz^ zvOductMT({MJ$8W-g2xL^REZ_OO1%NqJ+q(z6cqaz#0#433$80b4=Ho*cNf5+fv%% zjR8l4IHK4RZ2QI^LHP&l6yQWU39xq(`{iT;48t=x!nzVp;r6G^+%snD6Zmu#|7Sl1 z62Y@(G&#T@&$+xG9Y z?I->Z6;l5RWKn2}BvE)T2L(L@#X!MCAwlwfZYDlxBdHxg@(-C<%E2&w2!=Eh1z1KG S3b11AjbQIX9Iv)*uKf*a6!XRa literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TournamentController.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TournamentController.class new file mode 100644 index 0000000000000000000000000000000000000000..4822a9c9ee6c4e70ab907a0ffa580c1cab102c6f GIT binary patch literal 2615 zcmc&$TW=dh6h7lPb>ilp1~9j7ODT2<9WH4pO;stqfFilHN`Q~c!$8BUrEi4 zijH~U`Klj_DAlDGYlQ;o-}UPA2R-hg>pg`#&QQ6T%IT4#H>!KnJG0EFz3DwrDndbT zdc*7dx3T33=~*;uPh;-o%E=OnCya;rz{4w!gztpxb(KyJFdZf?4;rsIHa?yVH8e8b zs@K7TcU`KuPGF-*^h|6nr(7%$1rtt!S#CEVFlSXPeB-8zc@BJJ73%#-8mlp3FtaQj zDIs$=2n0oI(uJh>q^KbJ1`z zoFzA%1q*p(yhH}Pi*+CvlcbzgST8n8VG_7BCP4u`3d*rz;z@lUi>uE+7p~Sc@*B^~ z@=YhgmleITT!u-!P}MQA<@K`sXqr0mRJcst-$L#rTmh_|_#Z-`0<&-d#~QBOIEUj5 zj#x*rzlbY}Bf)X@#tT^Z87CE3!rpBLScGNl7ls>P1y*r|)fU$A{Uw}NakT-j;;aI% zrAC(UNdw#K-vBZ22E2*y8gAQL>Cv|f{bigx1Y3w`7RUOp;C;Pz^#xq}9alvdjRK5% z2IGC)vs#7saA;y{VY`713j%ylg6?JsI+~(eckwujZiK6fpB&=)umsojm*85QfsgUv aC)l>IRp3_I-%c^C;rdft-x){gv%djER2#(r literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TracesController.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/TracesController.class new file mode 100644 index 0000000000000000000000000000000000000000..13afd030bd3c2d95e48ac86db066fc60d2723220 GIT binary patch literal 2808 zcmdT`TW=dh6h7nHapE{l2?@{wWeGQj!VZ+Sl%}LIRm+PLLJ}e&p2oY!>q*x;%gk&{ zgm~pIA%O(%{3yiPi|sX9LQwlsS>Bmh&AEKvx$U2S{rWoqbYZ&*H3oaJ`8o7wf7`5S^Qref1uu?^QjC~PHex!wm=AnwLR$+*`33UcnPQ<6W&Mij1Pool|CacCjz{iST&X!eNww{Uq;gIFu2s&I%ZIRsC;a~3VASd z@{!g`FTyIT)xU`}8WofD{XC$mQbS!DdA7Ub{tUb1F5hS+=k~<%y zb8X~4%TcVfb^cq`$hdRCalDLMr;pBr9MV{hXT=T2Dn~f>EXL>VZVTG*LK~J~jlo8z zH_MYlnZ3jqlo|}yddd^wvCuL-7cc8}C@FvUd(XDYD6iWA+HM%; zVPJ5hvsGCmLu+GxfF@Rv!ADeJ_Kbv=4rM@foM`gfUR?5i&P8Hn$a~V*@_ARp#WAb< zf~pdEc_-N3$$kuORk}+>!TU1u%hmiPT2daT%rj`5iZCIs>s*B4BD}$1_xzI0D)!>J z%3z&{%~}28tQ!Ox1FC2SpI0S9C5CFse|{SIGSN><{538nsQ8c5?H)DNpBQY;H{pGe zHd~8&t{dN}gk2T7ePM@_ilE@F%$cJ(v5R)7{HYbHlw~hOp(k1W;D6_4Ir(}zJ3N_{HKpip4(t>ab^$j`vi^6s3Vwd9|s}{?Qu~ZCq_jy z`vywWRAJCMREhTRu}mA)Mvi4Rv)#1A{o9bO3M=&!I}<1K)$_SpH1m5_(91mijIddS zHsLc$lH9%NmqL428bif@M5DyOn?RGCK3^ErpaBcCHtCeEi?lXqU4j-pm+5qiZZcY1 zzd`%kwadT2l|SfIgBR(UN~BuW>DkV2!3MmPy?Ys6A<3(>uferU|7E)ID*a#k4w!-0 zE3{v)(7s-&-60H(Li=Wg_P5||vfN~3?j71pfYfN81F14~y~5OcB++2-E-ef?B}`jW ztM5-S-IyAhF;-=$Q(=g~2PI4&PPNY%b2Cis6ceZaM+QEg+B+ZE%?f)3u$u(z5`03> Z8ihJdgEiXSruEJg)~9ff&UHH9{Tph?G;sg` literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/UserController.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/coordination/webapi/UserController.class new file mode 100644 index 0000000000000000000000000000000000000000..246d2908fd670328d055b14488580c0fa91bd8b6 GIT binary patch literal 4146 zcmd5IIA)aRK$!1!w$Bf4b zQNqZ(F~uqK!#%D->PV?v;WI6yPxp9@1%loUxmuFG zR?_ph%0oYa(R*x{QIGj`dZ%`eJDR|7gxjiI?Fxqz*yzD$LpVyJv9S!PIfkaI*mZhl znZTKi$k(F5w?rsv9$)Z%sbi!efw_uQbs7dr`1NhY8hlTx`?SU}FuKhgE!92-*)*UF zk(PTxaTkvroEN^(Hwg5VCbtOmFG-hM&<7($7y^sH$%^p#TGXg zm*C|*oFi}{i-8$+DZ(r8DuJB5PvCkfIj`eYD)&<0LB+$!!`E;0FfIZU2U)rwG?`jE zwD%awOCXQ5HJl|-?u@Y_v4*`wXsvwe@WAL%n1_o5#@o`<`h~!k|K7*3L_eHxlRgmc zX9Uh26s~n<1WI#tK*6qb{Wh#Kz0(w*avo+;qB@eIlvD+{LV(@%ksZ?8<*x0>Mj*Uc zEbTx^RISD}+Zx*3;Wi@Kk*=MVt7Hl^X^YT~e3x&d^11f5RJO*Q8YyI(npP9Bwk)^= zb4DfKB5 zT<37l$kg|6<_CDLGW{di-{OP5*nt)QIDv>x$4(}a&8d?D;yw=-@Q4yx7y!wWNt_Xw zf@zFq27l?t)oUpfngQi?cmsFkjnSJpF#*pa0E5f=87_S^cKIP({S{Z8QJ?LI+DZYH zanAsOYd8>?hwB;sZ?ycsjeDA@X!(B!O&|5Ykm3LQ(f*fW|I4tF;s0jK|E-q)B>s_H z8b5(f6ow*67T(Q3L61N&l#8>FAbBsd67RQ=R1P8en@X%^V3<1sLt2R(+`$lXupavx N!`TP8-e~!({so_~;5q;R literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/Answer.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/Answer.class new file mode 100644 index 0000000000000000000000000000000000000000..3c6d47542f05ce984f4d53c6dc30a58705db7af6 GIT binary patch literal 840 zcmb7C&2AGh5FV##^V34pKq=+M1+^eyiEFB&2!TKfAXpT^apFw31H1Oh_HM;n!Ko5R zJOB?>bv6x6R1Ss9{LeSvjQ#uU$1eaF!o3zW6ud2>E;9}jhdPq6Y@80xFlNCW#S`?T z(S{T9LFa6|3*;*{6y{NlY3eD(NP0LcYe7@N)9XR=prQ{8--~%aiur(g6s3GUp(LAx?jQW`_e|v zY0QD;nnP<{#QA~`WW6uwBkDqa1&2u1kbaFtLDQs^n8`4NLGwPy>&Z_8k*a<*kuCAG zXeD~};VHDgi_w6t$T|&Tt&7~aC=h)^JOwvlQ>uDmH(+Z9@&l&O%m=gxRS+&HwHDVhO{UkB+oUFwmilQf`~W{n%xx7B zx^p$mna4SE=Hv729RPZ8m_sJuX~r^ECX5_PCS{FsGB{-u`DG5<0@@RtqpZ-DvOk)TVgaXPWAz}K*Y#MT zua=dwcz8xQIq9AVEc~v+39=qI3-R-3TO>o^onjwm86|)HtwQ6y&*urR(cnJ dNIvdB12V`{mccIUrP$1%nL?{>fc;hB;0yaCm$(1` literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerFactory.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..bcb9ae0006742814a2d322974400419c961fddd2 GIT binary patch literal 2076 zcmcIlNmCO+6#kkmFhsy0xCI$;3!;qM$yk<@LKRhU!3ZVC&ZHR{Cf%u?4v7E6yEiX- zqDrbjkfhVp^O@udsg^L`r3=M zFL@a0ExXT+@VJWhg^9eD^o@>$*^|DAvR#h{fe`_>!dptrR=97qIVfO&VPc2xaWCX5 z@Lo4|gl`#!o=7F_Q-=P^{076obKMjU`Y}?(Ach#mYf_02OapU0=7_GUQfj<+hEcaJL@2&d9rM;kJa`)=f7M_Bk1a z!ir{P-j$KtQBAQ$IcvIG+PIcX?Lk|(CmeDdOkgU`@HE3zZzSv1NEK9LDU2Q~X|>5P z!V@)7G)P7-6Y@9QaJmL5k>Nsbc2mHM-db&O(GW(KaxU#a#Q8*_qOZ9vw&(?hc+`$57 zsZ*yF$l%m<$M|AJCfzvoEbH=%u?83}zU?S0TVg{-lA6YnQrae6getXC(hmvA`) zY!F-@?aeIwM)?!Qk5JxR_=WNzu6#Yh)y>kiL)?ft`jcCZCG2Io%M2zln{Z9102voS z;U;boNhLYOCK1yQGTM!)=W#ouewI-ekFbz&-u+58z202wVJIFm6tS2vWWbBbAuk#4 M;XciAFdqQ_0Q$ReRR910 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerRepository.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnswerRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..f5797a935e7af7cca8271aa4aeb6890174005a65 GIT binary patch literal 573 zcmb_a%SuB*49(QXwe^7;7p`3jX65RN(2bxJgjNKX>6i}F-p*Vyxh?%F_b&VZKT4by zu~J-#i{yk5&Iu=Z|9E`^fG!*r&@%8;D>ju+!$_!XgSQb=w4SOIM>;2Aq>$z;38&sl zOe`~RIem4JoD+7WjNR2(pX->sMp+cFZQyuJGs233!~5ZwtroCr;4+HCRErO8BsArD zlnFkQ5{_#^`KG|Tn%0B{I)m7V22zly+3JR_5RtX z^2cVkLD{jC*QJ5eeiPJB`OzmothgIOR8k*9GI0Lu?nzlV>Y`>`dzG2avciNUo2rse oNOsAB59^w$GT*|$R*s$g5|FDR=N9Zidxag?&)CVGgN%m&pM+?^Z2$lO literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnsweredQuiz.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/AnsweredQuiz.class new file mode 100644 index 0000000000000000000000000000000000000000..9e6cdb00a2f1f2d848a5c031c7539a5f127099ee GIT binary patch literal 2512 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 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswer.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswer.class new file mode 100644 index 0000000000000000000000000000000000000000..9596e3c9651e69495efdd0c5939ae6298b54902e GIT binary patch literal 4278 zcmcJR`)|`$6vxjENxBBJq@@o+TS^CPX($*rwy_l$ZGq7(Q2L-SX}`G6B^VOB*ba>S z6_cj@ADgsj(lqT4*dI0Rd#)WPc5P8jpvt-Lb3W&B@997P{`of%tPD;O%9i0veY0!* zxT3a>P+ri+TN4}z6;uj9Zb6x#YU7#l63A9Z>J7&<+C063(K6Pyx;8KDTAsDnmMgaH z_=ayewkPNW&OJ9=-_ZAAljZr+Hl@Da#Ia+h)7fuJUp503qtRH<@jzYMuv_}7$|k#1 zQ1O~&TmB6}Kh-jge#1hH`yk-|n_1iylwWh2vP4H{Qll}N5_F`txI0Slvql9fa_^)@ zqd{+0qY@qExl8p+B?z34lOwTQ|TfZThb2_B1p!W z+G1vmvD=I9p3Tk_R#JiRwRo~uAR#ez)bgSkL6d!eSS}%1hYt#x3N^=evCawT9QZZ5 zE)y}a(S@0S)Rf(m!_vdeEPOU5{J<5PjY&JOJg!bBF5$R3lb9@HXAqVCkBk ze3bGp)YDTMh!eDah@?Lp4qnb!z*b+cHwEnvduiSFWlOrtZ%t>a3p6$}2pC z9x3&!5i=JJRnOx^EyTcNzdo|1ffG@rw~m=Q9o3R>Vb9&=f-Fp=Z<6a7h0CG zRR4n}>gRhjU0>=^wSJ*TGxch|M|1URp-1!eYOzNP_3B8E&itmNox|^k&`mT!d74CT z3N>pQQcqA7fAe&b&e05=r&;VH+6wg0M^wWI8be#S#}?$&9NWOQE^#G{kwHrMG<@&lE90x+V`J-~vDYzTH3c?yU`0tyo`Sa1igYU26p2f+#HOX`dnlEQCaU3H*aYO`tOrPL4>8F__ z)0I@FEA(lapJtLwSHbi}1`}sg#KajQ=rj5}g=sFybS;(XDm10UZZ64m6HF@^Oq|;h z6KAZTFH@Q3lT5c#nU>R;=95f!!L**i#J4PB;u|FBS}M~*lBto(bUmGEA<6UrOq&@@ zeBUD`zR`kiq%xgJGVP=?eU;8M^r8iC!DBFem%+p@PsCJFOg9neASHIN#CI_#l0e@p zaaV%X=o;r}Rf*$AV+}oi3UYJ{J$?jpberzrS>j7Rmd94YCc-jdft~R+-Nh!pTsJXN zM-C`9l;YE&!2au-LmqsCGkqmFrDT++`@+9HgzzUB!W-$r8M&Bl+o)*w`p@j3Dc6M zZMe26dB?TisSEB1gIn%}a9pD+O~-bHb1qHcdaDL+w;j>uN*Hf?@>5N_?%6%Z75g8A z*;CTC5)fsOKIP}!=y0oTylS2bQ!yx-*ZpdjsSa8Uvh}Ng151gvaCUGt5OT=7g29~6 z_eeR?YNKmD;95g?>tmO};>ZSX2|U}eaBw&NOj=UyGN_k*SuY8EOJ1SBS*bJ_MEC5L zNWuinreGS<3?|Bz#uOlY3Stl^Yd!@Dm?SHof~kRZI|WIYWH4KomN@Kno5DHf&5k~_ zZSqcoJCf$!VpN?-mqDxk?{5`boPH_PiQRh59lQpG5@FCT|Buqt(3q`Ggu`H|E!5r( zLdumZzB{_SNQ`rDj?0JLKNZf8797ZM{#vCgA&wRr_~-l;Ja;Nk7%eaqZ(%gqaIX{= z=ht>AEZFamJiB`L<^%*%b}xN|a#DvTVia!J`gVu;=f&qquQ~ zx{$6CmMM&V)NiXrdfkD$sPhT@BZ@+V9spVqlD{5N{CY&`2U7e_*h z>z)Ofmhm_%KBoAPDem>eW1@yJxDOBT8)V7`nN~xY9>Q7}Q#Q!7hLy`>nCNzfeWF`n z=o-e93o@;TGF8HvazQ2onYPC;kxqt8qzVSrP^N_-)1y$P^>8Miz6W}Gf=o}xFp*-1 TOr+8XZ0PSMY~e^^Hvs+soSw1p literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerCourseExecutionDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/aggregate/QuizAnswerCourseExecutionDto.class new file mode 100644 index 0000000000000000000000000000000000000000..2de78689c05b423b82ea5fef4233ecdf5d525564 GIT binary patch literal 2166 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 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/AnswerEventHandling.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/AnswerEventHandling.class new file mode 100644 index 0000000000000000000000000000000000000000..b207d87bc18018e3b7be910fbe97eaa36b9756ed GIT binary patch literal 5192 zcmcgw&2!sC6o2b{IChgJO(;$IR0D;ifJGsEv@U^=CIoQWOp<1%!%3Dl_NKB{vs&ll zAK<`+3s){&xiQRi1{k>ak79UQDYD!wmSsUMfZZpdb_<=mO1T`%$IHn>nTg(At{dPF@Lm`%%b zT;?^*RW*4%9Nmn^iZDuG=FmE_OxMCG_v?q$mITIK-e^!kV7A6Z!wlSm8z@ZGRnR-++k!;&;+>wqKS_#NgkxkBLg z#bJ5$dm0wChxJSQ1V-1mLyIs?V7epfx)5AUz$G#|^}rJ};O-H144b!n=30_*&+rAW zyR;SDHp->FCN&&tyBJ=M;am12t3izgoLy;9j|z00Q|St~N*sjR(MlAA3x>mKV0c^_ zElb)>Lm~@3@iExaEWWJ70bhv&zS14={cu&z0)8Rd3~TvZ$Z)@%9T>Ldg~rsWapsV6 zDLNl(f`)rk$3!#Dkle5gdFs=0NzK_79M4>U2W)=L8YYzK8uRGA zcB@Xso`pF=RRr5|_btJ~empoTn=ByU44dAfY|*o1g=Jxj$|iTVEn&4V@&qzZsY6d= z0SR1LteuYmwWF&$ZBMcm-Dd&A4XlQFKxIKhxfxYEKivL72!71PBh%)dq$e_}r=~je zH5SM)8&)Uabpk*1kyg)J^GuQpfidMn)G6}FYH;e=iCb3mZi%#SOXh}~wcA4A9l>0~ z3V|m(lzF2q+k&F&OS`P$Sz(93_E2DVQUSNmnNwXsc)b9D-J}i{2E3rCw5{Ya)y`0W zw+Za@79cEZdHtV+RGbb|A(c*$7*Z4PPM#1*uYN`$kd{6%KaoCdKy|5q=@YpJ0?^1k z0q^IKF3sB8Z;5nqDojb2PL4{tt`KKd$2c$?V2 OkIw`i#INC7*!mAwU75lF literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/AnswerEventHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/AnswerEventHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..52bbe00c9251dabd66cda1b41c97360fa8359d8d GIT binary patch literal 1523 zcmbVMZEF)j5T32EY0hZcYOU{H14{el?u(yHDFmrlh*C@~h)~$|!cz#nV>9!brTc07-)LS}(;0(|gV6m~!p!Q5hScj_& zT8Dhh!-P|xY#XD^0<;)=^hO!9IvHc+3!P>%DP;09qX!A5_NnhR_J-(3C=zPN$j`X= zz+?2=6dlB<(9q3A!71WFb>CCHLD{rzKhkJ@rJbL0Cx*VGEch`)L2#2SnG31`3911J zP7lcc$i+9fNQX4<%%_=JitxXP;d5upM997~)Q+88 z=vGfEd^Ji3X!ba1$6&dq1yA<5k@>oE*4$892FVOJe@fd6j6QYq;!AWx9qn?%Q*LaM4FkWy4Zsl=n>oo);pdu2Pld=TG& z1PI>wD8zVoOBxzbE8xN2@nn4FcEl%@Glv+uXI3tzF zo!Rkw!eSz{+e7P;zLM5Bw7W{8tF;O4qV_K737zIjXO$4F*DMN;>9gBM6 zt(Fn4hfr#N45l6{t^5-PpI176=~rZ##f-8^Dn~D|Fiv@6589`Imd4mrY2lU8RoOPJ zp-Q??wj&t~we7px3?|l0icM%Sn0+gDMUo3LU$@rSX*kOo3$L|BXYyT4xitMk<)s5& zSkuk1?;i1}wbetOqRgq8DKCV4Co<&AWF2Ry(S|m~Bh8KLmXFoORm+!oYS3|QJnsuH zd)$*3+%HfP+|WzmqFwhRJas6MAb!pSeeS$ z9~8((Y03Ko=Q*KJsfavf4odgXs*r@}I&X!f1kv|Q%8wGPj-*trJl7sGwD&8EyHTrZ zXYH%B850%vh6^5|idwnJ2w;p+)GDtJNwK~!u?QR*V|)ZLf@wH61Cwx`!Td^RNS#;S z65&c}&32T=7lVEm?Uo?6FqrEYDe`S$RXEqm3ExwWLGKh?A7RrepNckv3l;x{b4EE- zs$YshrW<8%HiZ?`C1M7Hb0u4_u0Df>mCo^glm%FPInZA9aa%b>`K|`ul;cAv4?4zX zi7P_2b^`dGvF{RTsMY(vn%|_1rlXt>W7M98>kQtXU>=W0^XFdvdv-9G#PC`&h+dh2 zmH1SJ_Fo#EjySM}!M(ASprkFDz}{qy-W{U}uHA$sBGK{u7|d*_@}=9N3l5fm!5R7& z0%(vl2~+fK(&-^-gFgBM({JXY4>0q!w3(%SiY$TF1=?pyIf9EYN2hsOtp;48&t+N+ xuD}ApTqQYNTBPqGc!T5${Ms1srQd*0GPpq(&%jOE8*u9<^ldsX)0!YR{0C{KV|@Ss literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/DeletedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/handling/handlers/DeletedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..0676ed6e0a9007761c286b3e311498305fb08ef1 GIT binary patch literal 1882 zcmcIl+invv5FLlMX_i8vP)fPF<)t9BDLaIs;NGU>4D)H!eryIk@UfE7BAH+8x z0fKiv3NhZ@l7|M&#}R^e6?8Vt4zpA2&4x<(`(rB>1;&PXM4 zXSV&Wu$Tz#cG0?|ucS2&?T(V@YHfl$sJ%;iLZ`XXStSJP$C#tX^lA73Z4)LKy!~Ik zR?7(2LnyUB22&4}R{jx#&nun3^eeK=VkSH$m7|wf7^l3k`|T4zOJi)RwD8L4s%$T; zp-Q??wnG^Vwe8zm3?|l0icM%Sn0+gDM3M_KU$@rSX*kUq3$L|BXYw6PxitMk<)s5& zSkuk1?;h}|wb?_SqRgq8DKCV4Co<&AWF2Ry(Nbb*Jks2_ZuwYkT(x|erv@F@#`C`L zvd2An!My?{!416>KCbB%*Yt`<>2(}m@d3Rmf#|hdWu^Lsu|6a|OodCKLsVVl2bHOe z{eFRbl$N|daGnzim5Rty=Ad*BtqMtquJcw%N)Ua|r2H_!>PSk}$}{aTLwmQfxE-~s zcGkX1n=w&wZ@Azgs;HHli~zz6WE)q(Ys?b!L^&PL?k+zAA^|3iN<2E=>BjBGUfE7tK8SBX z0tD}T6k@!)B@GRz74Ts1crre7JL7!&{_zU{tihcoG#G3bJ{jc7b&W_oO0A?xoRLc8 z&TRXAVKEWf?W1)`UrB2m+C3%F)!GF2PxtlXaY-MjP4`k2E)~TRv7BS1n)WsX@oJ@w_j* z>~T+C@Ss3Ra6>PJk866xHNE0ddY!~qd`PcKAbKrVS*dC5XOfQht zowcvhW=vGv8!mW=Dr)5>BY-hRQLDT@B*prH#3FELjPVh~2xj2yEKI>U2A5VkL+ZTt zmIzl;Yp$a-z8LhoXg3A1g~39{NRe*|tHQZfPWqm540@;F`Usm&`BbzS%vbyy&Kc!U zseUO2nQoN5c`2-*E)g>roGsacb@dr6u5^y~qb$JHR|D-;AGefKlZDVU~jlTHsv8}!j9n0dPpeSq1orOh1e(_{&>=4qcTNZ9nbst^;XeTUCu9Tw literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerCreatedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerCreatedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..f4d318b81b091ff9fd4b7f14deb2003990fca858 GIT binary patch literal 2979 zcmcIlTXWk)6h0f$IChgJbraIio0?Kcps~0YlG5TjNiR4QNE(LW0oKySRw`-L-F1@o zXYs-e%)mQ8is4&HPV83Dco;Nuv^u*--?^Xt^RHijC!#vtE71s}pAzGC!$7wrca8Ak zz>}_)Ucf^wcg+D;!sW3(5K6mI;3=tv+7CRT^Icc$i`Zy4>9zpUaqFpULkVt#zi!V; zG|K3PRBcx$Du~;=ibvu=swb`|VG5z4{%nK8Kl9&qV|%a@=m0j=<5-&1c+F_J>B-35 z<=O~!7=`X0_nv4OBUn8l_aXQVsUmKGRZ6&lXw2x`Gd#tPj3)6irG-c{QB}fBov5z#+Q$H;P?G|SISWE82ZmX?FP@YoNbw|k8}+N z>QQS?c-GMQW5I^(DlOIzh1azyV>I=&i@4!41A^HTnN`C72Jno=ZUu2*ZZrCMvCoPe zyKg7;DdP(w|B8b&u|3cFKwnlf#Lm(VqtS-+MTy>oqsJAru9Q^c^cG|Majb-v;lA)4 z8}A@Yi@`~hM0G@Z#i^9HJHqjW7owYdCqYf{ws008U2BV2DBCNwf$pkh0n~-NwX|sU zg5yh}9Tb*>V)8nUffjL?AYuJl5>w0jyO#HNZLq&jqN|qo_d@1N5G7I>>+Z1wj+Yj> z(-KZE9+|KbrmJGdpvjy-J3(xtgcu<@&Ou;0&WrsIOk;a3?Zw5VN`lld$y&mn-8=}E0ta%*$kTo-xwTg*2SYOTN3@@>rDWJ6=HV& zWN1g@Z7|wL1McF0^h-0YnjXI}=BBWjq4y@;xRF`@km9X0{TqS3w&pUi{(G@zhY4LJ zMz4WR(FDCgB^t+f8CTZ`Rj7)+Nqkr7OukOhG|tar?=;pb=$xh*e9Y48so)$0><{Pj zbsnIWzd#q$^-{iG&etpHItd^Lri{=6q&|W{6|9xt=-T(y>%Y+3e}EgIckmvAB<@vu z7w?JmyFxc;8Jr8cUZxtBS5Kx5sJKM6e6ttgAGLp_X-hi4T11wA;RZ$`@~*6Dnz(L rUz`|Ws}P|+1j2WP2zQ4-_@NM?F$BVog$Qdy)TCdCu#Qd`p^d)*dNt$ zPLH>r#egmw847@eF{AlR*zUMZ8a4& z<=TkkFbv#1?mf{eLa=&5?nCe!Dhas(Rw?ENq7kEW&+sg7ZZ7-2bN~KI7_MmGo#yR! zBHB=HU6}53p{Y^_Dp_)@d58j($7$pN(F*P(0}%6S3b{jwq?3L(lr>U zN3A{KSwrWK1skfHXt91Mysk|dqp7D|#0{Sr5X_#)tP=h=fM+yzOGeV%X7ux7pA|WF z-%jdN#ur5X6$fczd!F@yzN}`5ouwT{qYdSY61@pWk1J?BNmMdUZ!yLnM~ToX*cZNI z<1K@<7@Rm!sE$ytIF<5tM>xLl0(6t_#Hb107R~~sYi$vU1j+Mj1Km~20;mgjYiZHy z1;Z{k>sd4&{(ba#iEtD2|tp}A&G0PqWkI{u5c0ogp)2EEu zuT+s%?|)7rqcgCq-apQ&J=@aO%?H|~mC7!Wbf!&%Zw!t#>*CRtEs6f{^(Fwq3Nbr> zGPI-dHW=-r0e5jg`lT6HO^;s~b5q#N(0h|^+^8&nNby#h{taobt-0K#zI(A|hY4LJ zMz4WR(FDCgB^t+f8CTZ`Rj7)+Nqkr7OukOhG|tar?=;pb=$xh*e9Y48so)$0><{Pj zbsnIWzd#q$^-{iG&etpHItd^Lri{=6q&|W{6|9xt=-T(y>%Y+3e}EgIckmvAB<@vu z7w?JmyFxc;8Jr8cUZxtBS5Kx5sJKM6e6ttgAGLp_X-hi4T11wA;RZ$`@~*6Dnz(L rUz`|Ws}P|+1j2WP2zQ4-_@NM?F$BVog$Qdy)TCdCu#Qd`p^d)*WMSRb literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerUpdatedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/publish/AnswerUpdatedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..35c92134d0f9805e66192e71c735c3caa1eebbbe GIT binary patch literal 2979 zcmcIlTT>iG6h4gv*bM}-0SS`Jgct=4JKm!Z6E_Rw;$l=TmgR%?&NRDCW~L|8y+HD_ ze6dQa^xYq2`Sr{!>?WgO%W|sD^qlT9-?^Xu^RHijC!!kNAEhBiKgGuF1X4E@ca88O z=_yw$Px3&kJ#)wt;qpiyibT7i^b(~-av(jS^IcaQh{$L+?lb|?c5A8ZPV93d{B?Ua zO2dq9s-)%WIFV6nFX5p$RLK+9Q;`uz#;wJ1AU$rRit;~37)|f<1MW)YZixiO$mhHn z2u4%T4|W&!Kz!%n8YA|I(L`NE+VIHi@}MKmz(4yiMx&{H4}C_JMv55lsO4@MYso63 zQhL1oECzJR$WQk!gj}PD31qg@Mjww{+au>6WN1ZsU>WxMUgV8@tV0d|&l84cH!FGlH2IC@e+>q(-LF?x$Jz7r)vtKdNR zj*Yhr(qeGpM4>uDz2cNh+il_a!VAz%z7wM+cuP17kgm2wBof;z)xPekW&zZNyVbO4 z^@8Iop&b;KgJSa9j)4|&6eD51T4Gbp`@5R=ceTI2Pot}v_xD2Piwt9x80+qd1CEy# zxziL*Hy)XAB1|WV9D^ovq;_Ovql6eCI?kaqZRf@Q2d1&TmiFS}Qn^gmX>pt!T4FTQ zTS0jn+aygkWF)@ogiVobXOp6m_U$gmxPYd7bJ(;c>a*GSzo#BrW<;Y=JUg>Cg=s7Q zYi=hWx*Z0T;bJ=LY+Fw+F7?!F?$j84$mrU?*A_|(z1D@wrkG`qgU4vTi(SxAWArJb z)+<${)%%~5$mkp_tM!hvO4qiub@PEXVWrYbB%5JV;OqTk&ANEJWlN$reBB9vutLnv zpA7AIy!A)>c)(rilYVK&Rnp@Z#@r+}GxYAH8#5})A5y%Prhh})Yil+W>%AANc9_sL zV)Pp542{zpG)iOmF5&7LqB2#mH-Ya8oy*rrn!@>c?48A00bS5EjgJ|6Jr$gVfc@cO zzRm&E^7C{lT`%YBm3+OLt`h)qV9F3JK1xL=5HdjNzl3lTo2JE!*IaUsH8 r`r_0GTZIU<0T8|`M7TEq!ViTA^#KrmEJRovpeFr7gmrYn5N-Sozmwhu literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/CreatedSubscription.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/CreatedSubscription.class new file mode 100644 index 0000000000000000000000000000000000000000..99419b01a365d27d62efcd2cd18d9284382e5606 GIT binary patch literal 2930 zcmds3TW=gS6h7W0dt=jtK(E})a!U)$w1@{pP^BeGq)HnaB}=3}A+P7`uG<-VWP5Y@ zF+A`qkU)ZWeiY)&B?)1NcB3{z;$h^mef*uM|vsecgG82^fs$301W3(5JLu zD;2iF zEu0tVNMA z)n}ZXU_=duu7te$a}8n7ooKnygm)SII7fe+_25<1+&o=_!BU4GO-E`=rRYMHF!x%kAyTwB4 zmC+5DXR!QC?29lKdK^9;K11ml)MGOqqh-+S_w&Cs28)HZ-9in6z5Xj2SQ;GmaxY3; zQdIv&Y2_a>c)EHyD(>zvm|r&$Heiv#(y99!)*9P{B{uid6K&C%cpoD!O_HiuWbxvu zHNzMa_cd>~1{35F%9t_{@l?p?VvM{?(!m%tT0yZ6&SWMi^HsST6#44%$e`oec%BF^ zC)|@3JWMGqf7g=wpyWnSaw9m)jahUBC*0^3-l!xs)@Kf%y1=E-nKeV?#}O%n)9El) zlKZ0+c?Gq2GIjZ>2uDhj?Midy9XAJ>k5r89R>7Qltz0z^u>J;<^|GM&Sx!( zjHy239^^>LnBGSRk%ju zb@~<<0cP7b;3k3G%C~Fq4vp{9cOLw`3h?f~gWt}<`3d+Usmi?SBHV!wXT-n%Vtjy) z2)`;4A6FRr@MU1D7XW+oGO)WB0NZ&n*c{xWVAkN%JWCk?KBM^q`s%d$IfQ=!7w%<6 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/UpdatedSubscription.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/events/subscribe/UpdatedSubscription.class new file mode 100644 index 0000000000000000000000000000000000000000..b2dfaedace6fc26759496d2e1f92cc40fc64ee8c GIT binary patch literal 2930 zcmds3TW=gS6h7W0dt=jtK(E})mP=Y-rbRp;f+{UhB30VZDA`8pQ|0xX-E})-k8E!) zKZXZ>1rkW`&W}Qzxg;TEXg6x3N<54_wvWGa`_8fd{Oi}>0iXw8H=xEqraqj;$_4GO-E`=rRYMFPr;+d&?G_8E zS4KBrp26}nu`j|{=yCXH_zb0IP>;=cjFv&O-_QTn7%Udrb_+EO_WG}AU}WYBSKJWqs| z6Yfb19;TF*e`-m6P;w(Exe=V@#w@ym6K-@1Z&Z>R>obQ>UEosa%$gzc2w$? z$^B7^yn*CF)3Q++uOT?%R&I`DP}TCl?4>T3VAhx7`hVj>d!TVJ$Iz#S`$8G@Y4+a@w^AGqvmGm8VnZlTrjv%1hzM#IIo)| zQT`;xKU7A`TcI6w8*27t#0IA;BK~*y7Sf4u#H(*F4z``0qzrU=n}dy=r|XZlwjK@` zT%jw5ZgE%I*@Vv-9KNY&Q^BOh`Ry|%(c&4pQbv?y(B3suD{+&0JA-TGD`SYqjs#hS zGPqkAD59f9lU*etWhph9y7*cH0#bFJMGd$|kA#8;6uvFx91(RuhaG$j4Cd$&RfAiw z0CoCXfd({btkX^l+O+c)&AuV28jbDWpz~zq$}h0|2hD2mPALf_{Vx4?Xe24P3fE}7 zPTv9}z-;>l+$4}&`F0K7qw#(E&VzqY0p9&@@Q-qEegeKosxq&-2)E&r8S(GE7$4wM y!moxUAT4rSc literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/service/AnswerService.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/answer/service/AnswerService.class new file mode 100644 index 0000000000000000000000000000000000000000..4ca9a2017dbe660ded23d9a6097ec713df20ff42 GIT binary patch literal 3591 zcmc&$TW{P%6h5;gO?F9}G|;An0+Z0oZULK1fs(BvZ7E2R77E>TC4_jGwNIRBy&f|& zP6*)9F4d~+_}`F#HP^Or{e(1Z0kXfW7{t(%0> zjFfOKdXe;$Ym_HNXw#;;F?&=DA?9wjC)EybVo=wy4%7_%_Aod4!FIJr9el@y-26qfy~5`rK3FScaJiJl2YA8k=?AYHsZy_t6U} z-ae0o_fQ1L*J!sBV1%07^PNNScZ#gkt~y0l*0`_GaPonVh4ps0rEBoJn37SwCbmvN z0jHpVbC?3hyDaxYj$@@QKOv@mOcCy7N#G;o2T^P_?<8?3J&Fn+quo(H-}fCHN{ zyPFbpu`eV1JQ$rt@NP#_mWejNwU3vQ`tQY?eHYl(RCz41Q{# z^s=)4OWZqFbvx4l?#{zY3|60B&{WS1PJc^m%xjdaA>TjCi28*{mBx~sQVyP^3xiHI zg$0Glmn^M`m7#k#PW4NK6wl^B29dCdriA`@+;g|45|<5%%o+oHW@P@2(q+()K7}-i z{Udbxy)FvVaCmwQBfpLc5rgw}eszZP&|%>HgP0$^;aO70;7m%^UKr*gnzFZDWo2I) zON^9HEKZAV3?5BII=U4F&D)kKDG7BExjaz+kdR`MdLQNUfO-`w?H}rr|KFK8YjQI# z9F{9{dw*aPA0z7PuGLvs(4*1-rjljyGn-Vut$|9kho8#qgLLTY?4)|fpk3Rd8In&_ z=_lPSKIR0xUc1$5iZS(aq?pxSZmV1-A%#_-PL6tF(iND6Q#8)eEFI@*Y|_|-7CoP* z*$vvsXl(ro3tugt`5Bgeqgex9pl5m_MR}2)3;8ZM2j{c36}UjV4iFl&mLpuOL0GOq zxI`yr8N8f7Cj;4f0HE$;vT literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/Course.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/Course.class new file mode 100644 index 0000000000000000000000000000000000000000..6657f7f6eb8baa6837e9f74fb65d4542136d8bd8 GIT binary patch literal 3620 zcmcguTW=dh6h7lRH^(8(#YuCaEWOa&nk}@nP&ZA}CIw5Og(eN+0i?}(;!JDrTD#*^ zk$8aLz<=NwB%(?n!8<<+@tqlasgE*(9@n+o zsH%83^jt0dK>48?hraTvak!^nc#(9yAbugE*sUo)3S$`^C||~|A2y;Gi~juumoiTuO4ikAllIHX} zoB+7B$oTGN=zCS3-o^+By?0cg^j$$u=DK;EekKKbeg`f;2+J}@XJ{l(Lo_Pr%-sCW z00|n;QxElWZz4|vws$U1Ioq4g(;(#q4R5MIK5Eo{iC=;p0l4xy&sl8_aekjLw-Tqb8p-ZOVtzVeHkI76{#PYL>Zr(?+ySq#H|g5 zl#nr04iIZeVqkkR7{ZFpX`3iRSZp5;^ZF~fGt(lG5@7_0&%d&Nw#g}FJ5#fnDXmRG zKcslj?Q_b^$&too^usVJtH9g@w}CW&y!T}oJ$Iib-D11Kk9VI*UnA(bMf0`uuvaF&aw0IYAb2biSptl;ku^*{=vv`lR?oUIj zs=Fn1v5!z~hmSqlh@vp+r+b3Fe;PzG4yy;U?D%1=uBs+=oq80a*wt=3xq1fn= z$J%vjjaWOXR>v}IJ18NJ3Q$Ls28OpA>&Wx9gVKc}<|J>?aw^%m`CN`x=suU~%P4&Z zfZ01N1qWPh10DC%hM;f%H;$d=K@V4R3oo)SA1Zzf2CWFotfin?WI*>bxq)X@9~S9d zJT+(VA0bN_et9tJ!-(5{v>ASbOp6!Dw0M8ej$nO=-opAYM&FsUMAHi|>FmPd5sfV@ z9Z_N7>Jd#YOl6N~>SuFy2H$CeGYWfSz#a!i0lf*DqDh*?`VFw)U2oHQwA{0-xW`;F zW{%k~>uG)uf%OjhBUy8F0NosZgZME?r%7@JBR<^2B#-&z%;Xd>W{mv_yD72X1BK4x zC|l?e7%=Jux`;2k?xGb2^%olZ37rgZtbT+u>`~rsXd()_M3=FP58p-0lzNS3ot!Sh zol9+;dJ|5)O--7dSTI5FrE)5Ca=Mbr=}J1MLMNw1SYCP!Cyq{{6GuVNTq>u@PEKwr zr}=bFlbxKdgVT-IaN;~kIB`}8TA)RIJ7YK1$>~-qrzN_Y7Q43mw)s>9r#r9V#2K4# zvRS%}JhCaVjU|p}MsyNdHE{NNWRg^9U0P1O*7eBn-$iphGIWjJPrn}5j2Iu#^`_x_ zFw8fq+wi(E>@)#e_c8;`Kg1a}%tkWih79mDVS<&uNw=EdH!t>6j~56GO$wWjr==WfM<&j3)(7DqMN|S{238xP1!A zw*Hl5X9X3JH8wdfKyOwz_ZzKzHM;te*8adqv{jcu?0-O`sr%Q{_QUUoz{t=iNsa%M F=xvm7!7uMO+Ac4;0^Ga2tp&?y*)bqWjKU?hz`Z73$aawdjE zaqSExZ9*H9nAS$J$fU=6VcaGTs~2iQavb6|heHL!C9YA|=&hd3mt0XYBgd^oeJ(6pLG#HRxEn z4#O$&Td1BfN+lL<@k>E7^x$e)cJVjo)vkD7fr111T>vH6J?KLQx!f{1f`ObpDZVq| IWG|k618~@Ry8r+H literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseDto.class new file mode 100644 index 0000000000000000000000000000000000000000..024756c57cb1793bcb64323e5ba62ad4217e5321 GIT binary patch literal 3072 zcmb`I>r&fB6vzL|#ZHX4lmICWZJLB8<`%U;8(MBjOq0?$Kw_8PJ_)mRl(D6ZB*>(1 z(Kl&l>UO3d`T%{XPXA|R1Zd;|rukqU@9OM1zdbko^Y34O19*TR^XO7|{wlH$8g97f zJ9eaNo?G+n(66~pBlLfYUOR!d9WQ*XgV1iewZIQ`aOl=_XxIFMAmqz>JW#KXka%jfFtQ;zc$GGf7;jV?gSm9#}pNK*~72=+dY80IX3s$W2 zsfEvQj~N%8Q$>31h#;d(#m46r`Y|9N%NADT>H@MGSFb3nSs0A3ci&={KUBCx1yh3T zc3M9>0T(K3@ws7P2=B1KGb|bfv2`>?`nfpH9rGmXQ+b+bo5@Jik+pVGm;jz`C!lhk?z&L?jkgMC zZ%sdPWta}c;2gwvWsY(TZhD>$N)0Crb(qJu3R`ct2J-k`Vf}yFBzV%mCRn(^%IfB? zm;Kr$J^39WQAV;WjAZc}$%3RD;(5+iK1aA(pj4E-(|=<0H=}ly&jQVY z>OE9_m;bC-3FCN=D=~158e-rVO3_^v1Jg5qV4{VaA}3oYikxm?PNdz!yvW5CmPCHh z;u*^I7D~sCZ}T>w;U0|A=@|cA#RL&1>3WvV7wLG#?0|2L<2Do#@FgA*?lCR&DlUYS zvye|AwJGDP{*If!a-)m3ft=aAo_Wo$ut_aByhbVBa?>-s-!X3-s9Vg)q_Ge)szBT1qpmXEJSJ zJ1cifX{HjHHaaj#S0_yUhKV1&45rmI)8kC0o$MN4O*1_qQ@I0^Oqhge&@k~ko555{ uGi_%wJ;U>?IGvaiao<(QRPDed(=K5eB2yPC=2^uzl#a2yOVaR`aI8A^*ofGyY3I$SIu5|X);q!v6?o6WFHV((b9V?usP z-z)J-Ux-AcR=o0~5Z~->5=+oG=V5nd&di+eT)s2s^OukR0%)RHKu)0DvtGZW;uY>s2|95fn=p#Q}kdmWt$)es@Kh zWq+k31twcM@H=haD7VgpdHY($0xw$oV|_!Q6c|abQl|ty)?0hdJc_IgrD+Ts{ejhu zxnV196mUe~>5v08jtY(zFeUJO$eJ015~guXpb$#?#P5;=Q}wxl@Gn`TqOjpim@T2| zj*jzalkAK(yH0$fgp*Euh~hMZ%bPv&tb)@e)NqD8b2~1>N=FZvXMy887*=(qmyphF zEOSMuGJbFjJx6~BnUI*93}b#W6kt%fn`gqaF+P zv9z!C>NDRk?3OhzFf&*Oi$-@JzE!bh{008558H};V82Vxe=sJ5H$noXGL;kq;OhQQJ9@XJx;u*?F# z^CbN~^ysr-maHGFKlXdsJ3BF${e*e%_92GDcEpj{75gRjo3v>7LM*X81#(d(Jtd7{ z)~hbN|P~gnCfceafKY2MvS#|ys%73BcteM}!#rNCrUQS-w!nOanJCZndI02fa z`BfTlA_1RHZ|{OvaTC7~;Z|}>loRYxL8-(26~AS;w=>-0HYyIVvFC3ZzuJKM;;#B4 Y783Q&BWhjXki+fde+PFty36-~uaXa~h5!Hn literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseRepository.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/aggregate/CourseRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..8511d6b949b519efa75dcd1a337ca2dec9c9dff1 GIT binary patch literal 1841 zcmb_dTTdHD6h6bvu%v~Ml9b*uAr~N(cAoNr0m-FFMdg|nJ4ks#!|w4iusgGw8AJS% zr#$7)d3$#vTDze6*Jy#Jr5t)v4w3#Fp6X{Xx_6!G@8>hUn z-3m-GSUi>IQlwHRV*mIQBVU0T22YGlgv+hcN!!W{FN{4CvGh`$=2F;82=Q|+hky)b zx1v;O<#!lNu5TPNnA$ZlR$!jN!Z6TNYmJ?S<Rat}Qx~o@2}-ljSOvUK!1EYmQUQ zHhD1DIzb*|l+rzW*RUNA-oqlc-{NM(ToW6>JEQ%}@FxT>mTvqGpZ`s( z30R{04EX?Ew|bW1`|t%UlmG4B`VM?a&w1Kkq3bILU&FU#n5G_}APm0ynD5ak79UQDYBdh%d)3E$lBfP{(f)Y@9lf5JpK2NzX4zkwhNFW z&~T;Rv6ggu$2#@OYh&s~K zTZUuV%xUWDYPjZ)ZiZt8$P<`3G>#12HgL*=#vwH&feD*8n^X{(t#i@TJ^Nt!P_L`8 zDuJnhU^n0+uwNh1($MnI!VnjUW&)xe>T-`sE=~!2H!7FWYzo>mBn{cR;7B;J!W<@7 z3H-h|Do^sBMn&yo{n9>x{5rR20j3E|cSWrU!9@|Sko?p`M^KO3N7T|x-gcR7NX8w_ z6}(~7ws%)6m-bpzv#4ofcv+fjn2(Jn)fRAerAZws&~a9!C)_Gw5S|+iq^kr$xS&~_ zdYZ$f);6Ts(j>Cb6BmOm&f>~S81R)a;48fWKL}UlEZ`S{&9Ju1h4lB^-GOErj&DqZ zYG)29mxA-LE~vjpbxabbX_9M(CQn^jE~z=&g5#O<@PO4$OzJ9sUPO;C%ghrjc<5Ts z#jp%KfnoQkO`nIyGVl!P;tbkn)?~;9nKwQ7f-+o&xk=~FNR7Efid*2Yu?}vkV*WQ)Ef%P$LCRJORaZ3v@{ktdLO zN*#I{3P|AEV*O$as2yG3={S42Ms8V6c1xgrM>5;rtl1F) z?+92Q+b*7LU*?UD>h8OT1RmS~0%Q0EI|eVo zI80z`779>Q?-G>pJ%ckY!6f8h3VX{q)1P1d6D~iRop}PY|6nf%SMhfOX97Mi;_swt zm*E=BWA8dXr5pm|3m<}HFC)w=*!CB_itjV<9JT}S*Al>Q{tx�Syu)6h*V2IBh4&)sD+5@sCy+c!Be@M9q+0)B0x(Sj z{s=yftp8*H>px8(IqgpZa2F$|8;ad%NfPq=G_Yz0upiUFRxm1(EK3sUr!=t7GJyS@ z2DXZdCs~#xz%OZFpJxF3wLe%6zQBK-C0X`mLTLP!26it4*poD{S_ZJc`h$(ZhRR1@ UMG0>c`w#Fb;z9fxzJ;y-0O5z3@c;k- literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/CourseEventHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/CourseEventHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..bf26410e01453fd67b0c89bc7c3efe2ed97ef260 GIT binary patch literal 1523 zcmbVMZEw>s5I&ExuA#6O7~`F+5@`F9eBl$FCQZ{2lM0&Xn1qlfxz3S#Bz9!G9sDvR zK=7R(g*Yc2-Nv-hDN1xcU!J??@tuGD{`vy|HXy9S6oZ4p#e+=Rp60Pbp`_5U)k5;j z>JRRa8;rTKhiGh^OJTG{Gn4{tEcC!wjB$v{*|^VDl1Y_ThL7GX_qY7^OTUSx4mAer z7w5}uoamfORe{IHF&NBMiQizMt#sOaX7IjqG3dD@c$ykaxx-P_WAHYE#VTQ0#g5U$ z-4am-`x9gpTAM^F?xY^E8Y3N7YV4)N(`!;mx6a`E$^@y-Jf(98P5^xg7OQ&4FXhWr4m~$ukp`$GLsX#$+<1B@X zMgtO!1|&K;ApesqIvSA5paE%BtYltj<2>;(l~8cytLkBRq)ZS%6Xu)HfCcKvmCjhm zt~1n*Z7=j}M=E?h$a`pZIcdjWp`!)Q_PCLLJ#yAuUs?v)1Sfw&+e?f-b@SXSbbX!d zaKm$SBmmW$qT4tcz3qfN^DCLPAv$NGU>4D)H!eryIk@UfE7tK8SBX z0tD}T6k_a6326gr1w7b0o{Z1j&N$z`fBXUfkKkq#8Vt58pAAaox<+IkrB>2p&PXLn zXSV&Wu$T$$cG0@5ucS2&?T(V@GHC|ZVTL=Xy~}z+=cUp`Ed=W|i^606B>aH32~!N- z{5M}Qmh}+I&>w@D2TCjdkiqBG&R_Z!MPad^Y_hfJ=ml2BDR1n%_A#KPF*a9Pcx7~5 zw&&JRC0!_64IDEVY1_B97)-619GlQ$F#ksEh^!Q3zHY6tvv86%7GG(L&XhZtb7}gO zDx(8lS<@}C@9y)Ywb?_Sqb#YJIj@9#D+=T*WSthM(S|mqW6e#6EkB^GTb|Y}U*Wky z$F=diFTCt=PhN1ZLP>BVFO^S+^h$^HO2_GS6kq8+y=sBzwNhuL`jxRhBtA-oOQAzl zUF7?fsg3=+3i&uKd4J$MClo3bktgP$bPufxNrAiK^A7+GBzC zPHk~3Y1Qo<`ieFaqT=3g!9!F@t26}xOfX7X@%o4q>w6Nbz@Z7ozaS=cTtqxJa$}j?(yS(C?z%6vP$=3mqdxxh1R$=b>`S_mpGMI|kRs*mTUNqRrq; z&9u>+Q4Y20KrzU4BlhOGu!6co%wTXTvIU3LXRx^1Ioyv}fTb4$?NuMQlv9-NqrjUu zK7?|&V{DPRDpYGbfbSanmQtZs@A-OuvlvZBIUmNTJquSDyg$M`9**YEz5Ms=U@(p0 zwPcXIq$f?{V-?zeZhSh@z#0a(Csu-zwrB!->G7g5)CAXV!ZMNQaDEKtHdK7+HtB+c zWngfE{)GSEa2vMtcLU|Af9l=M`F0eP{aZ`^PT;@EC5_p~hgR@JT;cu46>vQEDYk;*3-x zcV@@$3yX=+ZXc~ndP-X3(C#UTE|I2h9VWPk+PkDHbeb!jRYI_Sf;oCjpM@XL)?tFd z+yCWjt&C+ogfj5QVCtdL%0FW8d8PfAenpm9%!J3Ja3qdw}n;VJWx*fu5t{zr{MYsn@;&uv>41+ zOdHM_ z%fR3a{R;uqNScHx`qt@mpR_?AeS+yX3(*Id`C8h{(LP0%Kx>}%nNp76JS@=Z60JrJ zF3{&9Ee4likzlTn94;-<_Yk~Bas_^M4EXYIz$Y18r;BIc2JJPt`4jpUotv~K$PNDi DkpW|a literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/UpdatedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/handling/handlers/UpdatedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..96bda08c34ef481b211be9207c2b3b87057c7dd3 GIT binary patch literal 1882 zcmcIlUr!V<5TBmFaVv;|iukwS3mCH-6Q4X|LI_5a!w^6cAA8+lue#T6+8*Es@-vvI z(RV+Tak_hW9GnKCKD0ZXoBrnCZ+hQZ6gwoQY&c^XQUFj zGkg9(SWJX=2WVZ=Rni)Vc3(+!i8MXyFu{G)-X$HO(_HDS5`y*XA{8Ff7vTrAbr@st z_J8?WD`Qy?q4fPRn0TtR^3NE2S#JHMUy)@NGs-5Z9BpA?obtxLZ=M5M8e>zXg;z#b zW!todD(OPmj%6^=Hdl5TjIEgz>(F2@^H%JOBo}19ZmqGCaFNyKUu%obgMfr-M92nNu@UUI_V4WXPAuI?hm|4Q+~tnj809enMNdJg!>4#8ZQg zYvXxWc-i5eyx?JhlHdkj3Lp3B75C{C57X-`zTzW#RRYm#sme-q3uApqe2@y4LWii@ z$d4*h8T6}kRlfmVR zX@fbV94gg`Vvy-Z*_%ya1$Bv-!C<;%3-+teV1BuEx*uf$7PflYt1j*;rzqdmz?*V> z2<1`B*er2HsMcNpKQQ(kr9!Pf^ws<(Wi&12d>EtVB-~=~;SBS5I+{QC^53(A!8nH3 zl0mdhZ>PlPDztxYcsk<18U_zWR)UhYXaalb@uD%*1lO#?B9Z8Hehj8IRQb~F&;oVr+=MhKluIa(K;xs(0!>;ZD^1l@PK%l}65m*#H}8xeA4Pld5zA|s=%Fefq+HjMPZSP_a7p_~oVK?flHa63EQHiA3R z-SKx-nq+iKn_g&BBUSIn@I;(y^AZTki8B|r(lnM4cT%h3iwdLpV}8Ozsl$C?;Eeo< zcVfY44)!4)!XK!=*nP-|Jz+Gnt(A3LxkDZg#3jV%e~eKz^KZAysL{?4W3GDPzVn`J zF{)+P2j?lEYfdHtAW6z-DOdIMmy3; zc*Ly}<}8WBV;;Sl};7 zZH=5{*dua5*V~_z@V0))XmVS3MU_5eH2W$SJH}`;MJtT)7s?2$;}g*hKw~Q7%=aKQ z8ayXRyZ;<)5^*4)jikgg8{<*eLT8yTLP(|~MlE>)!5WA$fr|2t2y?-^4RquaJBBLwt ztTj3q8bjZ*=~j$}Dzu(v)4_Ap8V-mV2>F!G!<_ZA{P;aiOW literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseDeletedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseDeletedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..fda2f13209163b410ff2decdc266b27e49d3c869 GIT binary patch literal 2430 zcmcIlZEqYk5FUs0-g;@mH6cw3trOl7Xm%;IK$8~fl%#5^OF>O~Qoq%=>)birwO6(; zFZ?kikl;H%3NiNXsh4(vo+?iAddHr9X6Bif`}40~e+PgzJgCAHfftz#hlwyf#X`#? zDIygb6$zFYb!1PO<{^{jlxq{FBGSrmeIg=m!blCZ;UPca(wZ!!EQF5o0ZdC|6LA=(MxLhx8hKvwkeZR^-Zkewfy#X$h5dnmT^mFCv3JZP zyES&=vB%_suD3ra;cdN3V0v4{yb3o7%)ZLSuGUJ|V1*F&Olodaa>8SZG-e|4zSB%A zm$P^tt33!;FIqbs7}a``T&FQsC2XR%xubp_n3y zX^95Zq7{CYAuZ!pG8+_~5)_>hT z$uRA4eVF%L%XdJRVdJ5t;&9py1Quwo^MB6|?2?MXsS>&F-{*Fq;-8qCA=o$QKm$Yc zQ^PgL{M!1PMOuS^z{CndbROoclP`>knpJr{AiD>i`G>;I-o-bEd*J#Q zR)D~JxGq5*mZ1tYe6QpDoq`6mpb0bh-hwN|J_~br|302w#=V8>s)2cYS%43`;$oqA zt=N}}eHlLlgv4CHhY0==N_Wt3)t&wZH=eiXFR=OtN>lJDJ}an+e**9sKAZl%0iVMf zN?+hws=zvK3%C|>U2{jcmT?jI5^mvhWWd4X1~y6z+%7RdAe;ciCc;Q literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseUpdatedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/publish/CourseUpdatedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..25f6779f391538d316f044b4b834918a2f7f8ca1 GIT binary patch literal 2430 zcmcIlZEqYk5FUs0-g;@mH6cw3trOl7Xm%;IK$8~fl%{H`OF>O?Qoq%=>)birwO6(; zFZ?kikl;H%3NiNXsh4(vo+?iAddHr9X6Bif`}40~e+PgzJgCAHfftz#hlwyf#X`#? zDIygb6$zFYb!1PO<{^{jlxq{FBGSrmeIg=m!blCZ;UPca(wZV{dOGj_Kpan`ydf5brYU z_ciaMaTmheSO^{E1DKY`CgLzmjXX~YH1fRUAvGh-y=%^W0+sth3i|^AyEcaOWAB(p zc5Cd!V~@!NU2lIea>8SZG-e|4zSB%A zm$P^tt5PRb6nQPJ^O)UpviN3Viy_DvcF46jMYo zEzy8lw8GCaq-ESnW`m+rf}&G`i=FZ|wt}KlR`Xaynkj8vxUT}BkuMk8<8;JFuB^Eo zYDq1YD;0(c=~zOJkR5s|?0~-BNr-7()%vPlTW>Vr27KIv>+lJIh4C6FGVGEx+YypK z9i~055AvRC`3~qZY&^7798TMTzyj@c{_pvLT~aYPRU+5@d)y9G{1bCC1p5XZXkdtb zYPbfOUt52(NNW%fSo!zb!qOtp96=S$Gfp0X>m%%v;;z9r1p5DJ!dLG*Cy~Gv^sGHT z7+NFW{B$cuLlw22`sv^}YL5oQ3<~*_&cmE_@`W)`vnsC#WOv^)|46vmyZGjC4_qI^ z3J`b?*CnXKGE||4?{%ENQ_z4GG+_qcTX3b=XJHQS-^a7dxVLa!H877a3-EzgTr3o? z75h@LFXM-RkeCbj5Wzn}=?)sMy3^m_#`6~a1y=t+X$n5YX9YF!PXIo{XVbqo;B#0* z=?h#-6>84r7<2%8i5@T?SJ3u|Z! G?*9$b+grK- literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/CreatedSubscription.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/events/subscribe/CreatedSubscription.class new file mode 100644 index 0000000000000000000000000000000000000000..df722d9db7bd3e0b3feb27063dce3cf3cc06769f GIT binary patch literal 2930 zcmds3TW=gS6h7W0dt=jtK(E})mP=Y-rbRp;f+{VmM5?r*QL>HHC*<**-E})N9@*Yp zehd%%3M7!=ogalbb4fzFL%UHMRpMdfv3>lV+joxr*WbVW0RTPtx&bu?GIjnmQZ~@S zcPJAjwQsdlBC>kq4uwHqB=!)E^7Mcv}wW$TlnR1V$ka707EWyoT^A< z@#3k`L4>jWj<;LG3GxtSM41SAD&#XUM&2c9Z;T0=EY{w+%y?zKo?9*A>J|Cw@=&AY ziFP~|PENQZEqIhtTK=sibzaF0ujGbzo*VP%@=m$YExb{2s*TGWK68Ofk!02c$WJ0t z2&dB^Qj)vl6nO=;cs#ZFsSrmhA=_2Lm9tzQCVZqKY_|$#Rp4`{xyXS}gtmlq+8kHN zwp#^#N((kK(pQMhLcR{6|DEc~ap+P|+t7ixJJ5nP23OYyXGOf{j7r8ES&^>{ha7!zgb|h` zW^jFdFdMYorJK9c#Hkqfl~v?HFRRVGH4)1Ffi`2`rkVev4E#`=XLKDT4v$=MANYBJ z43u?Qiy~vH&saIZkQxkK33>JB8vLF;PUJ=tK4kFo9Q|?DgV#}W^K=acOL;CB+$aLu z8&RD5I*ye)jqwka(Q<~GG<6$l_GQF|XDcFlK732*L|Ee0w-*Q7$xc!RI=!vo=I&4Z zN88&EhYYUJ6+^eUE$wW==M0YCRJ5sJQf=MNIg@Dd99=0RN-}8g(LFA4i+VeQYvn5= zz!OKjtU?*wtqc^=(V)(*5_%2LC6rJX-)MkGs%BZ#fP3^vD0o2O+g8>RQHONc!pFd1 zfgVvcxCKj4r@u95K$FHg?X;jxJ8#kK8Iph`ceTQ+|Y>7 zqtr^8&>5*j?99j?2#cZ6?f|U|6D6&2X!n&w7fLg=4ny2W?Oo`mLno~oV)#AA=rP(Y z7E-T_ZooW)<)>m_gt5@$@X_!oO3$DkoADSegJ!><|E)1tEO2)VH4OIpuV`RtaJZh^ zTjG+U`nO6e|A4`h)yq+FXOF@Bx{0s>iwu@d-QTd**d{Eoxt||vi_XOR7;$NmRK+5T z7f-Dj#+bNoc)K;2AdgVSl!=I^LOv5?igj=%GeMcJ=T?ij21UNQJTmCG zHl8QK%L(_S1rJk7%RjZGJ}9{nl-vl;a$^=JSFhb{%R4IOy111(r#aCNnRTEx5FDm`AyihQY0%==-Q z4ABk*)h&aSzL6r{6INyOa&g{Ilw*Le+HGd(xYXa4x6n^av@NVis3BWYy*Lp%im@ay zgX^pP^Fhm9y16seUM0AvoFWf;S#9R6iBRtMjU9(B&HNu_;0MM&qw7F>JoLqV5atEa zSI%cGij1i~R2z%~G%e5xF&)}yS`r~;IUPsN%(lrNuvBojK8almbcWTsoPMqFC#WMT@mr~;af;2!V#~&zBt%+c9JsC>1_@+c79xc zw6*ncz~Bm9F?5T&(#|G)%HZ(7iZ&HYYMkFbV-hW%p(|xXNe1m*y2mAMQg3H)t$bw+ z@z{|dt561aD+5Jzv}m%cgkA%532Ex$YYhlU)p-^*;2u2^3La4Swv=;3)BzoKa2puR z(Ict`w_pM4^tS>HXwq1xoffod=M9>DMN%~y+rL5Q$;y>qVEGT4)!?mC5=i=O`tQ(4 zQg9Wn(RiJ{1xA3`_6@j6Ah+`E8oWc}yY!t0f3E_(`|sc%i+WmfIph`=WNR>7;O16>uRCzsTciqm|BiozH zkKut|fdmq~^P>=FE=fptXg6vjBpyZ{+sEIzedpMJ{`Kqc0MLc68&G2)Qy)%Z;=Bs@lY z#X{23|Wzg*P^1pQkO9k#;p@zYJ?==l94GuSQ zdrMqWRR3OS@SP;w(U&y87h1*hET6yB&LHP&YipSi%L(3v#@ zcB`OIX~AYj`UE`Z4dzIk6a*90YX0@5OCPKO2Gj?CDivAfmZ+Tb*)HuI$&LmnqM_0;-k__5=bdO8iqTbHnTKUQt z;E5waR-p{;RtAdbXwhU>3B3mB64KPg*BcO!s%aKA;2u2^3La4Swv}^4)IJ?{@G&r$ zqeoN?ZovZ7>2DPp(4?_WJ1uC_&RaD5hNNmVwts`gC#zR}ft5dKR)cp+Ng(NW>3@+% zl7g#njmGQrEieMiwr{{q0=boM*Wf)G-=}XH{DTVc&cB0yl!NnA@Fh}}dDTJq1a8lW zfA8h^0G|?mRU|&EF!sT#z}7AR_V86;cP;?7`*N^3xJ$vT!54X!G6H-_^ZWGGY4s}z F{|3;CW_JJp literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/service/CourseService.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/course/service/CourseService.class new file mode 100644 index 0000000000000000000000000000000000000000..c2cd6cbb9ca3da6350987b3329445219ac5155d2 GIT binary patch literal 7976 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 WKB|5nD{4O~t;5VC_^(1#VcrwcEW}m- literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecution.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecution.class new file mode 100644 index 0000000000000000000000000000000000000000..3efe0a35d88d8c3fde174a9095752f7427b66014 GIT binary patch literal 5275 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 T)*5vgJdXcQuueTM{tEaX8{Jz* literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionRepository.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..0e611b51470dd9de8ad32b40e044d9e041bbed8b GIT binary patch literal 2101 zcmcIlZEq7f5FVGdDWON9yc}?MoGC9YAgw>~g%qUFE2o}93M5qG6LP)Nt=nCDWjihT zUq~l`1VZ8y{|Urwnsn1#LFJ@I^6q;4WM-Zj&&=nqZ{Gtz9d1`(jKOvuL?_jL-AWNq z8Lh1FR%w}fyAgJ!Lm`ddMdwAPm9rk*j#lV}vK{9UU!&>-ZH5%KiSW78CfRT@!(HpPMJ$69Te%c&UxoN5 zm!(1mQ;RCqMu&$CPAuGBV=%sCW30d_1~a9hW#_D$f?pVXd0`xSo9}2cc+LD69V76hEOxL8-#lA0`+t4N+N%>0rsN1A0KDMqU6UkuVFdKVj>Gq^z zH7~Lt(;9=5Eu9z{I*trJIO5+HhZwAwfC;+VL(+BmdB+5u;hOfEJgysxc`x6dfoL4{ z%026#>yoyK9hr8>t2Z7~$`#$80~HYIm~S|n@sVJR;`r_+IvkZohT_E^@cX@Yz4_OF z0G?bg5cBn=_R8AwJUhB4LO&dRzMVSSvYX&#J zlfpXHyXj{8S$ume#O0oqg8Sg}P-X*G|C4^Dubjc1@6weqQf0T%m7G5v6X(*@9`T|5 zaj>73sIw@Ubq+`s&DqE&FR!GzJDAhstZOXV;EgJO}#qV&mASO>M~6;Z|_> zP>($o0NT*aQIxEy)UT74ZRu$I1V`Ons@{lem*3Q?Xll{Wp*2C{bR4FjLW}wr?e37g z_*LIQ_2umJzi|2k?Z#l1o|A+DTIYI_Vj`Z03xvPe+y4re=zWULFVni>;Wzk$1QQH? sCnJ=rA7TF-V0jOF?MK+T0ronnKLI!BIR-cTUfk+=Q6ehhkI6wgBfH?I%y<%iK(r&Oz zC4G=SNDEX5(2G1k9;)aJccoPqG|;O+5IH1=Gat!+_TxW)e*mzLA7)VzID2D)VPB`G zu?&m~BOS&;8i!i;)A*HnCleLOD1E1rG#KbGiBpxl)uBp*FdinUQopHiXmlI}ve!#g zPZ|{**piT$#|)NzTtY)&roDAC2Z2=| z9%^i@`IyCLY+d#-mswYQ)HCa) z^P^9t0=m?tC(4|~-RCl)_c0_afkFGz0q`|@u{l$TK(nXJu|s~%_SV!>j^CU7Q@8(` zOCFqPp-7O4cJ)7zC$e2A0%v~B>4>=-2W%7p3yC+1-Noc$;h9T+v2e?0mdWR^R1X_t zLWe{JmMKPg$JLzGv4N56$Mii2)g#R++;D#!cDUTB7GDe_j&hRuseqg)8M zr$58r$l-sc5r^^^z96jI$;%Y+iltf0oF+6LZc-*;vv`6A9Q78yq?WHFpkCOhe_`bh zGAiJ6*X-mq8sx7z-@d*Uc7madPb_*yDT%^pglLIm}(QI8m({!J~=V*$pzZj zqPEP`EHmA9GIg-+VrrI|I+Set0w#W|2@^lNz#S*kdYNg*$#mDvv|eVqM@-*dz{IMV zFtIWOcAQMDGSdSmQ{ZN5m6?7drriseSich{R;s|)_=ei@+ijGY_MA-j==2UfO;us0 d@n4AP-~uMDqzMyOP6hXE`yC!o&tm%?_!nFD&uahx literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionStudentDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/aggregate/CourseExecutionStudentDto.class new file mode 100644 index 0000000000000000000000000000000000000000..26e4165a2ec7375251aa51635294d65556d4fbd3 GIT binary patch literal 2410 zcmchZYfl?T6o%h1U>CE2U@&b8fusqIF-ax~O>-e77eb{-G_B;6@5?eZOYPlicbv5S zRjpLFQa%Y=ewOa{K>v^Bct6QerKXscYQAkV-vmgJrip$>_xFLzZ$P+{V>pOyB(Rf zYmMGd-W&|xv~2Ih1oov3E0|Wec;>!!b;k|b`upQEnw|rkF>g$|F<}+FIoGggIQE%ZNtHq ztgVgK4$4l1+YaVZ;VlQZMYu2$CeLzEW7E>L6F7s{@*XG8HdhJGg*( zg_Ycu&1zra?EFfB_y0R%Ps5WR+=w^Ovaj%r;^H$}X%rlh+_m}9g7iS@GjD_lum#@9J9SvS3ribG5}T=b3MPX;bSHTi$JHRk+GinBsSapORD;8*oV~ zC9y1o#PX;U%fq5|sAnJHxX7m+T17k4{0Fr^6WKDyD&2sxX$tH7C5tG+3O=P32cJ

vPw-6<(`KIO2ALWYn4}Vh zOtT3Sn@0gtGtabL$h23?)XXz!GToWLBvm1i>O&NDqA)58f& xazlnpPQt{lSip2Y&$L^}^o)Ol3hHUB?$dgDN~UKMnB=w&nJy$uFF2O4{}I2G0*L?s literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/CourseExecutionEventHandling.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/CourseExecutionEventHandling.class new file mode 100644 index 0000000000000000000000000000000000000000..ac032bc93267a27b85d4bce37c6f05adafd75947 GIT binary patch literal 5615 zcmcgwOLyBu6u#p;{Ak*w2`vrfp#%y^0gFO-v<`ugIt}2oIZc{#a#$(y#GX{v=#0jB z`~fyBSiqJITXt-E4sc-KU%;>7a5a)`Sr(Qp+%9CzNHgDe=iYDb9r@`$zx@dS>#&oB z3<2p#y=gJ`kQ=(Brp-*QyWC`kX4mu$^}gJCU-n*a1uwHOPGIKPI5Bj~z_AYwkEtmMOj^8Fqk_O} zg^QZ*T1P9#dPO}e5x5$m-1SW%P_K+?5y9%B#s^Gj!+gYsht%ONlU$q=XpGDZqgE5N zW=I+}q(g_cOs+DU$u$ChFO7^j!2u)V8Dxda`vk@}c$H>hn!t2NE@dIO$iZbY{@o*6 zP?uXLw5plB;V{ea#HcxfA6m5G-q8yAy*kyZ)U;5_Rn0NX$3~56i#WPiqc#=j3N{DCe1W#&#FVJwOmxlM>ge#px*M;-i#TqNv;{1Ja=dzug2^Mj$7Zw?Oi@I zsiR!oOB0@!o-i`=kyCxXrVc?EG4(FB=yUbz5QGs^hY-NXW~O&+`kCoHH2BN{T!OhN zxBxE@xUy8~q^n&iaL1IqbT(aKHof0$98$4oV1`na&NQrjLolx&JRFyG<`Os=S-SOe z)nLULE6dC_m33a-F@(`TDGRjaqDnb55DWorsnSOgYO(VVo3>;Py3bsOYg_lykxI`9 zcgrvEj^~*pkL8q$$9Sf&B|Ve=SwdGSr@~z6rRh=*UL)|s0QtU;S7@M~UeA7QRUMWr;nzsIe6cDGsaCSib<>2j9 zdI`fkBfW%m(q2%nPQsRymIv2KuQpO4#b_f3@1==zn7+SD+i?!kZd{zh8Ck`7B~_fm zF#U~6dG_hf2kiy*#yM=s|BG`fq}Vv$P7~)aeSen{n8PEwca{vFKnPq5o`zGXDg&Jf zVKGG}biZmbgsqB9=zcX9DslJ`5%6}Ea{ohBscwV7LSn`vaM=g-O=~U1f2PZUvvuMel4u;AU(M#d0Yq_YUon8emBWFe>Sc_`q021i_gDagP@?5*HP zZ+zi*xO6Z(^8{x9!d?ch;O``k1iY@|@04m6;6=EGy?MOy83e`~UIfXmBh1U#_9nf8 z_ZD~t+dlZKG2l1;1AGEg5KhF1z#@#p>)6h~8;YQStsB^nCwVi5&{*xGz^Zq0NcThPUDR$Hn z7szjuz)C}aeV+ujimHgSEG|etCV_oA1lUhWU~3rhILqP!@N*K_XG4Jf(jP1XpW{EA vah81%BaL5^!0rwK_9O|cJOtPu{lUgyQ{|(3?S!|5{RepEa3g*RU&Hplr3gy% literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/CourseExecutionEventHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/CourseExecutionEventHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..8c2db55348cfc4a6b60a8dec1597608f3296bc8e GIT binary patch literal 1658 zcmb_cUuzRV5TC8FX=1d0v}*m=Z62h3xqA@=QwpV2EJP`$7JS_7jhEHC-D7u?RuI3I zprG%50l$qncS%fpl(s=RcA1@<{msm8XXfYcuipV+8+a9F7<|fn*iV$}8WDPwT1gW+ zBb7*;dFPLW#ZYK>gw}Jc zp^3W|gGZ%Uib(gZA;uiB!XTa^AZd(^lok_yCrITeN{%R$*-fRD-(v7gboS`J#iMz}PqaF-KJs6zs!Ks`E;~pFe)Pv1oo+{0Z^*I%j7q9;lygBbnxxT*Id(k!lT43njamV_Pjbg|Y^F?C?yQihMq z|2I!?v!^}AXj@yYIe5rm?flxK(qmBX&}fwST;=rfbLM1_KS5;;7+j&zJwxABm?fzK zRhTO@6e!e5e~nI0$SSw1e}l%`r5j&h;YVR}llED{09vz=1#VwZ>pqyw8o*`R}x(!O~*RKLzEq_j81SLwRcHN=rmV4>rTS44U58K z`ZRn%TZM53@BhEi(!0%L?>2*TJr5X6K2low#|*x&HvW|-BFijh6lk(BX!;cv#wl;? zQT+_Yq%k&CTJ%%<(i%#;3#lElPzKlkTwmK^Fuq|@tU`^!%v-T9l3dWaO>2#vf^)2L z|FyR0OummPm!@5)ytKp%YnnN>-9sMLwp++klsO@g@6S}1u$S{aPeEwm~`Bbv@zAt_SyEtB$- zH1<3xw^pBNj~Uv#U5T}*HdN`J(NcJXd)yl?cyKSOI@c#XzX_iH)Ow!`3 zj`pgJJIX1_$$EgS>^@R=uVHMKxFTe9H&{6^c8|g(E)IS7icLzl8p`?5h4m@8&fvoz zwDT!$onH8Vj}n6k3>PwkXp1gOiBD8uWqz=y;(!=~J0nv<(OWbDb-E3yc{Qo4S7DjK z^-}_6F#TMWcjz{qbg&Ez#^`4VP@&lbOwzkbs|U0j_M^`*^=2;m2-9CnnHhRck|fZW zrRQ`xkKiKA(Q1K4tpfA(xTN*4F3s06`Y>{ literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/DeletedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/DeletedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..d199975fd8dcce6e0722dae7f4b5676e44080f94 GIT binary patch literal 2017 zcmcJQTTc`*6o5~c%d#T~f;YrV!51)QCK{hyV`3De$zlv3iI3gRVYV{cj%^n(@i+Nu zqKUry2mCk2)7iykH!BgN5AF1FdT#wr=i84@UjX0{+^j-{fhl~_$(3swk$99^Ns~Au zmB^jh^#{UYBD6a|>yoyT);P5LN}@}o=~#z&h_d6A(FyLO_AY4&o#sks-9b3^1atJ5 zJ_{RYt1!;s{eL(5xb$xQ$h-C6T+agrlMj?u{vm_!tBrruWm<#vhv$t59Pw`$p`GBo}mU-CAR(;0&wW zeWfirlka27rD+!`FD>!Hnr4n|cb`YK?H2MBWljjByby9vWXM-YI?hm|rBKs&0ExH< z%FyZ%C~+5*6`mS&TpQ2Z!pj!-WDXAtl%%(BuJCb>&bUWsJV@uMjK)WFb_Yb~N;gi` zE{ydd75(726gs%qM1GX3uCRYsARnZfw>!>r(n852(#l|zZlP5n8qsv#3Q3WoZ<&-I zr?KZrxwQIRd(6<@?nORbr_Nw;h{k?(GPpjpHt&=bQ-=oA}0>gvMAljmLTH;d`SXmgXsW>3U;MT~LQ1li}K%HJgDqcQz`~ zuyjJ845l|!`G#)ONe9coV2pl-02LZdz$AUEG`mNuVLkc`Q?KWvk1+kUl$oV|k|crJ z9QD)XID&I9PqPcuY86ML;lm+BidU!gWmX82DdZ=99@ literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/UpdatedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/handling/handlers/UpdatedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..ad1795d0298188794ce09a85f148acc06ae7028e GIT binary patch literal 2017 zcmcJQOK%e~5P-+&Bh6B1X(>?3!!4Hqyw8o)5P!e4tO~*RKBa|Jlj85rTS4S4Ap3 zrq99$v{e{q@b14GeNuY2apK)ZaIWV8gULrqEB~0mx7EhK@i`VVv^D z9@o!cOd4ZTrA0rrTh>t8T}bVag)+GQ=la?%gYhRO#VXVo%)Ai?BFP1v+pyNyDLBU} z_g`s?&g2J}a%tLy%1cYUu%?+~+dbq_ZKs7iMVS) z_6+?n0NUT)J#_|)Lp1iglfliQZ$o)vFuPgSZ%bG~l_Yu?oG*#OUSSyU)y63`E0eUi z-O*mPaaTD-Iav>omEA|`?lp|f5?6$b?gcA{#_m(N#Kn>CUa?8(Rzo=-y0AV4OAOxs zK|7z~*6D@+_b4%#z;Gcmh+fh?E%B)etjrJgR2&dvaA#ynD0+(~piZ|THLoUh^(rhg zSUM$82Gg6WyhC^Bq=RK(Fh)N^fC|kfV3OWdT0NlMupfPbsn>JS2blg+%FNJnk|cq~ zEIp^oc?1_>j#djaY89BL*CiSZF2fa4bCu@d&?3E0lvilpReo)R^5x$spI~sE4vxVM SdRE}(Pt~_*y+UK0%iG6h4iFy#N6>U;@OXL8G8y$4fK{F>zgpNn8?32$to8)jQMdHks+3>h9&@ zzw*f{tTFMUpZU)9JM-7yzx@FK4cIKf2mzhipcf0%RxGeQ zlp<7tQK4Y5QG51~X&x|X4!Jf#B0{YU*9RixCJ0qe8_th-*t0^(fFE#aO_285h}I37 z{(7^2-PD{}9&I|M5{weKsdOhWsTQ)cr&+=em41ep(()r4G}1H{A#=F-8)F1!_Spdo zgbKE~M)$8Bl6UlBM4;C4 zh%u9$VB0!R)(KSn_0DmM&=o5Zjvz@&U@lX3CqYYvEZ)F)c5sL9%zf9B?qXLMfj%{) zRMtCh2&}e3l>~dtSgwzfIM`?5GlS`3CrZI1+}~F^VHUAUDYK|530ykH-|E)ZY8252 z57v@o%^==5>vS~lpm7(%^gswL7;UPEm*6b|Q>Ufh)LQ8>+#tj}m6{tBAMl7`)lw1rD$-OdENf!cXtlD_ z?%+qM`3s+UB&Mp-fqG+tZR-ys z3hDBOS;KtMLt(r0Je3jCI@)+zEw5CoumtZ;z;#$AFgw^%dBeD}O|^vNk9$d*>z$1B zHBZ4^hRjDx`QfPT3hYqZ`u|57a%9Zl#0%X(+TymWqHmcSYe;2usft1SLFPC+v%K;= z3(D{jfyI~iAGRN|xet|%D7QUHCxL}Nb^%GsaGOBqUrqQ9|IcM5a1lLg49==r-#5?G zd|H)I>#-kLE=P_2%$o9>!Lj86-9DXMKBN8NRl&RLdviX!ClhSkzuq5WWvjCG!qRN} zvfUT%TS8y*)vCLR0gFK3HIxf50rOCTGCo)E!!iO@sNvZpKG)!4zD~h3-oK7#TD_+hOujK1ozRu%|4rn1?ufm&tf6cFx2y`7N8ePQGTj)>~*Xr-E^h53K zU*VlUaW?`h_&;Yr$U6UhR9@8h|okP HjKIb}ep$e! literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionDeletedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/publish/CourseExecutionDeletedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..46378286c95f852814e1d25aea1ba53e7fd5418c GIT binary patch literal 2899 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 HhG6|4x<H@dt^Jk zJo0Dw3rHZrJHLWof;cmowlq6rTd4~VJd8ZHkH2&Ke2#zr^XFdypaY-Ppu!+deK?7g z8yFFKlv+s>IwO^cof-NgVKEfi9ieq$qNFtr?V*zBLTM(}As(Zgcx7~mhp4>^-DKdT zRRavW7KO)XZ@Q3XLN!=qu=-3KiZB*>6h0k1L+Ke*V>23|Wl-<+^1lrRw`LUVO%*d3 z_pZz2!k4g{yRv!eN}-8tsD7oi@{buDtp8g|@9#5M?3xH`u*_iPJXV|58e4}Iw(!Td z+M+Y@Ax2!9BvrAvkouDs*sNb{kJvDugsvjm@e z!$l5!Dzqh}H}<4Nw%M5BT4Bm&c6mzVpU1uqq5pvmFWJSH3*UqmyxM{WtTDK`-aD@W zd)_KN+Q@3eN{?vz%_JG1?F+j04Ay!^ig;gGmCcLAML$-Kfxfo#b(u0xm2-6){n$i1 z!ioevbq@y1W1*uM3&u0x>%BQ)%$>ZmJJDVxxUZZdpE}vq&TAdPJ?a@d3SF8-WSD6= zGWLMFlJhLy$A1_gG=eTkm zSN1a1hrv>w9R|0jq3#SR@?Dc8%AZC3>(a1!OXZ)c)>$JD`{yeno{i&DIu?#7_r=x0 zcCwR{idJW4`FB z1_g9mIY%7s6N-a(fx!Yj^D1x`mY_<1YfytajaAxdK$CV}q1opoRiUx@3$(slyYUmO z{zkJ3yjDm8Nxx42EgDG*Zo(}Z-=J@a5n#4`8}1Ou-F&+O_h@{RzURT;Dgkf*Gx$3> zI6nhlCRLeNZG`vW{Y&CMoR1Ii0pXWL;=>YSAHN7}{R&`DUIcdk3ShhQ!4}{F1+xMl T=UK`K@CnTy(O0F_Pa*sZm$#{t literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/DeletedSubscription.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/courseexecution/events/subscribe/DeletedSubscription.class new file mode 100644 index 0000000000000000000000000000000000000000..8ff41830190512b99c5bd9a0ec7a72007b9ed06d GIT binary patch literal 3092 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 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/OptionDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/OptionDto.class new file mode 100644 index 0000000000000000000000000000000000000000..4560a485d891ee9ce3c73145fee613135dabe503 GIT binary patch literal 1776 zcmcIkTTc^F5dKanU0PNy1*(Evqzb(#tKz*#f*2E$Dw>i$@OHW!%OPbC?rtIRH~FB6 zVB&*6z#nCtv)jwIsc-PmGrMzU=KK21%>McN>o4#VudhjV}r#|^-+!Ypgyffi?*0Ho*_@XY{5`$qY zdUkwQ$>k%RX?HFOh6NJ_Ml`7<6SuLDuD0)Z01M+3YnfC-sj6qu(%{G2K`Cpu}{a0!#Xl9ie+#GdjoVZJjJ5J6!mQ ziSI#Jn$8rZfx_byIu$$xL72u2z3BoOiqHiP$m-Z7U7%3B!1Z&?YrA-kthRSAkUxjj zt(_rypxihn2w{?Hrm;vU8b?>9S}h#?^bn5-Z=Jluj9xT2%{ZMJ+#(yY|AF~$9db*$P>FM6{kAHso4FKlA%tDI6 zT21Knish9YLl@k%Ez{9G$Fz)!=WL1>hRb!s_Fiz;)2o*0Iv#g-ER%csS9R_Q%dvH1 zYs=+ZhT!_0U?~e-3@*Q3an;kyPSvpDI_5%c4AR0970zH_`I)g}=oQ1>($|D**;}&= zMpo;#u&R8+@~l#Y&)K#k4C3(^4C999hARxchTN7XxNUNMsf^pQreouyV6fk=S4-So z*<7gw+YZF+ETPmEcX3CL>9{U8g)fFwj$7s~gN*6aT`z&RQhJ7>xGN)^nq?wO)(O>T zE?BlDE;0CFB4#LX=#?uWOiZY=r2r?kukFg@27|5zr_A%P4~7ab0EZato0#0_1`Kuq z(vYFq;R1B~vm*t_!#lKgtN;ancDw*R{_NcX^um7n_GAJ2{Mma2=!g9n`DM%Iw`BC! z4LmzKVaGHo8-{DqbFkPYwk-_c-R0LO(tplSJfXguTx>h#+lFgYxxhSOaDU>B*rL#; z9kESWliT8A-B0U-6O*xni4r^KMG}LGL?LB)RI(7}aUSKPhXMS?k8CWj2b&CqnS)Ws zoh`ycahstm8SRrso328iG~KjB{;l{Igd7Q`-EyygU7FpFE=;%7<%=}=9Y_!YsaX&1 z70HKlibr&dDIU>7ujz8EOkrVS@Mqk;w{rJhpS@THO$V!rWy=Dl*Rf2jY~FL+=lb1f zIvY=xC~EmVSq4iTWN&A-@Qz|g!>G;gE|nS7;*kpzRvlzx9HgV@sETI)h=C5rvt zwpT5^(fsQz^9mNv@K6{GVnh~qu)(g?OP=XkVe{(`YfxQS6@871*(}`0L*4UX(RCd+ z2M-y1ch`2g=TvrhSu>q#&8oN7mhWEQ=9<{8aV@m0RqLM6tZEGf(7J}z zRH}ZalURz{bX6UsABXb&8kLRQqnIW_SetPiJi=RIH^y7zua3%=CMSLQoX$r!g zUwOHxq3v&~T+-aCb516!=X81r_b$B2DQfPG?nbi6-4F8Ce6i~G2`~Xf#jRjvI z(8w@CGblxyFX2c?8F?m2rXspG6=TXoOqn2-6a{3WU@r=l{Qipq3~eqG8koq}udIhJWEOGmOu46+9#i}N*!xCGYCtM zJs{PB_*^1tf$2TO8!}r7j0~ov!R{0$H{b&0$uiswl+(SEKC~#W#lo42N~^Cj3a78` zRtK#0HmoaZ)|H6$b_dG8ZlnB;TKSzw`6}Knzt^-+gm0zt)B!~d~>4NL%PK;y_wJvsj?+!3BK8=>sM+%JSovj4$FC%#7r2qf` literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourse.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourse.class new file mode 100644 index 0000000000000000000000000000000000000000..2f26740ee846d07a8aad4b2d6d7e416ea9c5b020 GIT binary patch literal 1645 zcmcIjT~8B16g|^Vs4bL!DJlpm*cM^mMM)Z>2?^o{rin2znQq52*zV%)6ym?qL}KED zKfoVlymxoIlv*Bn=**qlGv}VU_w1j)zkUN~VIzx#!21(p4g1>fxY9DpcC_tUzH4jQ z_uWtCOnSj~wO&lJ!P=79>57G%_5O^T=X` zqiPv1`kITY8$$Q8HzYv@h^s|JwRaZ7IG~=E&{7mL9dv z(Ch?0)~~T5H!ba&BjpKHddh?bxHREvy)oA8(L@ODPleZ}&5z3zV*1b)l}o%A#)xS{ zSaqB)LNQ%2rfZEnRE)32soWu1)>bbxkF|W+=3DGsfyK8&$LN7-YhTl}TaM!z$=?U{ zeuZBCS$alVCo~J~8|B!_dQMj9cG()+SS2GR>8ng4!49J+!Tt(%au@A0gvj9r<@0pv z)1IQe()@`+bK@Mv=Hqjenw#gSd=GBbXsrs}a?;HoD1M_LLGJ3ZK|fq oq^8DHP3zM&jX5Wr-6m<;xBi6)@NZ?Vh*3qDy$J7M=0k`W-K>z>% literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourseDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCourseDto.class new file mode 100644 index 0000000000000000000000000000000000000000..358a4c09449c1b7e70dda64b41cb127359b5f815 GIT binary patch literal 1734 zcmcJPT~8B16o%g^rG>IUDNvO!r6}}6S(Qr+3Bj0ojAco?tGio>{whsG z6EFM${wU)+yJabLx$H&fbZ5_b=b7`dfBydZ4PYA^S)>Hse+ zYuU2pyB~sc=_yk>{<-pevu#_R>nrcfwv=yv>L@?3UB{Horl*=RQ0D9I){fipeDyqV zvq%f%Pvx03TheKo`;Aj&1zBVSX2M!|X4z(NY3aRv5c|skwVBFkQb=*mVNc zRNkgQF7n)yZH5(kTn+-ycAE4U?`o0S`P>%BJhL4;*b*2jR}KZzJMOW{VF-Bx2F3*@ z>$aozI_-w?-b6Hox@*bSp(G*cx|`|X#P$V_>VwQ2@sG-bJ7KeOn8PjH$>O%a+ksug zz#QgT>!u2#p14-m*>a_)Y?qZCg}|bLEJifpT?6;9%v#10QNd@f#db@sLstxpBBw(i z7+BSz`8aet>ICgpHZX>qz{F)6N4+BOZh%QA%S#oUxW})g$F>bPR7{San4DrcAvd~h z$7BB}6SKXJ-9%`=eC*e~Hb>@w!)2)#ww|=&f780I#|+EN&JcgYTvNIX{8*%PPD9J2 z(T?yvM~3SJpEhU(?Qr!cCclMlQ(SWl1KrYeEQJ3;Cro38Pg-D>9$G-sYTrdIP_6wy z@d69lE?uCc?fna^T?rS-224^oPmw9g6|g{AeMm79>K;N#R8Zw%HNG1be9$MeDY@#C zP1*?kI~KljBSk$R6K)n0u^-|Qy>##vt;Wt&YvjG+i!MG#%K09qOw5!Cvl=sHDIs*1 zbM0p;^)oFcGOhC>2~4GarV^P}ZeY?Cj+sV7CXP=6(^@~%Y9iC)WTu`vMtQ1|sdfXC Q?zWg|EM$7ZHHD}D0DyXZCIA2c literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCustomRepository.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionCustomRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..959896679b4e7102435e7dbfe2901f7a67810c0d GIT binary patch literal 409 zcma)&K~6$35Qe8u;VDYO#-)4Q*aM&@YGOj7LB$IUeM22mT5RV*xS9(O;Gv9t3emW7 zHJQKtXTE>lKVIJe;1aqe6bj5jbnQ4z0%{@S*#w<}VRR`>WrdM6`m~~$^qfr$iQ4%MUT_*|iM_Po$65_gqT?M@vE>S!5R*xn#GNRyU%D&o~hSk;j ziN%qEe*gC8^H?H#J2?54YGJ2Gi)Jypfr9-pTaQx5Y&-v_?d;d|p%=0g2N!u(cN=Mg z(Yqsg4%IzIoRj1WDCmqs9Sz-ZgL%9~|C{%;p>Hctu#+zhpmO{cw4s19cLnT0C)ZY{ LZyw>`OFaApH<*7+ literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionDto.class new file mode 100644 index 0000000000000000000000000000000000000000..cbc56358e570963d6c951f17d135dcb3fc35fa54 GIT binary patch literal 3980 zcmcJRYjYGu6o%gk$u0>E2_Ya? z_S`J+qtK7tH1(%Q?Q1`OH zz0-PiBa6%Eb};bN+w|Oq7uDT`)u(}BA|UDdV(*>g%(+V|azLc-O(cdXVM07ttLY z9%&QSw(NT0;JiW^yM<~*VH9Hyc60KQQRC1n(^`6zS??+IqEBYI3h&FTKF*pkG8Kmp z6!sX0%L*R~(;ywp$>!=6$14iTtpBmXC*nAuBejWI+@=)v8n>$o*ThXQ7ewnad|hFm z8JL{iJUfo8k2xQt#g?3j59YZG{CHmut^3!jfQ#!-}aSaad6}V8HGxe1)$Kw=M?n~Xo*195Fc~oX1%<%%Hpk~G(lU(R(+G+u%lDcTdlHT`@w7| z*T6vAg>ymf3kPa0ih^XO;iYMimcdNl{%^HY#)gAy|1u`eKW{ubU92s3d;afXL+FxT z!z;Q=x`(!G656r{Xv-qkmes5+%TrsHp|-3xZCO;>Qs}gY8K3tDpNIMD60JkqGx{rz z{H)!M@!7{^LHBOD9_L@mIN=1|<}V5G4m~8mAGG58jRY7Sdx4WpoD)0T#JJcCO-9Xxu;~)N`gXLpXBP z@L6OUFEY7Srd9Yhrtu=vB{E&^z$6OHnR+!7FER_$#Uj%cD^tzRbg{@ZNv5d|Oi~Rw z(;m&lyVJrnQDnMiWeV(PH&JAoA=8ZxOj4OSlhRDQrY%g9MW&lprpI=s$s*G{nJOKa zq)X&XQao&2R;KA9(}I<$ZfBY*KG6NEO6&lZ^; zkm+FuCh49zQ@>{7SB`~gzR2{*%JkIEG+$)$$yDpWB%M5G+OL`TWo2PnEHc%tOwa60 zi$$hqWNLI^l3OKb8q`eun6oe~7nx!!Q^T&O#+;BP50nNnkM+?(~BGZEl0;>BkOCa@*!iLuBegte@+6PAknT;f6M+kNgeGc;mYO literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionFactory.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..0a096ebd3af529477822c1b941adfc8688ab0bcd GIT binary patch literal 2600 zcmcImZEqVz5PsH4>e#(D&THGy7E{{dB&EmuOP#iaG=Wg3DLA!SgoHMi&2=05*6gjD z_Al@QkoXTslrKag5(qx>qYyK9=fqLP2R@vvy}g^=nR#aCnOXn+&u_m2Si-#`3Ibnu zo!@Kgc*{uNsUXsU@naKc*^bSQ+mlxLGK%+E#I6w+Gc zRH$q{4-9vlsn6wMxL(A#Kr@G9D`OJVMNA2_aBP0-M%D0(RcRLVxq7>FOY66K{IB&QcB` z!&pWIZ*iTxE%3u26SK%)K8pcKKcN3D%8(A|{ilR0`s86tSlbqu98l{cpgsNmL0J`e zm@FEU&WMDQKj(Cwd4$AN=Z?yFwpbVs#xtm$rDiX!7=)(&e8V?#gga!=ji^4T+X*N>@) zl;NcM&GgSeoq8kP=Jc-u7^XM6#pzk3FP~;X^T_-@Mt`A~0?PE{WzgG6dV1*wnorRe zUAP#dU99{)?O>y|15_Se`xO&3&eC{=#;Y_g(YQimj>ffp_%CR0YJWcQGDcu37{xf* zo}jl$OwsQwrg5IGkt)tm*B(nSR8v102J(n^@NR;uhWGG3S^a=)mkJ~X2^i*w_$Y(< zEh!gBHoNdADnG(wvQDmdbmHg&g?EtvRTJkksrI4sSuEoY zd00ubL?!V(B1p=9SMgcq_h#m|bbv{A`1w)%>G@tBFkd<}U&2~qo+BcwYcv(`Me@9h K2F)ITOKsqT75-CjEg?OIGjzb2+c|TII zaw`n_7awdh7+BQ-W?+=Tcqh=BG1?5n1qMG~#|EvAjxgZ9t|lsyY1%w7dN;!A63>oQ z_K^qZM^tRU6X_qy5cwP#dm+YXsIZd+E-@I&H<6lJEOxLdyAMGweiE?eR3tW&6mc2l7>txv z7)w_(6l(dLzva7f--sQCXmTs$Yi6q!J5|MPWfkRGhzaHfx0Zn@p7(lDS>~9$4Ias; zMp=EZ7!f_ZO{NO?p3zm_s=s~0>-%W1EqX2mg)j4^lTD%Yv~y-Dv-E7Cuv*#JUR&V3 zrvziUWl`F!>}-^F%7tfz1xl;ojGE{Lx_uqIlxeRxYDWgQPP4MA37y!rdHMLyrT8bF z(qdZM-h^Ap{%tv#raK1NvaT5)*HwD!rd!~4I^9bzEQ3#74zf0%T+ zuI#$6=A$!nw8R@~cbuT;)Ja!wp1d4NL!-|bnk>?9%+*R~Ppv}}lLj8GK^oruFbo-5 z)K4TmBsqPuUm^Qua_lo){7zCIOwxOZY=G9~h9;fHS73_luQudqxJKVmI-j9+-NFsH nNs2)Rvyj@}`jhSLmMy1W`r!_}`(Uo=dA{K}OPaf+zX$Le5V;FS literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopic.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopic.class new file mode 100644 index 0000000000000000000000000000000000000000..abd7646299f6037a608769a5f6fe696354cec3b5 GIT binary patch literal 1347 zcmcIj+invv5Is(FAz@2;4U`sID4?Vu`~ihZ6#}VJD3?}ILE^#Ln5|2)4qm7IEs&^? zc;Ey0D8$&Cq}wPwRx8=#@p#UeIkSKM{`w7I7mrHFF&y1`_f%+fBF(7^Wx~G>WMY+$xriboBVi?fH*_89z6wjoGt8ZcQ^C6; zj`*AQi3}}6$-3LU4#Q&e;&v~#GLmMSq3rMXMNcv;WFii%QE@~8#b+v3c88%}&s>iZ zWL(1+jTS@xh3?1-W-uF|j2jFy^+u}uH87+R=7c>?rl&RUxcxO_onO{v^xTYtskgwXUR{klm{ J(v~~~{sFRX8-4%) literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopicDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/aggregate/QuestionTopicDto.class new file mode 100644 index 0000000000000000000000000000000000000000..699ebb848e010eda3aa0cdf67ddc2c22d350b4ff GIT binary patch literal 1452 zcmcIjT~8B16g|_HuC2>gp$LLX{cMYIAAH1+7>x-@MGYS@8Xl(GaT!u}ad!%dze*F) z#0P(XKgxJ!w}p!JvDxgs-RZgKp8GMse*gFh;3*zD$O*hZHeRo*lXfIMqx?|&k(Wfi zmfa-!U`}PMJQ*gZDo(s3?Z;7~;uGzw#QWG&iP2H$$smYTAdT|g^pAF;W9>gTk%NN3 z%%MDyURQ>J_o{uUeB+=fFrS|4$lF#ibLmgA-BkjPv7wtCfw|U2+-7K0pyG9bQu_av zJR-I-gxEH*4g&&;PjslwhCsgF*b^u`i#n=|Jf>Y-g)1=K(xKYw9ko@wlclV*B42j* zBxO)S|FB>VbRw|d8e?~;cE3Kh1nZ5xG8VAt;F`eOah2g>3D*V6fimgP?c}QU#?WL2 zvnxSh#f5__R@O}yx3DVU4g#~0&i@tKAF7SK?V^OTz~o@-(mfD(H@^OHb6+ZR5OrS5 znEPgU16XJ>ROmu|M4fi)+yN@9sydA87iD4P?Wh;~>V>wOU%h;J9@+gzjm78rP9VpJ z-Q(5G*Jt6pEvvFH!Z8ncS zllv9RpLviYS5^~J&VQxe#{;5l@CLn=T5PUSr5bjw&_7Nv`>;&TQQVan*z>0u*Bx^#9V`E^Q7~64GxKOfoygQCoBSy1+ z`~gl>apB5^D>p8wf)w2OQ53C_v^#6$)oQnrgDj0S^L^d@^>okrpMU@O3jmg3GY>fe z`<^sg4)gc8WlCzh%;u)gZDu(>-PyC5lO9r6 z`ews&YYubk<~PxY)!_Yl^fnKp1f~wGL(6n5ob+JtfZCG4xWnspDhNzhxu~1Iv%h#? zR@J)-fytO;J7gvBZFOjSBM;hkM%YL<9n)=5kNZq=aYEp`;Tf%ZUC_EEX`8b{fVAUF z%w_Tpfv5AsbDibg@W?&ASlA^ny25KT50j`{N9a`{xG2CyGP>~471Za>A*~rUZ+gtJ zg8VT&!S@{6^luxb;!cAaHEKJUb~VGZ?8jD}8gn?iT&FG-7{Xe)D-Fw0a-Mn{3t0}6 zbIz!7>KiVXM$?jZ!;r{Bk3CF5oyU{qXe*SXtx)dX3jMK_eeD7c%g<mShUA82$P%O!Pi={B`69|+-7yx3I+tWhJ;(vl5dx@TICKBB+KC24O+wY zfrlGa+LdUg@(yKqi@;8A8G`ngU4?dXMSHj_x%7RF$yI>2vn7MR{uw2Mo&;?dv?S0? zs4VoK1e)*zK_m!YfcG+qsPp!YUR%VdM0Sa&mrW(&Vm1-=^}Wp)@G#N6liMz6BI+g# zDB?g6i6Y+0B%;pSJ9-3W@FW|Y!}LB+;7asXuXCzMcNXZGa%w^MrePbnPcvHQ z!LxS6+tpLUUt%h=RtQ{6Em8z7hM=Kou`A&_#oi$*ZS$6}=^6|A^VPVzsU_>t55scM z-3i=CY;D-Z1m(d=S5PChCCG1y$B_|PvMMnFabF@kE2DL8O$&J2^StsPd$O@zj2g<%lLO3 zX98X?;NJ=LT!I(j3XW#+D&`OvZ+KyF;3b548J~kiui!lf&*5_j{%QjF^)tZ72pG5! z1%){nh1c;p2X82X5&tyuZzhmDN+Y=m z@26V-K>{#M1O5;`YFqztAJ%`8KyuQX1mHGiPA3&R*^(sW4{2bP3}8Q{fh}QHBw3au z($8sNpJo92B@OHjDxPFnk^sM^fqj+%?6=-vIk=1em`k$k^Mus+Jq_$$2Cye-V5=Fx dp7sVCfi+c+zGxS`bsRsytAHEvOZXZ#{sVGQ#JB(e literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/CreatedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/CreatedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..7e159b1d969148168742af6fa5cd8553d53cf957 GIT binary patch literal 1912 zcmcIl+invv5FLlMX_i8vP)fPF<)t9BDLPAv$NGak{D)H!eryI8$d$FCid=a03 z1QJL*@==KKW|xpEAS&R&UVAb=b2-P(*KZ#_0l-7J(S!zrUg48Lu3XoM#G}+on#38Y zMDEOvKM)oZq1^#mm-Lmi#-ZI;5?%6UfX*wU6WmAbUD6Xe&6UoEi{QU*QFu%rhab>3 zVTytNf6<;-`D}!As+bIB?klbQ0|sB#I)5EoWSPZ`YD(&0FR(C9d1K$Uj{z@@v8mEx z)V^odP)}WGUkzQ>F|xOBZZnu#Hz_uu#bAC{?29B9bYR0;V`t$cYg~P$Ejp9$W6GuJ z7b-79@WPsIj(vBJN3E?M@)TuG#HPFua!+K)SIIlhP@@fm#bZRr75zu7HU06B{#BkD zbX*(H`@+i}_Y?;Y3zP&nic|Qw5?fq}Egl!!pGAvnu?-g_w$))}s$UrEL-C_>xD-0% z)kR)+XXxzT7Rbj%%KHQ7IiXOyNJeE9O83yJP=@F_Z-t~1(f3Tsf0kO6NoiVpqCIA4 zZx20gMy;CEDq86^AusL?7d+$@wQ`dY#000PRURKnW8+X_5wtYH`6t8(X5rKvOv7mg zi))>cfL?k_!Yc(f-%%Q$4*FfRTY@yhV4-8A$hU=6VP6@id`~$ByT{P{n5K^Xc(f^z zT5+R0$6%>89%%`KGn*wx&x94k7Ab_msZupqall}Ctuu+2vJuNK2HLAWZY!s#?uS7^ zq^bQ$KaP20nFgQP{LI!i2s(k&n z=!k8w@JVTy9;|$GnU;48lXpJs4{4xWIkG&kVd59sT(U!^fc GVfc6R0dX?` literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/DeletedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/DeletedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..9d2552d58ce8ca98266b3cfe39f04f34eb2e707c GIT binary patch literal 1912 zcmcIl+invv5FMwNG)tkSP)fPF<)t9BDLPAgkQb1fvB_18`bmMkoFSgT`FX9uB zKmv(JJ_<43>=J?kq5>Z5wI|~$mmM~~^l@B`W= zOf%5`FWR#zpUsd?6_dg2ZKajJ!{GBq=dWXnEVGyik4YWuITpq#Z|s})5#XgUHdR`T z+V|8N>ZuFutD(y}M)vmAZ3ff#Oo~ltF<96Y`y$B&9oV$i*f}`H8W&$`i_YZxm~v_Q zh04niys)O5W8dB4QERJ*JVluku_-Tv+!Gn{b@Glg)M%-=G#(>5uIT^CTGJm7>0jrm zLC3Z6yf3`$aZhpZus}(Gqd0|+E3w6u*y3@q{aLiQ7Ta(^Vp|_pruv1kJ`_JHhfASD zUR~sMcZSaXO@Vw|q`W_Ho)ZeSi)2(*p>z+e3T23{^HxYI5q;03{70!(nUtoDN7`eC z_WID{YSgMpt)i7)6Y}ETaKS@fQ7bnYK}>LpTIKPPG&T<<7C}oBoPR)!U=EJY!wj5Y zu)NV33Fw8lB)n2!3mv8L$)MjwyCq0743;`ZihNsG750^J+V_-WuzLi}k7?@2k4Kvl zsTDW6a|~8$<4-MNu=u#-=&7)R*dm26I9{p-D-IZ}ZgeK`QZ{1s`9OQs$8F^l)qOW8 zsJu*sbE9KymbfBxZ70AV7`vB9L!=-2;d7Xj`E-=?;Zn8d;5>u3e{ee|<@0-1|GiEa z%wTv!8ALD4z)F0ef)GxP-;_A$hrzXpy`Zu!nxNz49=$_G6I{CqYYa|Ls*u6_V^zL> zTXe+18Zelm|0sY4t!7}BzD?TQBx|roA7Jj)QuGeyKb1ZUG|!SJ&{(8-zFbFe5|(JU zOrzC+Q}j7agTWbCA(*qY4u@9hTZ1=fJp?~D0etNj;4=&^(7`FVNOJ=&eTTkG`*j-A H6o!8T*KBbr literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/QuestionEventHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/QuestionEventHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..5977b4d4247e64c0934f4bffff052cba05239d9f GIT binary patch literal 1553 zcmbVM-%k@k5T35qgMpX37oRL4AqFE!HE5B=y=a+vmfcQ;J9ui;3`fnEQmF#b9|%TV}Is z4e8*hWCri1;FiYNSZOg4Z-R8ldxC+;XE&5qev`qkwJFH{ab{;0ItBcNYFXcBP

_}^6DZVs}lrJhlM)SV3EPnT6-d9 z$6IR6Rv~tytu(&qXI->=g7l*SY8xriePLCx9yu$%ryPTPit|6F?**>}>h1aG=zAvK z6;@>ENd(F{LBBN~$DNow3v27c^7>w-Q3pM3taVgElhn6V)5DYhH;$;Wqdg{QTbr#p zxW{1i?Ea$mVi0sp-%5O{3c*%RyUfBu?aC@JxI{PV3_X`&maGa?VXpL0h7ged3auVd zR1p_^hx(hvYhPjhXBl&yzO#e@^xmLvy|e>Zf}6BjrngoA1A!JHw_t?;Z<9R(cMgGz T?L*+ZrvNuk0cLQ&#CY%*%l!8k literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/UpdatedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/handling/handlers/UpdatedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..6d406348a01d01a18dc9c1e35affe6c0c46a4075 GIT binary patch literal 1912 zcmcIl+invv5FLlMX_i7uxfUq6<)t9BDLPAv$NC6dED)H!eryIk@UTmi=U&JRM zfdmqdd=z55*(D7Ms1@*FuRR%`xtwF?+xL&30pKCrXhMTQukgtrSFUSB;!$cPP2!AH zB6nuj?+c5G&~6{COZrM$<${YKxeGGVMj7^mm zqxNlDLp^n&eKmAh$H?Bkxx-*;-K5xr7K8cMVoxNwpaUD$8aoRoS!4O7w&+a0hbfn) zU#Pqc!3%4;IriN>9<{c6$WxR#5u5Tt$TuQGzDnM4h8k@kEFL2|uIT^8TGJm7>0jlk zLC3Z6yf3`$aZhpZpg>80qd0|+E3w6u*y3@q9W7d1i*2|dv8@g(Q~knNABrE9!==z6 zuP*YsJ40vxu0TF6Qr;gp&k2RvMKUU@P`Za!g)&6fc`GE9h`wi1epqT%CZ%cZiT0SG zy*>1}8MSIst7xUyguJ*nT=0-r)XGgp5EGoDR(X6Rjg13|MbOd&=R=4Q%)+TTn1<5~ z&aZVw0^0JHgjWh|zN0ig8}z$qw*_g2!D7cqk?#nr!oD(2`JQqNULQmAW12el zYQ>H29D|Fs@t2k`Sa@1;v?;70wn!liPL-;`iUS5qYn@5Fl#N(=G0r7|!L^&P!rEH=irnv#venMZT{VI(q H3d4T@iX(Bl literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionCreatedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionCreatedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..76cd7ffbc252220a3864fe9553abf5c9a87beb37 GIT binary patch literal 3012 zcmcgtTT>%N6h4hWLbw{hU1ZlCa8=MH<7K@pdoywowt~2FsoDo0I+G@8HZwi;bb$4b zX=#-rs|}pyU%>*{>>kM{_-0URcX6G1B@E6@jGpq)D`!Q2qGCM zKT(0??L-}!Q?7;2qvTZR#1CblRU-6>3`FAp&=HA|D)PmNh)m+gojPDz{?qh)O$%;B zW5eDSXpqqzrJH^dYZ*0K4G*{ z3smSI@x%yy7PkFk9z0KwCw{KvKf!)e>5v;>6=H6n8ZnxFf#1r`&Pt=*b`YtcB_5$TJuwPbQv>Y^IPWWXL;}rsZ9pb6^@fQ*P##mP;kNP0M4nK<_h}&Gu5- zDYj6PH5rL-T!-&>8KLav^N@oUU@1KuG%blHJ*xd53}?q8!O#R4nyDROTB`9aw=9Rr zh7%=(>3GeK@XXS3rkB&eigcIJ!mE1>Eyif92i0XPyFE`Jqxl}TWv5HDRit~2n*VCT zb@6{LB%=vWHc8-cgAWFn2b*qqH%hY z3RJ}N2);xERH8DC;gqmWr0XP2;r?};jbbfBrW2aM!z{hw6tAU<*VA38e~=mY!@Lld7%x`Y2Q_gtbC zs$lmaWVu8>mRZO($aTmZWFB$@auae3;z1aFL?7e7XVAiS4Stei@LrBVMxXY9@GuwQ zGx|K&hc9w`sOBQ9(*0b7yeKwu5gzmr#da>j!#)spauFWU<10t;G#BB^J`fIa5vqM4 pJj+G+iq@_i#rL@g>wO^bT!f825JWD*W*^l&&PDhdvot_ke*uME&?W!? literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionDeletedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/publish/QuestionDeletedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..6c7379a0ffd1efb2acd137d33130fc4fc1649976 GIT binary patch literal 3012 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 JDy@DB;vdu`b;|$% literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/DeletedSubscription.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/DeletedSubscription.class new file mode 100644 index 0000000000000000000000000000000000000000..9ac263352e4629163dcfa8f014113967f0b35b1e GIT binary patch literal 2966 zcmds3TW=gS6h7W0dt=jt^g>f^X1S#;%(RFHL{OzABvPdf4apJ-q^k0I&hEOMu?O3m zH2fKU0*^=_!8<<+ab|WlA??s^)J8}=j6AlFzjMxaF7}^){rWoqbl{U3R2X3D!*Q%! z--yto)JmGr8L33<%)lQCi=oi&5UmRnC9QF250pd~{xC-8mC+#{p!P0wu({qsoSY2R~y6P87D}RT<{k8K`bA6w|{Dz6J28-n32@#vt8e4}YHuv+l+M+Y@0Y+S! zBvrAMKUR|aqZD~bqVZ(x@>3BGlqTzy=E^&64mBUB7@Lhro|WKpZ@9>Tr$Spo zdSj1DWSfm?A{91lW<^jU_%!It5c*##KQE`w3AYI?c(nx$SYdE^t$R|^d)_KNT+d2= zsY^Nd^*HIH?Fp)41}j}7MZ7Pp%I3x5ydNpYVEE$wXO><}J#Tp%{m4W+!it1iwI$_? zBcY=hi*#nNy4EctE_Z8ncdWfia9=q^UUafb&6^eB-0m7X3|*RWFv#E!jeQhKqdh+M zle;3!i>0fa&zcsQQ+~$D5k}N-=(@;zL9QX}xFaps>+lwXpJwQoWj?%&3Y?{jFj&ar z!QkqI+|Gcw-Y`j`{4vj8mh$B-HE!xY$Mq zaHV*K^l|D+kX0&!8>LiHN?J78uh$@;XTl`nHMqs#Qdw3Jh1<$GO4lA8 zckm7{n4^bQ1+KvYROxR8YEY-KN;?f`(#|V1`;??AG&X;O)^{rxeu3pbXjXxXg(Q&l zCHimCNK$YauF&`zeG7~Lv+b*}N+8$r?Fzh3;~Vrn4gO{cc>CYM-_F7LG58{>%DieL zybJHmh=1$Z_yF${ei;)Vlo)&Gd0=a20K5AD{wQ9QkDQ8(fl@j KRa*TR!oLA<0(H3n literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/UpdatedSubscription.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/events/subscribe/UpdatedSubscription.class new file mode 100644 index 0000000000000000000000000000000000000000..61db526020ea717898689d1afb60a508cffc8734 GIT binary patch literal 2966 zcmds3ZEqYk5FTHWd*jlC(3YmWnS++Jz;289fC#E|M2S@CLPK(m1X5M`ymNPMckRXY zB@KUupTI{Xkl;H%3NgF4mymX8FKQzsKCHa9$Ir|>^J4$`*RQ_=KnFgnL4^UPJ{-r& z^^FKUO0A>`osmk!&J6sauow#M4$-HgXKr!K!mZ-!|-1J5lYXX8k^w|ErWWuoBypdSehd5O;j=XzWdTf z7G{Uv7FHM7q^|mv(#qdq@L=uy)ZEx-Fu!3Utid9ActXUcwZ_(AiOv0dUt4r0KEQ}e zlcXvZ8FM_frXOSCzU0kDZ-hKT8BX(t}S@3ei9Hq&_GzB`7i_IGri8X$y`sr9JUTC8@DKbNa*$E``qQ z=_8-AG7(P4{a8uvk5c3%iN=$$%TGl(P@1e)nk(5V-qk!?1niB#CInH52a;Ip8wLg;^~{JfkxC)_5q;Pnb@weCqt?|G~Aa6K#e zr7q>*H{+y_wkN2H8LV`T6!E^WDw`LJ^M0fpgW=2fpILe_^}OY6^dl4P2rClw)Y};> zj)ab4EYg|5>RPvqxZJJP-Ldv6!F}ZvdC|!#HE&jgbGvKoFm!3g!61V_H1=^QjrRD& zPwt8^FP5%yK5JTJPWc%pM;KAVq3a^=1-XW>9PZPM1j)bG^`r_~oT;(AfM9THmc)_yv~#pjidpC?tWT zFVcUDMv{U{aGAz8>04j~m~CHyRRX!1Z&%0{$38wkHHs7RpwP2 z;RE<^M*LgP#|OAZ_+?C7FERGci@?^-0Cx99U^mVHw)=drIk-t=R^a12N?8JYLi5}7 KRcZB82>%A94tCi9 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/service/QuestionService.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/question/service/QuestionService.class new file mode 100644 index 0000000000000000000000000000000000000000..481b24cd5dad048854f77f029f21589046157201 GIT binary patch literal 9987 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 U*Z~u%Gy<#oy9VnxlGqJ^f4Eqsy#N3J literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCourseExecutionDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizCourseExecutionDto.class new file mode 100644 index 0000000000000000000000000000000000000000..571d23c4134eaa28b5f50d3c5564b35287a283c8 GIT binary patch literal 2114 zcmcJPTTc@~6vxk$(n?uw1uC~93h1_678SfbJ_@Rvdvs>D_U3lw5=X7Sz{Lk;qoHKv^{`yTs+w>$!F+uM?dRnbwx^KG9Egt)g3H?N#wa8eWnTg2wz+)7JK-gJ|ZtUaUw#Q^x-y+ZPdb zOM*rVEva40lV#~_Vl}j7Pp?8K-R8URIi^*HYbNBnjf|`>RYB7oAM}z$!Uxj9*ThrP zGQBN9J!PeSd4qerYq>E z(A6_Xnn!9ELu}>_Q#3(SNtzV&wtJ~4G)*&tvSsP*Tu~F_YOd|3FRG%C1kEaxq-)GN zr_fEhh3oIs3W{ayKaobcu*QNyeUxI2+X~%bjmb`pMisGtPNDw5e@UTb_MeUNZxt7B zT2)A)6bhMpDkwyG(L{pYb@MLKoiC+#Y?of^4%)^;pX12gtB*XRMw~b|My7LPaK7X1 zI~vE!8zwt9G+S~u>RapJ1f}*-7KVIb^1Cq|{Q#}*Ci-af)$K-~o*B#rhT( z=3CqWYzls8YK%je-G>#hz4@Os{LObu`%W>81CSe}QT$T^CmN%1%vd0U04z|2W#2^> z$S?h%>vhVqy-}xmwu^OA+2-rC(h4v|32YihfDwqNAv{Vm7#9$L1F}3s8v*&BMr#Os z7puJj6Q)4_U}OpnHb~<;Wxrw}22DZ?UYv+b;U01iQrLp!Xo>t1*jtP#e{}|@nr&lB zG?^0qNjI62kPzI57{g5SVWzoArghqgVww*#Er4n95+=U&CR3l!guaeoQo~GYB-4Xv zCN<2o45pP!n79I)O#MC+-i`>Sl`zv?Y{ZEIfdR5|iGqc<_n`-{<>! z1HcZ-5()#?E@2K{D$ACC1aJ z`>aWtGk2msNqm$rH?Xd$Ck)g&JXST^WMFlX94#MwU;~?f<#vje1`N!`uhR@HMi#LA E0e2=m(EtDd literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizDto.class new file mode 100644 index 0000000000000000000000000000000000000000..0d4ff4918e890963e90b1831f79474fa3f5963dc GIT binary patch literal 4329 zcmbuBYf}_Q6o$_rumY`u0*Rs~8ZTT7lZa6x1TWwPH3}<*iqnmP9X|b;w>kXw&O=zGK}n^=Y~Nf!%feXk-bs!wrwY$4`tqo zrTq}ElW~xtW{W!4oK43rIDX!qTV9iHoS{~Wy46=Nu(L8mcyHTTE=Y?y(il@Yi;hh1 zj-B#jnU~?HMJ=%xv+v=m|7PRR^Yi#;M@~j==&i?I;9InxxfWlp!}|azyaLZM2||mG zCZl7oDDCOMbqf4>5pM``&<%XIP>K*EaqjX<#bp`JtvoErh(kvf9dd(G7|Gi&q+3d6 zs|HGzidbzMyF)U8;=zp@+=Q@4(J zz88;Mw6A|)$)e_oASc^t9~~F;HXX$ZrafQIs+BD!=|8M8vE*P&kf_SX=6KZuawC9y z5eES0`x{tv!!bt(mfGn(I-8+07Cm@#HX9Bs=p3DgD)KT;bIkJW=^v<);Eq7s$f5y3 z8QRY@LxL{Q`%oeVFDAO}t{GNQE|0PWwNX2d`cTkC9>r5m?h2ZnE(z*LoGuHx!cHA< z;(}wHv&YARMB*_jXpBBV2{L)D{QGtw3Vo|y1szKK z?g+ZeY3hbwBC4u#Vb>`^ok>z22%4rDRIm!ERZ(qvDs{x;=LB`pVMcl=Xo1HIj87|) zT^0o$NnDl$Jz|%3xXdM0#}1zf>P{R!7xV?=vcpnRjO_4CP)`E+r67lvlbur)WzhnX z_GVq}sY-dyoSEsItx(8LtMpc_(y=GR9J&t4Nb_Z@)pw+%)#X zv!dIcYM+X1*@c5ui4=mwhaEv;dY-FRe8(%14BF#>T^LcTG^h&S{-Y`=NV#eXssXJC zh~o-4r*gs0ibSm$KAVaTF-~Us`*!Hv+s#MV_D0v@#ThM))xc%W?(Zmf+S@bMr(V3J* zb5Rz}Ls@hNWl`bEqKuVAwJM9ER2GE-dkpj72*dLj-bP?8*p|WHsqYu%b^_0KOiyrc zhU-cEB@!okhu*~-2RH=}4)8ZD`$`TlIP@ExE>l0-!7>fA{h&-EY(FZ~Rkqj4G|u)$ znI_oYF4H}>_scZPcD_tmwx5>iG216)ddl{BncQ8#YijMs0N4k(Vvy0ReP<7Wj6%VQ}m7G8LjIJ)rURobvgntrAJ zpYfpy^AWYGkAub(t&#^X9y|`qDQX=YLT+}~kR*8+yKYx8wWdt1N(m`b1|cjy5%JWS zhIOWkMy56T%EUCRGhGJLl{!qU%9N>1G2y&5FpcO;*NjXBGt-F9G!CZgb(pw9Ql<{Y zgyY@7bX8}%Wn?OvnXc+gcffSF4ii^v$|MvME))i)ah>VDk;$jPw0Gk=(=3?g>M(KV zNSO{OCfrX9OcOfOf{|(6%rv1hErID#9VYH#Dbpdvgu9S|>7LH?#K^Q^uDg3W(=#wV zufxP0HD&5lOt^Fzm}YgRWg}Bak!kN{b*2?C8p8D=@vT!^9_W%G9Hna6dLMxjNGiMy4%#VbW98`Iww7KZ5C}I!t^UNSS)U)I=}U SbDO?~<>vJby`t}K9Q_x@2rbV5 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizFactory.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..16fa77e10b7e111453db0fc1076b6b64ba11eea6 GIT binary patch literal 2587 zcmb_eTTdHD6#mAT7@H*+41|z0F=^A9K-u1JPSUtsS{j?Hi^*_yV{Ig z74G?#sio~H+c!hsR+bz32m06wq-lBKu?#}9qwK&BWpJcy8Jgcj>RZ!toIpC3mgW{s zS1nun!P5fr0_7*xk!8A;=a?INPo%8{F5Ok0()R@hYIC~+`6Yi}mN0;#fk9jn7;P#~ zu1B3c8MLiER|+3X6tG#7S#E?GmXfxM`q@S>hqBH;ydjGJ}>WvjW%7 zVYKQTxU6cngf|Xv;oqS>5z$8M(-G% aj$4-QGhMk3(0iKa3qh4& z$BzPAt}3pjWsX?-ATM&F1g>sJ9^c~hIHVCoId(fV(2FtPKis zo!sXn%sS7+W7Dhsqej z7}+1EQ3Vt9o5m!r(@Rsu6{_mfs1YXnQcdtuUkDi=;KSIz1$=~$$?_+(tC*u@&;q+# z$KBN3uSq#aYcunIqWnD!_Imyl_nw{M!K3ldPOuEnUkqXbXuOjbrUw`prrxU7Zzt>Wspz^^LWIwQ~qrz=LS>GTI0KXM`e{4e?bnrT-qdgObjxSE| z(wMH20T7wzI?#4U&~`0GFiH@+8gj;NMkMv7pwymet2`yCSfE)d2};)L%^X2ifzp)W zXt6+9n&GHgpd94|6%TZv2K9ZAt*+uCj4!)Q8EQTcFH`nJM}m$I{(0+Bf9Z{~C#L;Q zhO(zD#wMs)`wyA8NoiwGc$CGCvV+-=hFhuCV~rhkAINADS)Dc}CeMR(Z)AqWYkXrn zI}gmSWuO+WP2Yt7>=Ub%jY-7IM7eLt|e zr`D)Ks6UsYdl_nqtlw%x)8!wr@JCA9T=r5Pv BN#Fng literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizOptionDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizOptionDto.class new file mode 100644 index 0000000000000000000000000000000000000000..3cc8f213a52542883930843143052539398c377f GIT binary patch literal 1784 zcmcJPTTc@~7>3`0($caDl!FQ)2nszYtKu0XL5vAW6$zvl+-;Y!3@JOfyM=_`=pJpwyNpW16c@!$`y$f{$F@d22 z=h(3;j#sufO9#>o6Nn3pMyE>K+tOzdb?THVQozzt?8oY1N%{gKg;wx}7s|5qmjx28 z=C>CLq}%SdLtlAiUME$su6>`C1jMdDd_{RGTovfeWQzi^b-gE3=*6&wYq%~jSWup< zRotmjqzl@0Dn*B6q^cjr!hID86bo0O6tRn$%g?-=Ev9f26A9cB*t&FZ3zN7_0n0M1 z^}Nxyu}rqDGmXwAA#le+0{sSR#=>393Zxjl-e~A(M_|3BChnevBvK}B-ogSh0s|`8 zXf?XC;#;jN((#s4$l*Z(d4b{9Ak{A|Jj5a=u2l0aI+ZF1kTbGt>OHbB0E={HzH37% zuzdv`;sg00!+pK?$?=^-8S;fu(cq$@{_}9m^$Mly!EIp=Qk)@8b{k-RM<2uOQ60M-^ z%l*XA_sA{HHN`N{Jx14&=qqx@1G-k z2D_DeoOobt3`49i%$#XVvx=!>vNBh_j`@0wC#<)`(>}qCDb88vYKq&mHToZz`o@DE z<^tl;;doc_r+7v$6TC`ms>O48BDeTz|N8_fC)=3fO{RDxY?CR$6vDh9t{0i+E;8Ne z%JdvBx-rdNWSS+@+<%x%lbcM*h-m?fU6|~zylmHLzAMv9ys9&;(HfnSrkkCutVxyQ YOy%3Kl1-_Se7mn(SOXE(a#Vli9}jqe8UO$Q literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestion.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestion.class new file mode 100644 index 0000000000000000000000000000000000000000..c7b6a76e8f1769dbddf37f8d399ccd24737d8946 GIT binary patch literal 1858 zcmcJOT~8B16o$`~en2Voi-IjGC@O9F7!pl1K}iVFgakwc8*iuEu?)7musemsD}RzE zHYQ&91N>3ObEey6X%_Fe*qt-8@0@w&oOl2H{q-9FwqYX$F$PCxn)f=&YdV5!Y1zth zxaU|(bUf#iz7($H!uBqu>+!C#T*s5{g|ei_&wJ_%7wxty+d@lzh||NK^t5v96vP?i zPQ``b9bvcmLGx5vnnC*97wone6!)$KySA2X>25NZ8cL3p?nnlOkm^Xg%5I}5I~46W zxY5SU3CC?omqGHSvX$OquwM;r9HtK~`MLhPGTc^cFo^FsEt!Tf$Yx*?aty|*wZ;S> zUL^;XWJk|i8{LOq#kOFeW3HNI4>g~~oGGU~kRy4k1} zQsP`Fp>LC!YdHg>X zeVF@h_LlKoG5e?Sj4b*$2h)&;A{3yE?+Si@h9{G19`52u5s@$x5lhTOL_?$%az5s| zhjBh`wx%$gz$c9lLEiTvU*U{4CkXP2Cv6r>m}AbIKRXZw=OKtbTm#F`M{5K3@coXo6=S{}UK;UO&@DP?zMVq&9B&4LN*(lQjUS~1Hlnj`Xeog4s z$V3HW@CY8`dz7g-%Cr>8^aPfpn2MuJ%XnDz1}5rGP$%jFLunLKX_RRtlBpKWR2pUC z$h2_-6Zs@yB3Ce|NA_-Rl<8?C(@J#jLjE53={YjJxPgfr8!(YeW3X!8Yp{+ZedGZD E0F~Zdg8%>k literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestionDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizQuestionDto.class new file mode 100644 index 0000000000000000000000000000000000000000..57879460bd1f444bb92a23cf306e1ff4001ff6c3 GIT binary patch literal 1912 zcmcJPSx*yD6vxjk-6(^UQVM*8Pd+k%q=8*Dov~= zKKKFrP{#k6E)31^*oWTJdwZ7O+4}e2?>|KJmR_eQCaCgN8LfuxS6$Ok(sFFeHGJ2y z&4%xOQ5U8s4b$;2q~{w=+wxprdgrz!edDZUUm9k;?#a5Tq;ZJrVN3eTcAa<1O;KD> z_S8H#jfUyejf3i`v{Z_cg7U$u?HXn2A&h-#RvS`K=B#VIUlTM{x-#8&l&ni{N6>gr z<;YfSk_%~-m1jG3C{6T~_FNb`N>IY}YSI&wd}BMd+7&dS7b=3{du~l;XoMy;8mC*Z zEZL6y*lJd#chq6Zm0ZhgR7`M!vHKWTC$=x>xHO!~A%$ao7`}H3l?=_&T#D`pDh~l( zqq{VZF4d*#O#=6FS}%n9)a&Lb3A(3IiqZ_VsL=y@h=~c=cAK2PX5MWxTQ6%gN*T6( zq|sxx&V{VI-DkyuMq?etRgKmdF%8879t!?>LnDpSf;67!-tY+eJd_?F#Sc=QxV2BF zXEvq6UZGB;1tO`B}ic^d`g};p$*F2CYX&_NM$PDzbL^vmKdj=^W6UASY{T`_00;L$vak#rh zQ~0KJlqgTrxUqv71YieENY-6p2gTK&bh}LpEbq5ziRDV0be6?7tzQM0qa=*72rvof zIXIr8d7R4#zyaAkoOT29H-k12_z7MU0vD#h5aVPD1G1CG4_f$!hZuYkV({Z^WC~A_ zdnbimNRE~)u7drFF%!JbqpAxbrevQf8C1H@l!6Pv%Yk!{X=#vYF_LMEwxgJq2AP(@ zv~mLzcfHRv8ZcqyBAE0+COwkrc{Gzg$g~Eg^&6OY0{cv30TZ@6f@yt_X)}`PMRe~% Z^VR9y7MQkgVB&4)Gid?SOPn!!^&fzZf)xM& literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizRepository.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/aggregate/QuizRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..c0d8d513677577a9254432e255fbef4c3fcebdab GIT binary patch literal 1797 zcmb_dZBG<25S}7%aHt@@zgJKJO|GB(;-Sg;8q5jm9VC7tWp`K#d)u|`g80e*;}0;= zM8Eq-{1?XQ-W|IbW6tO%-R(@LGf$t+%zpdv=?effV4(s-3|3PovRK&-Err7%QGpg# z2TI0PZ@N8cP{_pYp|PT)0;4UOT@|1eZ!+~x$S5)xNr&PkiLWr#Ryl3lQM#~fJct^Tmi#rN~Z&MaQ-ZwAYuG*ISLJjX4URJD~!3j&4hbFQl&nM;NQTXD7j*Av5eSr*WOQyba^*0Tz?0CtH*jU|2LpxeLoDUEuEPFpD3U6Cq2xf zx72*8F!)ePy>7p%51d$sdevg{Klapm301iuZvPgWr7S}2h(=MIj}m^CyzEFrP1YIe zVKVSVD%!=I3YK~^wN@G<^m`bFF{sd>DkkX`>HSmv4As}uM?b>xuOtn@G`&a3256kj zHNNkkg41MwCYR5`Ir>h}`gs}`EL?=kq!?jviGonR|H<}h-(KMU^yjq@M@)2~yi9`2YX_ literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/QuizEventHandling.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/QuizEventHandling.class new file mode 100644 index 0000000000000000000000000000000000000000..39e72676a0e4ed9246ef2d20a2a7591fe76d44e4 GIT binary patch literal 5098 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 BSFHd5 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/QuizEventHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/QuizEventHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..aecf324604feb6943332dc72bdd79651bcbeadba GIT binary patch literal 1493 zcmb7EOK;Oa5S}feX-w#o@_x8Ep!Q;4IFYJSg^H*YP$8uVsh7=q>MYp1W_ObUKMW+0 z;LeXi%*H8ht3ni6wq|F?-#6dv%>MfQ?FRsCLs*9y21kX92br`z&0~i`Nugt_h2)vl zNA8##jJdMMXl$HIVYEdvlmc!1VIV)pI7H=a+~+FEq)JP}$G)W99l!a^Z=tC}jltGr zuH43n&bd@2bbK0v!MrDZg@v}#Y4eG}o6cm&|D?~;)L_aTj`FR5+YDAb)rfu1Xwqv* z9)rUv_=MIbk%~L1$7(91sy>yENM|>tl5UH^m-Q)#E;yHe3Y!9U>ijnj7}TEV1nY2> z!NNN}~X_$ zbR?c~P0?+ijM;8No~8ATQr5u9EbgMC9~)hn(xeOwX?b{lfAdroc9p{vO?#_72lp6E z=qG9?20>R3jKHVT7i#tV#T5Ap*Vcf+CAt-7=(!BDWYwS!b0ZIB2Lbu7(CQII`MBUa zG~X`Ie}%=LqnH)?&JqUDdyT%$ksZJ)T&L9ydK(@X2-Jt%gj)o7o9r35^B36vUIO2} P0JwDlFoXLej0b-J5=q^g literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/UpdatedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/handling/handlers/UpdatedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..38c3d5574d85c7c9a291a0d4c7957c5878b88e41 GIT binary patch literal 1852 zcmcIl+invv5FMwNG)qfM%dK49@KO-k6%tPbA)%@Wq=2B5N<2E=>Bg|J7u#tIzr#O( z1QNXSQHb$wN=Otg74TrM?TpV{&Kc*+*AJfn;4WOLLybXN_@tjJ*D)gTD7BI%aYibU zJG1Neg~dc@w~y8(JteJiX!n#vm%Qq$Hwo^c_AcoPo#sks)gstFD^lSxy&rx+TZd@| zyZ^`26N+OqgfZ~NVD`4s%HLt|ZoU0iK1G&U%qWi}IN8F&IOUCf-8urSG{&Y%3$Ki> z%JtY9s-Fwx`X2l6taWvV!Ssenu?`Iei!a2UNOD2Oo7NgT2gg|L#4~NtnS2jZE={jc zc?r)8YdSgh+$|n8w!6qvlsOTY@o`M=HjopK5E>6?9y<**#}&;@o*Hyq z8_#>f%P#li0}l$61UB?h_;?_wcp#{FR8YV76dwwzT9BZcRYs~;80$mQ!!)=QI>gjL zepr>t*uO52j|!3Z`p$Dgp)!$V$_$k5qE#UU(Q)1iN$H{Qnw0-2t3f2?(fR}JF++R3 zvbY*G0+9nxrNxApxHnwz5L491O-9!z7)6cp`cMv=2NH{*oe9Q2AVx3;3-d4o#~GYj zZx2QD)LRl-DXPV`()g&~>!95hq!R|qZ6ig#BdiMh!Qr&;Do5RV1dWgA=tvJki^1`r zuwid8SP2JXWni%Mu;k>iu!7hjMKD+>Rf2=Mmx+(_P$s^*)z@D2a7Q^sdEO0bDI10` zZnTZf5?6#O?FQ(5V_zoH5Wxq&>er-xHhp4l(Iz=G@ESDJ7YA#wd$}&bc|=mVE&;hU$t#Acd!Nw zrs#hLphl}1n5A!>b~ouXoJSvF?)h@`9_BxnHj6aRk|oesqItetM{oj`X?Kc7qXsMV zIZ1=TX*feLXK5WStSc6Ws+t>xN(8k7VX@-m@1kl)LQmsrz-a6#ehb^%3)QN#v9Z`} zE=Ir`^lCN5YcOs?7)^yxmNtMsEwstYV3kL{5}`>Vqr53h9Mv$=C>kbtCIm)TkafS& zGTDn2SA$lw!qv{fqZXov8fM2*hBi^88dq?3#Fs4<@wHR#w_-c#obFT(xo=_`cyb1m zlhz|Z_TLqO(DxYqHJ1WUUP@_fT&aChs>^FKY8JnI;el<5c9anZQtyGCWM!_{#(v7 zFRZ3%Zx9Oe`H3SPHF%xmcNHppDLVLX9J;tEIw)|W!+4t=@ENfX=XkKQLNA!mn@KFn(wR}#hEwT-YY43STq|9+MXna~U|dIi!)S7?fIl*jV`K5;!%pdt;?FrJGz zi`Nkv#raj}2C)_)V-bzxVS-+@7q2B3uP5tdvQFVe12mniH|RCHze%s#)k)SFyN&>O zmfnC9Z{qY092~-0_>FFTSA6>yn*Rg49=eVHe%!?Om=^FqWS&Aq#`_9+nBn zHOO_yBxDLQ4Y>ii32`7Z5Jn%+hxl(BHPErqI~hioGK@0%xC?~)nFycIrA{*gyk*}zRyJXl2$HV pi=Q(QR%z|h2!1BQdKU<_OoRtrARJ{PY;;l0FcV=D)7V2>e*>cL^S=N9 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizDeletedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizDeletedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..50cc6c1288917237e44102ae1200bf260e9352e3 GIT binary patch literal 3171 zcmcIlYf~IW6uphWE(-**fC&)efJQ;Xj*s|)n20J$<|9-1nJ3pa1$h5iQcg0(CL^A<}L$5b>V$T+RJZ z_|lD~FT5a@`})LF-1Wlvgsa$X2w%yVt7GBw*nQFz&s=`YLmj(Oa}N*)?jz`ydB8QV zt(cPnbu+pxRo#svCBpi?@*4a^sw3Bzq2{OBU5uhY_?{LrOyB5bGorE@sznUEu}kG(9o`xLrWu-pT@efTv>1j`Tjyg& zb5&n9+DJyM_f1TFPtJe} z(s~5Q-n$|c`W~adW-{REOBs!gE3;2ab$Lxr&61ZdJg_a%3NvHqpUz#VncZd7y(DYA zKyM*YXPvg9lvG8UVeH4pq2jR&j(N>NlSC+rNum=eiQ+Wkd8gF3bHJS%_XA8^&56)E zUY$GBxLv99kSi3hR%x5SN;;S4w5I$O+uHH8QG(@7kn5lN*@sAtYQz8wT-k2|egB$pM}O%*yCgXoCZ5uwQt@`KDc zCqf@M=Q#qH+RUS!*O}Q;iEhz512j$VG8)TvSlU0PKtoj#@~>>S?<8zewzIwKfl^>8 zRd(wGfrkC5`hT(_=A<{qIQfRtHn~2KwXZ$Hj9>#8FCje3g=XwWXJ#{9nFds(j~Gq; zdrzUI7!9ZLlS-o4+qMx_g(32>^WV>kArqP+Mz276=n73xfr@zU!zZqbN>ru+8pLxM zXURH5!#KYRT|d?`WF)3hJdDw+_Tshl;`MYLPuB^&Xn-cu^#;9W_c!TvyE^GQW!E79 zPtzN4;!T|1frA5BOTW>r@5*ogLbHEh*G0GS-;1009@8BD2kdi+D&%7KK4h++-or8m zxdyoo8HY?jCLuQ`ZU*v&vJZN z%0*b9ySWH?Q9Q^+_`HKC9_Aw4>i}UZ7vX*f2-~>`UtEyMygYoHi?G-M!uPodU((X0 pYw>d~!ZNK~8o|#+SnU9zo{R9H1B9bogtZQ;8Ra6ZV;Z|?<8O-<^Sl56 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizUpdatedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/publish/QuizUpdatedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..a84b1c8d6d89fdfef5152dca6be7540125476676 GIT binary patch literal 3171 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 EH@HVvBLDyZ literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/UpdatedSubscription.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/quiz/events/subscribe/UpdatedSubscription.class new file mode 100644 index 0000000000000000000000000000000000000000..027189db7590c21a48977c1409528b33c8de34b1 GIT binary patch literal 2894 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 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCourse.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicCourse.class new file mode 100644 index 0000000000000000000000000000000000000000..b9518c361d4a4214eb048ba8e626b724d1b3d83a GIT binary patch literal 1624 zcmcIjT~8B16g|^Vs4Xo0QdAIBur0#Ai;@tc2??SRYRNlHDo9f2E1U#0P(X zKgxLTZo4eCJo3<)JGW=fJ#+8bKYxGy2C$2bJW>KjXT~1(b^(Xtr|dgyD#0I z{jPJW93!ycgn#dj@9cL4DhF4%eb=a-^0x&F5%N$Dlt4KFIWoR>dxR*BxJZ)wbs&&? zsav!Hbv+}E_FQWD3O)Oa z^o_L7XaqVi%5{|en!M5NvKO|oO3q2s2be~REk#j^4HfQWC)#HSQNSI_7wOceJwto7 z^%JGm#wD!Q^GlRlo0q754{z0JtrHheBIF#Ia-LQT6)fN`Wn0mG+`}TBh$*Zq!iX!U z!ia09ai8K+IwZ?eI7>ek^I#kfCJgI4Qs-`D+I!#k~Qqvk0TfadQ&m-;=Pe5R8s;26srsh;l j>(ezQoDIcDNvO!r6}}6S(Qr+3Bj0<)Q_ata64VbWl6hBb{B~LDosQa zFZ==iDC0Z3Whu>a*^AEUOwW1ene)-V|9<}gu!rqDG6L*YF|0J;(h7aYwdpb4*CMa`r6-Vk?YK_3D==2AwFR>K-mxlR3`GM5rUho2j;r3E z_d3cyjA=?u&ywA?Bq8bghuQGN2?UOsqsbY;kE)~4-l??G&0N%hNmRMO^ zg>eVms^UVmHc+JNI!;1h#Xuetn(dK+$5>;|B&E1~IEj>tBV@AgK8RweOsplU-M&~iIOcd<|f9Fte zy~o`)t)LyR|H91o$ZeKufnlIqmX4+9TjYc}%yXv&7U-b`B(3&c(E|0xPn55)tnKO* zD%w7|!sd-|nQXueg^LuKrCbTil+~A%W1;>fltc}6Ue@4m(`P2J!sO;(AwRlml82k+gANqA>%al@bM59+%@B@S G@c0{3HFtaf literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicDto.class new file mode 100644 index 0000000000000000000000000000000000000000..ebe19dcf58f4ed69e98096f783fa005b9ad29630 GIT binary patch literal 2892 zcmb`IYf~Fl7{~u7KvKeTkz2)56{MO#%i0!HN?S^y)l_a0#MjGmOt+Klc6Jv!<9ol4 z&KRBXg&)8V<@kSglO@?%1~U30=gHZ9F2D0!&i?(+pML?^#194J6b{~o=2_hdPCUyD zwe32#X9k|_SoOgBF??tF+O*u@o%RE>;n=TMjBZw=emR_J){HYpWmVh^Fy(B_V3TXlJS#2r-& z=R77g6$XMZ(mvR^Iz5fRJoOru<8~xtvz?ZOLU6u zZD1S|3ZpxYtM|?tC)z)X^D`cC9$U;A^VNFTAHH@1g~MwA_EtY49+v)Juj}RGB5vcO z0zOpOy*i^k02}xia|$Ch9VVHUE}ANpJ6fgPM}tuK#6STzgy)WdPw^ScPUF3(y(YV? zS`8*fWdnmKiqVpRySS$?NTaP*-*ua{I#0|@14A&x?7o2qc*yFCSyw~eYRAlIN%662 zU>GCf<4Xfy$zhB1aU6H5C_FMS8XxGffhS;R-Jn9$Zc@tfHEP*Y3royr2FCCn=OnrX z6!xzXq=%}q2gi1G_}V)?uzXe~~Jw(kiHE3{sbh>Ia+wzuCDlsV1pj8+aftywls@IvPo@x+9tFrxU{nC*;bhv%}F3au7Td(4} z+TW^ML7;;IstS8o&s{GF1$?it@gIG%rpeSsxxyTqtB=0{<)CKQ9~U3 zOewmn;$U&MkNW^oe> z41bqlR~T48rsH5O5QgO^`ZTj%Ir;>%CJ%3C9&H;h zs3pcvDdlMS#U-BUf;CRg9J4>)!IV#!@=-++rUG3kxkI_8nM!G=?glQ;Z;{Bh&w3238&18Co zZ?l+I(oAb)dUOesOijWx95L~GlEJi=W_pszw3FRWYiXuUGCjY9NhUa98jYCv3Cm#G vNHe|2WZK1ER-QWMG4AmSnO!mRQdC4QhV(-QU#qh%NrtplSujA5K1OIznvLr4E? zZmP{~KA*z{OpvhbW}8&0!Uak#!}u}2ku~8tmDwMgG$YGArmQ5V8D6$#KX#+Vv6!p5 zatf4{dDlsiEN?ONw^$#miJ`-l69t0dRkA{`+M;4y5$3(xTIZUgYp9wThFes=q*ZNk zUq*(a%2+a_q%Cg?haD{*Jb8Qf%;ZYla|mF~FeoDLc_5t_w)U(~IVI&h0& z=ykXgs=aXXB+HZLL!GX*8@OX#yvs0niiWH(&WbqX@lCUqaM+1q!*G9Rh1cVov%4kd zPHdkOk6yB8uh-+VtaN|v%cZl0R=x>`lP`4u*A-6|~eB6-_HLXv`$8Zn#iQ$2>VtL2i z&S=&0KE$J#_gT!FJwm@FeEj#U&A3gpz-Le3vv}g*?THBW33VwHUHcTzsJF(?fj=<- BmgoQg literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicRepository.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/aggregate/TopicRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..f0f47638c3860fa0ad40bb37ac7146ddc66d1429 GIT binary patch literal 1375 zcmb_c-A@xi5TE0#P*4y-!Os;QKux+&zR*Ha5ll@jN^6ZzWP3NgF5KN-cK2HPNBL}` ziN5=v_)i$;u9X(U0}nRYZfABnAHUzs{QUj(I{=hnGY1n6e2gtm0%=-GaEopvUB!)Z zr3j2_+n&(KMPz!Y4G*QOl|kK=E*frCEL|>qU!yN9@<(W*m5-w9iefW#qTD2(hR18TW)0{4^F^U%23JVli^)z--A4WF+mL1C!fN zn+{CvD-Uxp@4(_H(1F%U&%zA{etn2E8WnWWW3CEg83-#?#A2;l0fsv)KiB9W^Uw{b zOpnFFJrh2%4KgnH7@?-Ryuzij3qvse(Pm&2F2LAeo zVF-O;2z}u)^w*4*&qE#-7hnl)IWSWiVzjzl9idXUDcr>rwZ*E8u$F`^)D1yEIIvt* zt_Ye!%TyjJ*V;1jGEytv%YKb`I#jwYeBr?MRh|dI5-S6K6sI_gpyfh80P03rd9p$|f=`dKDGzao^m1q|aWlGOVmdt1p3Ms7)d>(^oFkQuK zgK%lGO4a|m1BzpLjnnu1%IpjkjLP<7}E_GV45fenR>UfIlA0vSR=M literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/TopicEventHandling.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/TopicEventHandling.class new file mode 100644 index 0000000000000000000000000000000000000000..37930ba28be309e7c4d7f077ebc3b4f68bbc55a8 GIT binary patch literal 5145 zcmcgwOLyBu6u#p;?6_%@CiKNq4HS|B7KQL=od!bc5Ws0dljfZ4kfn(|sjSf%jq~^e zoWqI@TQ+RjaM<)5;Ltrk3b-0cww$PzWgoJTH8Y<1zB~83bMM%H|MUA_0I&jEdB_p? z)|Gn8W?qdOx}>JVOs;#}WQOhWeR*UEsvC}XM1`j}nJKtO#UV4Pr%UcKQ>Ta2k)GZ# z9Lr`-UElFXEB@emIFyG`0@DY^p`qIb&e*ISP*V~Zw|Tuz1%a6=7j@mU_ZJWJsv0X3 zm{joF0U3dv>TqU;RtM&VsK_#e#O(};`FzV4b_tBGa*O6+3Vqg*vLXZ*1-MK`{(bBS>T&y!TAIn5F0&2ExTCp( z*KFGK?rFt|od(q`YT6i5mgXAf6QfSGIhcPD zbGX!+hBOTv-ZZycEWGsXNB|pejW%J{N3-HC-;GzuS)enrS$`Ej6k| z?!d|ZkysVf-=I1f39~fGHA9mpE-g-|Ia`9`A@lG^RgO*SDrcSrk1xwgqvk($t@AJ| z1C2840k!Elcq;?VK;F$DdR8?CnxA#Q{m(DLC77Lr3-AJgEA!P(irAI{W4P?6kf|zj z=)+dCM#YYSSwUrf)3A39!Tf$WI4T>=BTySModx-!S7E}8JiS3>gIimMFq#-}0(md0 zV@^Zq2wa`7p3z=y=f}?r z@4rRho8I16B`lls+tC!}zfLs86Cy@a0p8A_ethx&QcJp3ZyO(}9_KGF^sjoP^g!zq zlrF$~{i_xy?d`O-YGL~8s20zMO0~rds>K)gHekRTK(}gb<0IAL{DY`A(E3Ex?)I-* zoV2&o2+ZPT)4wytt1f}-;b=U8snpw95D$%03%b9G7~)o)TG0K~Y^<2zbvWehDv-g? zAr(!l1a7A0AOe>I(7?24E5Vpz?+_KYc}tjdjrqm)nkr*z{(AV+tmM~e0=E;Z6&5Bz zxql^9DV#f)%*%QB3Jo|@5c5!thT{&%!^3E}+mVwSOx=*$C-f7Rs5V(?IK)* z>)5-2&qNM^@r4gTvX>C%Wo-M4Ucq++p2Kzk{%QjFt>=J`5zufUiU@Ns3a?{32X82X zBDQW}Kbhps1d_XHB=fM4YW?j5;7S_sJMeDX`cfa(>j@-#X(V^x{Z#8eNC2j3z#qa# zZR4ah@T9SnPE)A@l0qln~uqBL&B+HUS`Y{dceg?3g(!iEc z@g&QV1o$}(?9&Wjzw`#n!DskiSdwL*CxpgtXw9Or~N{?juELxsg*RTGg67d znLU3fET%%cL$ogKDQS&Ed!Qt`)SFVt6c12)mv)8D3Z?T(2*&Fcg~#l9_yKJlCKz=8 zk*l2$$wmld;Elo5W2KdU!r;Sd`|rGpJhzxrDrt1Hg{5)I8++V31F$s4W=adMjIK)d z${H%73nd$dKUB8Xb{R~pn+)sFU@-eu9Eh|KWW8anvD0v#)fQiCi_R1Wm~m-(r79vm zFRkem*mI9~v$4}fo}nzLjTtY6+!s0W6|zop)M!J8l99G1gJ%Dtjm=J~X0PzfpyS$j z-VXKBPTNgiE1AP#xqa zb*YT~afy7CkG$7+o)Zccig*)KP`Zm&g&ai3c`GF4hrVkv{xi1*kwn$%3+*vSd%v<+ zBX@y{16R>xOi9ly$~&8d1Tpe z!Wb+@%`cT;aA`C4<(064Iz+5sa3L}U2c>6ld9{727cu#3TYc?S4|kPQlE4>Mn)%`ELxWC^q`(LNL9Cd|P+ot9`dYOp|`MOxHu wxI!>jNe-8;(f0(rMsfvyeGK^WZ@?!R+@y==;1=yQxcvkA4xLwMO^_S@9S7%J&j0`b literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/DeletedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/handling/handlers/DeletedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..21a5846b2f3ab67e4e3ce45c2d1a9a3cf1b70131 GIT binary patch literal 1867 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 BPjmnP literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicDeletedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicDeletedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..df4031b26e62b567b1f5447b3f9da3cca580bb17 GIT binary patch literal 2340 zcmcIkZEqYk5FUr*?s83=a824Kh2n&_1e!I_LLn(sQPR>@mx9RkB)$N?UFXi}uD!B- zNy;BX0tvqJqYz`?Ub|NoXoOhFyB>S?nYU-=_dkCA6##bN^Eyls_%3&Bm`dBzoH-FI z8Ea;BEO~16p*!J5Fs|&0FqUO9HrfhvEMsAr)47b9I2OuTmJfS~F<>2kxF=HKL~_?( z)nS^zb#400=0>Xiq2ZZ0(dG$@wQ}Orv7J0mWz3z_s`#TuVDX3_b0#%A5C&b5Kk{BG z2rQs=6uamRsy}|PM}RyeaDG=S>$q}Vo({zfM&}?`1Asvj`#aU z^wGEvVWAX4TZaIq6=st_E!IP0aZ`@+tKN}_y_LDxh~S}*0+nw|CEXVUdg~>?Lc4Sl zOqApzYo(Kke=3r-(Iqgws}oU&Hwer>FTh=6v}wR9A>Th%Mp&I5i-cnCa+wBCX>K$o zH?v!`IolZsnus_>Dke0?l=8lyYp5Odg%SpXN}>sBMg?JCI1Pl2LdoqKO|-BS%R;df z@qjusB2IJ6SlLMKqM|pVqBo++-gpU9QPCS~qex_yYvX*d&)uhSU5U(QLvscykV}!dnEE%JnX)=}R==(sw!b8d@q2r`#7^%9jFWf*UUvob+uu;%_69Wum z!gs{t`bLS4MMw>Z2(11yuaH*Q7h|Z=0Py0o)DgHk#;zcC18xxL|Er0>-2Yrc0vFJ; zo$@4TjeQH_te6FL)Or-g!7T1}i-|CqqGvN<-m6TEg@)6k1Y(X4LjLbbKXNbqWu!;G zkzo}Gyn<^68n6O&eB%tosXGNtXu%wu$9)Sf6yJGR!24J6>>PeuxGq{)#LW_13W}Et z#VfEJllA8u75 z+=P!S5vrotu0;6c3{l*vM7RZ?zO)a!l?b2xO&+SE__7jV>kLt}D-pJl0aI}MPvVPD AaR2}S literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicUpdatedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/publish/TopicUpdatedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..a616815bafe8bbab0309a39dc04771ae81f61e60 GIT binary patch literal 2340 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 AK>z>% literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/UpdatedSubscription.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/events/subscribe/UpdatedSubscription.class new file mode 100644 index 0000000000000000000000000000000000000000..563146906c7e34157ff52922cad9e25a78d3d85e GIT binary patch literal 2912 zcmds3TW=gS6h7W0yPIXxq=A;U+{|)I3yfRD10tx>5+zcl4ULj*5KqYC@$R~vu}8Ky z+x!*8|3Cr>-uY38M6r)V~H#jQk+lt$B)rph~NW}1#vDm#lMrE2i0H! zX%Ft6Mz+^ zF;LEj&V;tq-*I9h6KpIv1d6gRG_bxq)MC92?-Ka=JZ*NdJFl4J0#Ut7YEx3H^U(9Zw)thf82Puz5QTF z;0g{FoZ7CkvkjjRn7yK4W3lAM`JGc1;o&K+V*6HtKxfZPt&m&T(viK@UqvjB{D?wz z61YXlz$>Ph$jK0GJ>-RI*2 ze1!OQk@&dA*!wR6Te|?*gBO9_z5v+n^TFoe4hFLUpB7mP1o#a1_wZ@r>F2=y25;M5 AVE_OC literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/service/TopicService.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/topic/service/TopicService.class new file mode 100644 index 0000000000000000000000000000000000000000..501d877ce9bdf0e1819e037396303f8608bcc303 GIT binary patch literal 8504 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 I7n3mK|4f-Z`v3p{ literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/Tournament.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/Tournament.class new file mode 100644 index 0000000000000000000000000000000000000000..d93c61c0f239a0f23aa57a5f05fab151f9f7bfcb GIT binary patch literal 6153 zcmchb-*el>5y$sr{UB-95-r&=ZN)(x+o9;#jNANBqSz@T(T!SJk}XQ9(>N{!9wj&= zzyKiqqv=dv^4PaLv@?C{5X^t%TT1Yl5>-KG!J9dLVlpWWNr zTj(GE{_|gns7k+_qA@`qb-YTqWxBhzUh$-1nTB0)ZNt=CuHEz==#H%Dmis_DZl!G+ zj_pdPXByJ2cy`yZ^tQCTioUn!$UWVYm3sJcipB-K|8(VTx6-iNx@kqUR0sM5WnE8q zyt>(zf{JVR^&TkkP+7ALy|scRd1wh^x4RY(?3k{(+mcnwvOV21ZOavO3iCdL7QNDe z8K&z=%aE1T2Bu9(tKlmzSl#xnbT*o|y3!3M&iV_sbZf7&YI$-`I+&9+bj)wHWJ6F? z;se`mN!@}(Vd$G>N9vyK2s$3owXu6&8Xg2s4h1&>Xc}fmw>%&hhXc!SsVi#_rO{<9 zK{La#y4^7ih#woiy45uw3z~e-v`p{1pnhqj)1ERAvpNDlFf;Nhu;+jEW?@Ir__E!Q zIXXh8@-#<9K}SjpJ6RHRCQlPI$-Nizl=XWrFXnM`G*6!DY6@-b;%_^9bF56^2{>mZbUV^;1nDCh5OBrZ+f#2MKM8HgLdH@>cs(a~#{r&@Cap+O{0&+O3{!XolVHm@SpG zwT@%s%(dNjwOp37{D->LXh}yy>P2GLcBM9$mdkEBHmB3tLqm2rL#7C6o~`Mc_oyRt z+2!i``bPa;b!Fw=X7zS`b$NBOx?UeSsTZ98=Ur!ebER6Z-K%eG-(IiYtQ~5l`o7d~ z^pONKII`r2$bx3NTGwjGCK6RcYub*6Q>%>9e2H8(GBLI*rC}5vLQRg=X^YctT~I7m zw>-zR_LkJK>I}CRdkd!Mh6{Q%8smezGe{>Y27hqu2S))1LDVu2cWS$I2txqY zMZFT16RWtf3yy5tJsFA41nl5IMsBk~2FTE-g8p?d>~|Ql|79roh95qfQ0eftsDuE0cg6&7AMOW$LXdn(Y7e$j#lpz#Ze?Qn1wad|%G#|^uMC5ZOtCiYX?HQ8@u3V&m9WHon zD?^_P`uf>SLaOIv9%wj}aZ!iFX9pSJP-k|k=R+g;fT7mI7sgh1*EJkdrFWD=g~4@; zinvwTlHO8=dV;<@*uX*f@im|g6Qb7cx}IjXJGgJKluD|mg?h;JkY@E=*K>5k8|35g z09;9BD=p-3RA7MFe6(t*y94!nh8_v})&G?)iJ-u8TPR%&dBf!Lc6#W7D|~1K%_HSK z`JGh5ca`^OiC#u2eFPtlSsLRC98Xht;x>bJjB9b#q7qjv>TuPf3Rf*^aMhv$S1syq z)uQ@Fdlq`;=mhkf#M3{~=Fl#dzoi$-m-=+N{92#p%a{9fwp<+VlU6QH^yyr=IN7K3 z<>FMIE|!a#K9$PFY@f=1P?}%E-z$pd1sbPQAS~jW;56#}Gw99JOY|aLz{;1mge_otI%CBCc=C0$ zj5MkCD#>&iCSHj!O@>U9foIthU}Dz^Y(pB;e3I#ED$`;*(|nTYO)U1-2qxa+&`-QG zg5IHb@i*zGvq`3RQkj8kB-3gt(=x53F`Y{?-2~J62qr#(Arqe@LA6w-^GT*# zsZ2N0na(Gfw!ySBf{AlM$TXvvew4~|G0Ai%mFdHDri)3YyI}fs1QTbakg1@UR#TZu zNhUp&>Bs3zr6iLCQ*#6p=f{wVGoPTJq%xJ0OlB(6S~^qYcJOm%8%)*+CeG9$lb?xi zhHBry1;A-9_ZM2fbA0n#y8SmiMHA=e5wKj4_Fjh!lHIm4aECq$Vm_|yrzy(57_IDM zrR-<)Nvg8DDayVat;{F8_Y7pe9icBBjv)vT3uSP3tC}k42x1adlhPiw{Wy|A# zq9)3MWVrnX7w&H(`m*8W)e4oc<5`Kni|rJm-$gVGc2STWruF#Nby76^K1D+i2M%s~ pK`wcLP3K}b-9VX$+{Q;C{~*MV%Rfw$hnF71z%lwoShIaW^dGW-ZDRlc literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecution.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecution.class new file mode 100644 index 0000000000000000000000000000000000000000..0a13f22f44876e3412baece89a3ea748cb46fe16 GIT binary patch literal 2975 zcmchZTXWk~5Xbj8z7V6dCW&3!P12;0Vk;L>pg>8JVnRBTnY2Jt{I1BSj#@|RNXIR_ z@`+#u4>JtI2jD|7{8zGNS(5wM2kY#)?Cx*(oZa!i|NZq35q0RKL^(mvUnyfau>A|y zG?cU)+j0%xwQO_XyU*1d)02kj_-~}=8&|gFxxVyX+m`eVK1qcp)t%C};Puem31WbDjQypr$pe-qN*6gnWP-}m zRLq(Tg9KXff$iApNYJmXn8zU0*nASo+n2_iu|w&V3`=8{c2`jT*zL(OEzqh)6{-qa zXtleGgbZp_pdw2RjY{+$OE)!I45f9A%Je?3-O@-4r7ew?!rc!wx<=OpT|cuO`DA!? zA-!kj#XxdKT+1AEP0!}}crmXoZRGUw?A<&++(G5BRF`h=2h+o&Rk%ArFI(@*J*U0k zs!Qo%>`TS3a=F!xUE=uN7yNK@CG}9)#5z>NXB`#ROkq@KOio?_^kDUEdbPN>i5fd> zrX7ZMIDlr_>U6V_){M$-bp|skyhhqyq*9F&jx`vto^~^-*LvFAWGe$Ms^$!eXm1JL zrY%m;Y#>p&FTr#C*bIEE5X`-=s_DBUs_T0rYU+*rh}QH*VMH5xqd1~X zy-^y`?cW1yAK`lkO@&}94_k}0g4VH$ZISI0lYymzxyqvcz!|0*NExn0L8JA?_CB^-{1|g2*ss{51TQ@VkZ|2iqP8dx#mk zDR8jUD9XX&O?X=mNNeGth_paDm_^{NATVL8JAgp(LpCsS7zOAXbC~!=MNIrc1l_~obe`6d zO!qUHzND|R^0by@It0_v943C-5fi^yLH9G6Hj+%8Os221nKqJ4Ct!Lshl#5!V&V!C zbdY&>n@OhAOr{6fcej~jdJLu~bC|f^BPOnBL5E0c`tEKgnSRV cFm>lJap#GcxWnY|C!D|E(lJIZ`|pVU2dOSbDgXcg literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecutionDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCourseExecutionDto.class new file mode 100644 index 0000000000000000000000000000000000000000..efab460d90aed29ed8210032fbf5f261edf5c839 GIT binary patch literal 2750 zcmchYVN)Ab6o%g$ASrR76$qu+Dparu!7jC+tsoYmwJ6%o(Bb>C+=gw#uCsg7s^9%T zI%9Ok5B>mul;eALlTEVO_>swEPj>g*_dMsGb5H*H_piSJ?BUxI3JTs2#%?ve=rnL_ zqg~%~13L;_&uK=%OY_zVwe9%PTOCIBh3AGrq{BC!t0UV4t&7`eQ!V969NAx2FDcx1?WBzNaI1uy3NJ4wMGNoa1BF`+ zZIX6LyLO=AD%DzFr*vCx5(>)}O1L82A6i%ut;J!jZYRouk1R|k3#t}sSW~z$ydZ7R zxq-l)78RLw3uU|`aqn8NMP^}GCh1&pzh}Wp-0xf16wI8corV_wPc2L(ln*RC#ODfA zl|6lF- zYEH4`+%66;?3R}K4!=yxSm{^nR8a&MhSzkbO^#eTaHhDdzZL`Xol=N=`Itl0)lWQ` zsLQ<-nO5h3zAFP2%Pij*zY{2;Abrh0i-NRkY}s$IWh2Fw4H8>cn|7M(ILmmQ;oW^& zMLV(fCuV<(-7376DF?cb({-M|ti%b|vA{bKxIqsQ_=Q${w?tsA{s$J@SQ2}?ja9LC z+Ng{DxQz|5pS0;q`&k>?y@0o4#u)~jWw1GZD!9qeD-5~LpqmURAoEGE4hX~I3+xi; zOTL>>yb(H%3SNcIrcKiO9ZSFRL4ms9uE|5uA5QHN|8|qqc4;NG;#!?K^;nb5S>o<5 z_c0Yyrea)?l&M4sB_oH|EYoV1X(gBGt9+)_EK`+CwGm9x9Vydf%*3gd!&J{Qt>-fB z;~;OJ>RF}@GTj@&B%PWvm18E3$Q-7PEYoH#)8l-mjV#jxGCdr@B&Q~2vSKFA;T)!| zEYnUd)06z#ZDpArk!f!Plbqm`X)0#otj}TE&N3b3GJTDwd9~|1k4cX|CDY*uCYdHF N({#*q$ZG-L{0FiKOX>gs literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCreator.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCreator.class new file mode 100644 index 0000000000000000000000000000000000000000..595fb026baf349ddc614aca2a64bfbb676218d04 GIT binary patch literal 4221 zcmcJReRI=B5XK)laSYCdKp+7FAp}TCoDj7@OG}_Y!%I^_pd^@5C?MOy5!e!y6zKbB zYG)cV({|eb`=L60R#GIzqm-nyNmZw}zchjTeUAH0*|cv-;Gm`5JOoo#XS#hk3_t%xD;>h4U`0shp$S z?v&h|?VAnLrD$&?xaVV^=CJ<{8+P{20n9aPwkqVx_a^oG5| z3M%}1M4=BmbDG+$>lSpG}OlbZ?h8SyyGvMu7<^bZ*7HpB9*|hMK1f!%tWP#X}n#4 zu3_QzDU%(*Y~2d);3T(T!}Q}#BHMd#d?J-hYx!E_!k<=n7w=KRGyU0&!Z4KHFKZJ+ z58hY!0B1E6Xf3P|d)AqWwvJdir*IzJeLE5>;f%qw`e;I^{i0$4rS@KGm&i$R5mmDa zmqpb;qAK}3jjIaRBm_lHiz<-Jnp5~h+}xGSitAHBV#$TsTxr_Maxj1MAO zO>LqrS&;zs(VgTt)UZDN;@aOwoV3`woJ$qfZPgX4Sq~Cnb79$1D#)H6lvFenKE+9S zttr$cDP>orwyOv|QCJFvmK7`!DpDww(9aZZ|y1ioiR?Dndp2gaK$+jIH;XKn>gb%ZxJTqM1 z(4WgW@A;-(GxaMxIO=sdD46pV;_(FbkF;PF7C3I!sK8gAlN z~ZC(CU-Zlfx#*+xU!r)@N)b=vrHG64_E(nBu8 zBbs>3B5triRapRQDU@WKC|Ey+=1I#54n4~Z zwe32#X9k|_SgpYOA$(=|+O*u@mG%Sk*s*;t(Edxu)`1y%C%$VPYd18lX4BVAE7azG zdRX_h6?*^QF8Jvlzq{(h3C9nZE}2NqMuv?Ygq;d3WYurCn8CF@FNyY`0Ntj--&a1|ev z@xH=$=U3%2AsM)a>k8GT4(kQ|SFO!;bk}JI4nX0Cfim6_&KUzA;bS&fc1PTp6K9N* z))d>CfdN#+cHY2(*p6kkryVPbpBNa76-@)T1oTR#m^8BJeQLmnVU`Wt!DnooOfTv0 zXQVklm#@{~;`*+EAq-0_pBq@kn!*rWXDfm8ekKh`>x=2Sff0-{rff`-kyQBp0<4#* zJNtO_i4LE8M|+mf{Sb01unmIZHkhmxbb$=)aApwKyxOUmlw1_YGR4yZGw|#%-AW_b zSyH&hMdt0b>Lx%#^wU|TGvG;)M6c4|cDMCIM|Sj7W|2$_SN%;{(Ojq7uB-ie%L)P= zl(DC<`~S9E8P60RT=Gtq?ChGxG&`}E|NSVTM`oJu$sU={NOJrl$?=LLdp(kDT*_hE zaZmF(&TqFV6=nbYADH+xQk&$n!t20J@1x1v{A9#Rcn4GbCKtR*4Y}ZFO3}R|7tAmG zj`!M_7WrWtvm)o(SQL4)jU|z{+gK5KuZ{be!y6In7#)t&*#!Tp9M4J2(ETFaF4M7q zOvcU<5W2+!Y!GOZzx6ZK6iU`3pF(F+#^L>j>0fxz!yCY?$>G()(7r&OT4KCTDWR3- z7Z_8<8YkyEvp?OzR7#mjQAJXwGOtjwZu!hJ&E}aT-v6e13ttv7&E}bEWSZ;3B;AoR z4Ma>_;{{BMd8WleriaB$i+QG7WLoONB%PWv4Mt3SV-zqg<(ZZXnI09_ZYj@nmrVD% zFv--UOh&}Sw^ISrN}g%0km)hDi)y!$XWAgsW)~)z;FM`7V&ZG7fa!joX{(UwD|}tV l)G?27k3S~Ub{8huCMnYhnR>7jJ)dBgQVRPN-=MKE^)En_pH~0? literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCustomRepository.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentCustomRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..5577fc2d4a84568b3fd4fcc37a531d5a04753611 GIT binary patch literal 421 zcma)&!Ab)$5QZnKZM74zWjs>vJ|6I#aXIHuy~4CGb7 Q77S(f1Y!LNJD=k22YE(=!~g&Q literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentDto.class new file mode 100644 index 0000000000000000000000000000000000000000..e61968b16180d888e48ecc952dd5b135ac0b0bc1 GIT binary patch literal 4578 zcmcJRZCBh>5XUDJ*g|g~cvM=dwzd|i5-r-+QlS*u3Q}mx3WAjy!et3A2_{*ns8y@B zei&cqIc`127k&U=`H4LK=Vmv_#=EBv>Wj^6ZsyMY&D@!}_m97R`<;l!=!YCN7__t% znx&!>taz3gO51g8&kQ`$(g^WlPJ6ahoB#xWf?iI#tC3clTiRTa{_NV# zk_`Y^6QaE9iOZG3em5ZFvEf_(mSY5Cm2tvrq+$io>!EXJKRd6G?t@F zsHmiPD<1`2rg8YR66wxc#S*d{nYhI3T@kdO4lw&QLGRLJgqDvRN?ewm)gYvr$}n4a zPtZX+#1^g#nqdn)a4xZsu2Ud-{$+|diyVCrX^MsA` zfFV&&Cn*xL=36Eqv3||x>e13xGa8*P>~$rnkU)5;B+%TG^w_7$9~I*{l&vkVI((?N zC(hwgFvx8I5uS8i>5mt!Ado?hzB6d%zs=(uJuqnWU)n?##;scgjEt4^f;4vGHb# zsX1n9R>=@E(_ohAjFxGOKGiV|W|_`{>0BKqPRy98MKR%8pkW%yGF{Lz z`Ff_IEYk>>M(Z$fk;F{<6ccVD8m9AErpsEUK)-kAvrJdPbgd2(7i`QV6w^f-*Dy`| zh>z)fn$j|bRI2RV7$oOY3zr3#Qx8|a5K_iO=UxMON+Hb zcPm&mX+D)@nn$P>QcUSI=Ux_v>Y!r6T}#6>n`K(kGJU3Jn$0pTgXvBkChn^-Q=4MK zeNMxa&oZrOnRfL|`7F~Km$+);yXdi)D5Nv`dU4|q5F{BQ@^F} J>EWfGzX8C>j+X!c literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentFactory.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..08bef42f877ec018c25d82913168fe65ec31ad40 GIT binary patch literal 2170 zcmcIlTTc`*6#mM^Wd{@lK~a5Ip^zl{`~#%6Tl)KmC(iTv1x-=ER$``11mx$LmebK zlsr!Kjy>c?1Y9MD!X!aMhDImC9LP{4fz>Uecta=~@F+4O;#LIfXN$``wAvh((8Dma z#}9ZAa}@=zxA#P78O}YGO4{cP-Idu*hMpI?EU%{XNx^{S0)UfYsGp|B_Bc~$iv)0hCh{B?%0a5A`+%r)JXLGC#&J+8Na~Yov;*jw1bMEL*8F zoX_b;0F<89^a5|VAxc_eE5pS98Cy2Gv2-L8OOOoTD}_a~$UI$VcvleFJxpNIWx;3o zP+)X6OPFSu&e^s?bcuMpXrh+Wlcl3jG#$zMaEoDVL+vYlsQk11rwsBig;7!kktH-I z6vI|wnI+<_Ssw1M@@A$$Bmc83rDMCm@D=5pQ@Q3rtS)T3|Db21N=mAU9@&}{x>GR zVBiE3Tk~z)_K$(%s2zx=@l=erf<+iCP_r6QbS{>r!zx@gWI@67Aol}HUww8 zm(i-TJBzu@?z7Bp`2_RM;JvT3q0^6<4*2pJd>IQVe1^J^9tzU-J|0l-!uSyQ2jU@{ A$N&HU literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipant.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipant.class new file mode 100644 index 0000000000000000000000000000000000000000..5e903e64cf00dd2eb4277a212b347e468a3e7ee8 GIT binary patch literal 3993 zcmchaZBN@)6vxjEBoOBE5JK7jrIY~+rp0T!b*!|kl)^@vQU)~JT4|D#m|z-gWSjPD zZPKDid$A9&&#_ng5YztWI&o~*(K;1P$i5Fg_uSukxaWpH{{HQEBHEx=1C$a}J@d5l zrs-B~UGt2ZW!7xXwQHu{bnPSWt?n3_Zn>WNYF1f|S6=F8m=W!q}32`Zd* z*KTxF??84uB)emIM#FF*J`xh&1(2XzNbbOMOsRD|B=!PUFuR~3!*c9q^U&l$(O>6b zpq1^K-h2$3_!D*+3oYM}4vFsL8wJ5!NU#F8nYJY;?RropXs`V5?ml(3x_zpfR>-e` z8|H)F0c*s5L)}0K%G@_C(_0txS1IHJ>DSP>ff~M#t`+<6@~It6x3H*s$@yfltO9BaPO)@12o9JYYJs)hP0cNV&3U8N27FRl_;dt4*15oG%sKF?rmc?DvjM zS5W;ug8rdr42dxM%^sHxW_S$u6vc#$Ia*;axgQc%xi&Bfnls$f z(6&%%>UFLxIOocaWf{(9Q+Hj%9iZ<7?Ov=m2j~YuUwxoXLBqibmT{r$D4>1#<&mWn zS7r24T$5#siY!~yW7(n_%NDg*wy4CiMIDwcsxaC-H0EgndI}i*iI!Kds=v}@b-6{8 z>Wvmnt2bLTqZa#HG^-ZVEt*q{nHJ5f#eo(rs>Q(;mDFOkMe5H|^9{ULa4)d-e(26Z z_b^>X-M>Ob{9U6-TA(RfrfIrGSLq9yp_`CC^7qDC4I@xU8-?t#l_}X{JJ|=TeDgp* z_t@67?6I}%msP%b;2Z9-#cca3-#noC6#a#)T)+ZZJPfPg8OAe;XAI9c9(K;HwsRg} z#0Y72PS?^QIXMfGIcY_~7YeLM({0!zqMs6IMaF})5Bz@**Mc9Fm3i=;=B)-ICBO56?uerJa%(&rtL(gO?sRZySX^i6EHpP z!NeypVB(V`Xp6QJnC9b5yNOI+(>F;>^KqtqFdg(@;#v?e@yQmnlbGX+ai&Tl)05;J zUyL)o1kr&fR5Xb+An}H}?3^<{=w6r7zlc1&vX;TP+a%pLRkQm&iZPE&(I?5P{kwh}n zcj-H{Gj%)D4}E|>RHy%yY+2IC0~s>?V6An$XZN>zcK7(7fB*U$z%tAnIuy(!Uq7x{ z-o9h#zFD!Yilci@#WHH1^OOJDa82E?z1ODe>4#Rubv)C3WmQa1_nl+cHV#eO*Ntk` zHLHei>bue6j^X-N#X2%DBt# z*sqxig`<{%<);0O1BH>zmYEIPH>;*QuP_)>-zJqpKBialUCXXgZ6v0)$512yg?`g^ zomy?zl9%ytpU)S4)2SG>RR$(MarD&}K~W#-OuY=v&mrzwS<&69I} z=;;T}p<&svLzu5z#{E>!pq4LKXZsU%^c`C2PTH{jFPeyZyg}WLwoELYWYPg5b*xX5X z&7MgDH@ZeFENB?OpjcSc@IVqbmbB1lCedBiFcc z=(R7x!c;~L%J|49H-!~97T0bRoG2HP%FXg(n)pM!lrA?6WK@#a8BN-0)=k1~qijTd zEU6w%++MCX3ZitH;qpfHAdV%h*6DK&mv{kI%ymnyuq&-!Vy(~Ha8_rouq-y1@jCypx}9^rAxs6#q_i$e;-%XGkD92?EJ}4kUXu zknEp8vPUTMw8Kk@&jR0WQ!2{t;-9$iTc9@1XFtu0Y8M;p9e&e7C0xQ~zKMY=)DQ!| zP>SxF7%0yCfva^)io9M&QRIy}bdk5}m=pO)9rGgZ*Rdq>^E%c=ep$!E7CWF!8TM2Ghed)6-0*@3YS?HowAI@Qh5)+c3#Aj+jQs)PZNg^ErN? Nlm_?_`#4w{{|}^|7-#?h literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswer.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswer.class new file mode 100644 index 0000000000000000000000000000000000000000..6dd9404d65d316ec8fdda57a5611650e55c5feee GIT binary patch literal 2925 zcmchZZFAE`5Xbi%KM*6p*s-xg5)w+N?L4Tor7aB!HEEd1On^eE-z)Or9Cf51DKOL5 z{Xp$ZJ)P-v`T_c(I{oj-mSsieB`>U7op$$rySKaJfB*aIA0q0|L4h)Y24{hJK2ZL# zYng$x9c81ZDX$_gsIvEEtcz;gQL^W!(t4g_UX5Bo=x4@a)k-w{-O z8<}>-XBq{N0@;@yw69OJyWmx>Bj{2HFB3+OU3VZY2Sz35{Pb9Q&tHdxtaxp_ zweNbK=BrMEjm0IX9ODRMfyewq<)~m!(67zdXB|=OeT@C#tug0^wNiYIXj)xC*?qSs zi!?_I29>BPXs+4n&Xb@U2IVNvQp2DEU1I5`LGz)sY*3Ld^WH6kjBszwpev#Di9uKC z8gg}{9QjPA@RfBuken0Owgz3xQ~W;K%mydQ7vvm$%xdG?eJX#*XF7<;ySqPYI=q#94|VVkEMTJGh!6R1 zbR5SGtN=3$CAx~@_!;xlGS7I9`GIt7X&zus^?KY?JG6$joWm5y2W*$t+pG5A>IC6d%J_@UCa!EuPl7cQw5V@0naKms7ANw2CY+_Ht@C%t(W0Bu=x~k zI8pNpEpY@HEpZGS-zLu>_+^RX$Z3fq;g~mh1|jYyuHbhSKMuAv z3U(K7d`ymmokUU8n>TUVvL>yCN)c&}+F%y?w4A^Td))v8`Wdo`mBYxj|Df7$5HiHM z$?MHE?a>GoMji!i(KfW$cn>Qh<#q0pOzQ~kZj31(G37_u;|zd_(<4Cg8Jy?A%GlT0sCnI6%1X?GW! gXyGjQ2~6D?OgwoaCY~@E{H5mSW7@~cZT~&d|8K@Gx&QzG literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswerDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentParticipantQuizAnswerDto.class new file mode 100644 index 0000000000000000000000000000000000000000..3017e44754832f95321731a27005697540903498 GIT binary patch literal 2780 zcmchY-*VGd5XQgbBsMl8fMch$rAcMbKADn?YXb~cb2D3%jvz-Ue7$X+n(Fg-dnq^d#3O9J;yrMj&E9>j;A}8 zugw?H;Zw`=?Y4blIsUW0{k|U9*!SHWW)&{IvEEu{*K#`MVe5@<`#EG4O2Mh^n$NXI z|Mq*U)zu2+)98MERp;aL%>?xdt9<|FwI>>E;&mxhoi9Z)#U8R#KWmhP^a)C)zpZc^ub!?DnYK zayz-iM&~@`<;mi)P>IuMPYLprqLh=!!^u^o%|1lj#S(n^XKEfLttm3ZO|D zM))U|f9FAlcEQBS;nmdW9^yx8$>m*2i7va&7?xw!Fh9#Iz)FHC8!=^rqC`wNnozQ( z_?%=~on*S6%Je9mX?2pRLZ<2zCTWm}DIYLZv5~^G`5Rv*^K>(n=>R_s^Hd+^DKBM} za!+Dy(Dh~lD<7pQ&u05M#wr9@T$?FaTa&4}lZw?yPu12W(>7CeFTs>7pUh5_DkEUx zf=*$oO)~AIGCfXbs!cN8CsTb2lgx3%bRl5kZ$S!EeUfQEmFWo%hvge<^TbGoZT^T% T2UD12r$kKi0n<}HGkEqtnVwBS literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuiz.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuiz.class new file mode 100644 index 0000000000000000000000000000000000000000..230d2bb3d322ade466a24e6734f32eb8fb698b03 GIT binary patch literal 1929 zcmcJOTTc@~6vxk$wn!fh&bNTI=|NQy;>o*Z?(Rx261id>lR+ctko~Ga z(yk*Y(;*R*iv_gH!9V>_JKAgtdRB@Z2SsD+)BOM63X_(rf|8Hjn#xcgjb~-|I!h@^vo(>We!9ZeWR?cn)^wIKG>E_5(T;l2Jls>>uH0)VhIeh*s7g=sI^0Z} zeeDZ+zjKKfg&vJQS7zU>y^@DOlebLibC=^1Gq z@#FA~a%^Qifx>DvR@4T~Ly3KO{Ygr&L}4XZpur4z1~bHoIRnou4dHwktFM7SQL*xq zMk-4!%2k$Il&`F`s8E?swrJ{m5L?7p4Dt@6#t0-oiuf@aryNaVoWc1$RA#TMbPY3G zOd5mZQ_i9yzRx`8eJbqh1Yr7Axt_YFn@OcWB&1R1k!hcKg{1-c0@4&KDfNa>(w z-AuFiow*oOI$}zPJ>kCw6E{rIExL`dn72MqOdBme*a literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuizDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentQuizDto.class new file mode 100644 index 0000000000000000000000000000000000000000..e56ef85a9a6e6543c42f3fe0c0047b66913dfc47 GIT binary patch literal 1732 zcmcJP+fEZv6o&s@N(;kqDo~Y^78H6=M&%MiLNq2M^%O{NjE3D|TZWVwotZ+yQ)wca zc;N&1P{x1H3~gz~%U*P^&hA?O`eq&a=kKrI0QRt*M@Hb|=g{i3ouKJSD^#}Y*q#-5 zwj-erbQ*LOj0qEy%v*O{^}2J` zRQ_R1QmA{jY&Rt7NS0h?!xJYEII0h~ZUjCm4>QM3rD5O}?&NV>;QbI^#>66)*prqD z^-SF8>_WNH*Q)E@jzeI@L>^NLsF))=Z4YPWMebz=psioq99K8M~1n znFSqMHeq0bs=c|<^Ca+Ls72>4yi(zbcl=iR+?|l)%hCiXr74zEDx(K?D)H`){v6xAc;s#M+GTGs-igTfo1HjIMHISu)x&{?3)* zImM@KT0uKn{fX&sk=ra!gJGasmX3w!TjYc}%=1YLEYL#>NLuZ?q6Mn8A1Gd7S=-es zl(b#H!e&pnNH$=a!ZQ?^rCb5al+{U!u~3qPlBl4{WHtUaD)^u?a~yf<%oc5o{vFF- zd6A(Wkc%#hsn`$kh+aB)msVrvsx|WV_~I8|B;`^cQ?ARDi>lgX%2Pt<3FkS;R2pPj zO=a3jXDSUct&wT{KTNvAU8eDfi3^p&v^mJMk;-J@aax`F`WV-#N~YR>nDn%DnT&|( J3C|3k{sTprP8velP~c-jTS5n&f$3CyA8K}Ru}k!Nt>>UU2tJa|#rj@*zh@|Wf6~A0 z{gY?qj|m^5vS%spO53nMj~ZtD9C8@d+>J3QX-F|UxZ1fxRu)dWY8bb^8PlaHOi9dO q6+0_cYZe@CbE-?bi-SFj1N$amSz&Jrj-dUG9XK}ZSmng<6yOUwgvv4i literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopic.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopic.class new file mode 100644 index 0000000000000000000000000000000000000000..8ef3248bdd606d92f5736b8e719df0414867df54 GIT binary patch literal 3302 zcmcJSZExFD6vzKJ%|qL{P1nbn61uXrBw1TCV1u!(TUWXfNW*ALULhnmxuhP6UD@vT zwLl_5LJ0Ai4~01AI(8k`5d*px_I>d2|NhSLIVZ}WfBpJ9fE_dn$OwFU>KkWW%R6>V z!v+}g!hS}-3vSa$v zIEprpoKve+KvrPph56Dnx~APRzB+y(TfRWXYSVuG{6XEZJNIeJ=eKr3xf)r`#jI+! zFFVpD!wF^BpyLH*V-|z}!&hv>|;CcO@`W-AiL|dC(@;{BYiH7*>WZJEe0i&^MZ$S7gCBP=IGU<3pKVd zBoyWiS(Da9>&3WoXw2eTC?3@1{E%u?nOZ0sSS`fW21YEVhhix;tcMaQ&x+xDQHd8r z>u6`3Y*dvB91!Fs*N4D3ojA}xe4V`bSB-^(Go_pi`>()osH6Ter93}38;W(Tb|(+1 zPqyS!iz{nB(Zjd6OVjr_IJ14LC!3aMQN``pwnKf6dMG9TbxOk@OxHJ!Q|_ytFYT5z z_NZLjZ7xqL8Lej9(%tQvo+rHmz7S|!ZrBT`3q1OtngY|qZ5*rAg~sUTl71_>jnOQ_ zO-zl{!qi9&OpVmO)JV-sjnukmoFRp)n5FmTX!RS7Y_(DS1@qNgeJoWs`p~PJeJoe? zY#*hnp6g?!s^|Myt?Gq7Zv3Rw*66-YZ4;!HCA|sKo5nod7ocMiD_FuBuHhD~*2(H1 zI0x^cOe;E3@e4I$4>D@TKCpiq+=fY)XY5B#&Daz6d4t<9`CXxTsh}PfXv#2G=+fw# zqN_+3J6jz%`;b5lo!h5ff*uz^AE9D@mrERHpmsOe;yI zJz{z~f{CjvV&V!CcmS@LWInAXnQEy_+jx}r?N*aa`^3~3!Nm0*F>yr;Jf?pN7nyD( znZ8bCdV<|FrdUS{dcilu)EvRYohM@A4l{<&=$^qIg*GVrXEc6J7bji~Ut<5U{x>W9 ButNX< literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopicDto.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/aggregate/TournamentTopicDto.class new file mode 100644 index 0000000000000000000000000000000000000000..564c1c25c860382e4278b76f37a59865c0160fb6 GIT binary patch literal 2985 zcmcJQYj4|R6vzKhFG-uZ7kbGk3|O~9(+l&;I=a!WYiZdQ$hxvreT6`7`jB}_?8$aF ziT8XTkO)Y;-~;fX5dUK*PU1Lfw}GnqBu~zB&hK3P_dkFB1z;CPSqv!r@FFnJYPNsk zT4tatj$Lt0->ukI&3AtcURjRU`70>mx_tLIt-wfO{&#~&-2~4Y6^>oz= zw0RUC9=R{<%6{NxF{Cj0+yD(sQT*Io9E@P}gtWxI~Tupba! z;jnamGU~p0>eemW>4-YEy%2vYi$x;j+G*;BjPgK@%$Dug!M4I+Vd+?5XwN;>ISgXf zz&n^ym?+tfJ~*qNXzwUW&{WB-ShZt|Nn^sAheN?L+gGqI5W-v4lf+ixe|56CbezK# ze3-=t3g4Yykv9*=z(=^s?yBnGsHf*<3rih6)#|!YC|om;#fUI282A{UC>SjZQ8UhT zU2V3b+$wOMoT@a^lYbIv(Tb8*HY^#E8}kc z7Vn=Co;1ry!;1deht;~eCB8l?Cv36xy(;Sq=a`d$8TcbJ!*)7pD9K6wisAm z`5o^!Ffa0Y1B)V;8dw#1qk%P%w;I?GdAEUkZHL!F)+suirn4FT&2l93SfKk=x?QJZ z0m(RG*4fDV7V1O3DtjXb()X=`f9<}81Hl>7? zSzcjGZPq9`SDF3!4yH`ZlnE;mGi7N)$$I58$+Vbcl6ZU5y^pWbm==>v1u`x5VUq5M znMOk#h0X)VdLp33wvooOw}bcalL z`!LDW#7suW#8*)Y(?*hME0yUHifOgmNHXn^X}1rPOmNIJ7BcZwmBMr{$+Vx!^fkUo mW9pd4sK+0Xsn~}}wn@x1L8bwe!slZgP)f5s!M7;y%>4_B?U7#q literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/TournamentEventHandling.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/TournamentEventHandling.class new file mode 100644 index 0000000000000000000000000000000000000000..4859bf2a377a07e47ae439c3b073dee09fb69bc7 GIT binary patch literal 5380 zcmcgw&2!sC6o2b{*m2V)P1@4Z0&1X;6tF0SkJcfOrVas|Hq)e;>4lk%ysd;r;T&8daCx>K6`Bnt zG4-o{A)2Ujhdu1Hx0&3eSX*?*v8cU8CHL>chr_DHJp%S=l*gdTWVS}E`jcx+wM1i` zN^GhM`z?X&F<8wgyVt*6Vay}S0f<0#%{TioO5%#vS2bR|WkJ3(i4{ZQ=q1p7ih zGcDmLb`bV&Qx}Um_mp2zt0j1iz<2$W=CdnI1V(f`QMVv6OWvVlb;EQ2 zowhns`umn;>^4{leCby@sv{Z7Ft)*)4)t#cY`!qyLM#!t1aDLqks@6T&uXy-HW}O) z>GH3oGqr~(##;oodW)gkjm)-laW@OIv`h$H+j{VJIbT{jZx5I3Sa z*MB-BQeYsEloTkzdpYHdGxv^RSI)3>9+NX(P_>-%dF6~F^fqO{?MG71t{VwC<3c z;Z8l|O?2Jh+fHZLGJ%_!r3=6F0?@#;BUgfFnmr;aZ(y0T*b4VM_>HI|>Pf5N$7aRf z0T8&I+W4@?NhbU~vhD%G!ER73!dC>YL~!wd7onbb7mr5~9wy!;gFC*)_1=3E$vn6T z1cvbIdk9{J5g5g*DJViopU0q#=SduK4#uGX=kabHNBZN-zrls4QQB5Yz-9a$ z#gTx=75p97*JXGKuHxM_JjMzLj3+z@l3hob8+h$cdIir%;03%6z+X)PzjXrm2mu2p zqOdRn!|*y@7vK#|P{ylUc%M%4W(vvOERtE6%e4M>3UDn8_#Jq+Ykj2;>&+CBr&%O- z;QdVNKS%*)S->B{M_ubb?!)>|Qb-PalK|Yq%!yJl%9b=Czs&-x<^cOX3v27Eu%CN_72tFH+b_+s`zfjMOBUFJ9AMA0z-l?b de(w!71S`58ebFs=t9btij}k7#FX3xg`xh6b>@ffU literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/CreatedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/CreatedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..ad9567902dc45a4a0831b6ff2de9a0c61d2cc90b GIT binary patch literal 1942 zcmd6oTWb_C6o60GZMVBttMyv1>H1PdW)OUGMUYZaSVh`e@G+Y?olQHF3`urdf0DmI zP|#QLM~Nr1(~c`Es3`c5>zthHm&}*1?>_>-W4P6V27|rA$D>TSff2Dssg*RbGg67n znO%P%EXG2+1GFyAm9)m89V>}0_GVA8vMb;lYCp`w| zYx3&*$6%%2{vk08&O9$`^-@@RyF8J?;8aNuR+V6Ib*(?UK4n5yHb>g49Cwsc6#wJE zQhBvV)4jg2Y3z!Sz1?8oz}Wp*8Y=%`^-RX4M}6gdxMIBpxXR$|uiVxDf*1yK7+zKe zoh@=m;u95+I5&N3qCgUZJ2SIEfm<|z=y;vpC8J4QuLY|NF3!$6gT?2nd=aF=NMe0gD2oR^$oajtokOcyVROwhJOPa C-F?ge literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/DeletedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/DeletedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..6cef0b7462caf22af9bfec65bad62559e89fb0fe GIT binary patch literal 1942 zcmd6oTWb_C6vt1tm+h{t)wW*hHLWkL$P9u{t_Y$Pg{>5=1s}7S)7i8$$&h5*`jPwu zf`Yz^A4)u#opxMV!HR+p$t2m6|GDIs?ALD}J^{cZxYdLPgT2DXgG{-e5wS<9l{B$4 zQi;r&U4JMn#zMP8v@Xt-w8o)5P!e72&A@7rqxLZ#Q19Zt&`G9rIvj)@PcTD|$+PeS z+9pggu>aEE_DM_|A*9MLgXsrKEB}zem$mL+@GsKTVk$hwwZoTK7^l3kZ#$=OA&s$# z(!wjFhh^NdhN|mA8Ef$}oBEf|`VNE1$0orhv>43qi31U5g0^f}YwQf1VU1g_wMA#L z15CIyd7-k>2`{YaWthAByxrRFBTrCfbjS%WgxnV?@>Q~qQq*WE!6X_v^r%ANr_~yT zXo$ipPYgP)jpw=Wvd=wv!lMEuN!FeUA64i@6?)MqdcW&0I!13eAoNy;*{QrR)`tSr zDRC)uh^&YFxI#l?|F%FriY(6u&T~?sk`Yj49!mGos*s52Id6reB+>Ux!heLWilkgx zd#*jEXzvUy*2&!osFlCcZcJp{8!mW=tli2?N+M&7+O2YX4amlk#3C>?#`p)tHq5}> zEKI>!1{c=4HAb(zB>+pz=DSMci$UH)yDf+u1`AyyMYbcX3iE1l()X2Pke!145wiZ^ zIq5K1s>!SGAA{w3`KQ7#IJa4z)t0dIc6lO&!CXlXRwu#W>RNaF`IH4&emT%y<+!7q zqWm8PmddL|N_V@)rm-tR^>%}ULu2=2Y3TTms%J7TBkC&W!xihyz*PqCe&??K7sN1_ z!tk;(XuqO>BtBIEiN(=d69tkO+#XvEO5CCeM8}WmT{4>FI!#z%aB+Oq8O&~~@r^yniouhuX9Jk>-EYR!% zwN?Wb>9a(QqJT>zbD73r(=vUJ#TzspieDKczVZw4DF)YQ;{;r%z5zE*q;JxEmD(h^ G;okxO>wUuj literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/TournamentEventHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/TournamentEventHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..b9d60ec4eabc41d2165030254838e1011f143bf2 GIT binary patch literal 1583 zcmbtUO>Yx15FMA$G)tf#v_SdX=78D*yBAKRs#KvODg{)LQi-GE9X5ttdu2N*{4ykv z;EMQBi18-vrd1&X6sLpT7dYE`)WMV_*sw4>Dl0wH;3&}I9 z-@0RNFy_i0qp@)=h0zwxPztoM(*vV;j>^S2q}9fKu98fubY%Dwho>=nKIFL%KvRbr zgPrNTxs4N@bE!rI@gxR=)hdBkSZFJqHXj*?&UE0Z1@SaBm~w{`6;vc`2J01hnbm>O zq=}{C8T4kzKxl0eskoDRTxEqc!F`T{5}4nXO1d2e-#2HV`|S-*DR>5of2?Thh(YbC zPOuJF87#izLmp?GdTGxXZ5CjWu}80!L94SNCZW)IA+u7purRupVQ!yId65n5thx}3KNYsRN93(ccLFUV!{sv3WBVd z3KxxsDH;z`bat5jmM=OPrjbFz)EZSM^FkZv1*l321y??^9)>3h1pzc+xd{zeVX(H@ zsRZphLv7mjf-iQY!k2@*hvtxzh749ZTJY?M8|n9BXU+AcW#DG$f-@Tb$5TO_zVrfJ zUnd9L&|~9B2+BJ}w|g?8`w4lLH@8M54xG&5E;>5c=*pBPYhb9Zhv)y_o={|8IZV;C zciIbZkHN;D`;6L*LC~d_P2e->MZ0tHGZ%TyYa77e61}%`^t%l6WYwS!3#Es0gn;~4 zX!n?+d|dDanr~LFeS)QLWz2P2=LrMoTcx#G+5xP=4cgtLui=4#Kz+z9SSP^SWY58! WQ(%951bp`b;MN7e4DOd04}JmHcLTrx literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/UpdatedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/handling/handlers/UpdatedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..5982c471400dade08c8885012029a6c7274c77d3 GIT binary patch literal 1942 zcmd6oTWb_C6o60Gw%c8+t*y6uP1~1NWCp<}R|F{)g;h##;A1v(I-5F^3`w@FKgnMp zDCn#Bqr{WhX~&fntSI=9>zthHm(161A3g!VQ&?$2gTX=J<3Xle&xqKg)JmGz8L33( z%)UPo7Gt5^5n31LN?PO49x910_GVzU$Wi+k52<%?U+5%LIvq~JiQOU*9+T(c1KK7` zFtGp9-p;8{8^NW@E`zDZN-O__!I#zUU$8II)M84J#kIjLER0j$*teZCn2^TUL}}rb z(Ze)uT0_}&A&s^2GMf5JXKjzc#JWka2`vV*2jWn~nV=&Z)*3qv=UHQAS6g%@JH&)b zlNTy0jqt*nUWU1Q#M`Z%KJo-*Mnz6|A>P%C?-+!)WeH(c=GS-X{)l$4ASYPZVcH6RA*TVu5CEdf|!HrrJiUk&me+8sgUFqrQeDY89bRajS>6TYt;gX|3KkC62T&q;^D z<(jr*CVaciKx%5hIQMe#ok zER|P_G(G4Vo5rpP+1n2Wj*NX1OGD*9uAa%b^r)+x4_B-+4L2FQ`<=V`Ul7A!62r^N zpuJ5FNqnXP5(}fZCJH1mxHmQ%6u3nbh>q9kT{4=~b(*lm;Oh9SGnjd)$`^5m4mnr? z2IuGx2GF3{BuvrRq}4;(4g2knF#USI{T^mMmol@|Pmv^0o1=cFoVVc;%+u-$wN?Wb zXt+#`ynt(@<~q&8p+y=`ls9NTRDNTO@}*xWpJZ^04xWSC)HmSHsp`A5UZyrdX83ov Cf__r~ literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentCreatedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentCreatedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..6fec12d9c344ea2a15f9a6075e28f0423db58e02 GIT binary patch literal 3334 zcmcguYg5}s6unCc215cENaB(-g0v~L!Rq@#(j>${fSA%0LZ{OY9a&!N1xc$}t%2l^ zb*7zZzxNOH-*kFblCee922W<{nX7$Wo%`5(kNoG~zy3}{E3{pp0YUL+6niKDpf1knjsk?jjt$%w0C;qu7VvaJHg=Yh8QF`khfw(9_D*?VUv zRmC068*ApgK!c3#O4YQZP>G;nNIQYvcf2;gggDKQF)GA9ZZ;T|YB8ec z1WkKa8&B>r8i|khUW9-i(Zc5d@k2)CMA_`vHR(FuD&pD03h$J^ZwKaJUqk{vtptJ8 zvGWn5rJ5^!`@o4bSEs&bA3E+)Bm<mp764 zCRZi_t`oT2^ElvpH6BapaVLPrWcR}3Yn3XS%bofmcTFr;yNchzC*g_^7Xr)W?m-nL z67jWD?zW9v2&sFvCqv;v{YrQ1SzEkhG<07CLO)>i*FsMWsokEPI3>y2+|Rj-d(A;y z7oEKHfOZnmv-PUwH`-a;XEa!q4PK!48I5QG1n?Pv(=Dgda+!P1xN)EL5%Wr_QZT zUJ_-+HQ340XlP4BmIzF?kSk=#IuW{Mo#z^0#@!S5{la3gNO$PNF}h868O`*TTS^(` zvc_v7;NQj_wwJI{DdvcM2h;>h>E@tr36%3;?f+t0^x#m026GKhZgbs|4KyOy77PPN ziU>94S3@P!3yVD!od!2dUoe{c?^;8tF&gVYCCq7-hImr)ob6!ebm1_;%jRoM#MS)9 zmkBO`XDhuFTI%=~_j^jB#{buq#ZG2+6P6v;GARR8%5Gdm>%x3-XV3YWe%337 zr)Z8Cy#={MSLkgj&@kpB`0@@=kxDd1moYElEE&gX0_T(18^u_HOhq(}$qZeM6|X_T zyl_1o%K$a&S-KIAH|d>typ@htI?kozd_0Z=_%^)@C*FhhXK-)~WAQh-^J8h@7y952 ztOjTa&mm~yZvs{D9E;~gvgsqNK7lNa(#IHPAlD$*A!W!c*01dKbI72#=|HVT2!Y5mssK!U#X*BCPj;@N+K0lRgmKT!f825SqCNn|&Z0 T literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentDeletedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentDeletedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..1ce96fa47beaec2ee366f21d2019389baf1ef8a6 GIT binary patch literal 3334 zcmcguYg5}s6unCc215cENaB(-g0v~L!Rq@#(j>${fSA%0LZ{OY9a&oJ1zD?Et%2l^ zb*7zZzxNOH-*kFblCezG22W<{nX7$Wo%`5(kNoG~zy3}{E3{pp0Y=SG+ihP&b?Mle zyMb_}9Z6R>ek2d{iKDpf1knjsk=+umk`Y(O!sU^zWm^SKiwD}~$9P6|*scSpY44q# zta6`g?yZ^g0u3^{D^Ab*^>}p0yDX5(TKK%*&fd ze1j{K0M`jz?)x0@S&he1`rHYiG1u z&I*-81-7DPt2naPAD!!2A?nU)aBCjgl?D&ELJ4`5o=&c${e5n?oBc}M9MQbx zNgi1!U<*a-HZ2WH{4~U^XDx-glJZF<<&#P;pIp>lCFPU(Bqq@crP3zsZXgz_*7#HB zRwplsvf>)-WN9?CB_c}%CR@lAvSghI-L%eg4KU;GiTi$Gu~?)#^x+uYrn`)0vgMXi zhPkZqnh5x}afj_CtW=6QV&4HZ!BV<8sG9=id|3Oxm=--a6rsUf!;{-wH>HP01lxjP z;7Adn#{6oiWO`vSQ_*Q~!}JBCx&N*;lp3S44phRNW@(5gCC}Loc1{-#6TED^)P;BZd^s{!hCUOyMAUr z>lMROG)Iiyf?T32^fnb}81oT)c?YOSB^sm4n3r&tjN>$c^GWQDVk|+XBAUizhOWkn z*PvitxSoz>fSUCz-H6AV^iDk9N=GXl=hAUL9>)QEo8E;J??L-BI5>u}_#55%v9$0D zeeefX1GI$a5H#^Ofhu^8#q%QB^buB{K$b@7V+=EpYmn=ZGGrEV6LJe;LFORy5JsQE z7&|j+pcA8?=NP@0W0cXCeIPu{Mfi%o&h=qA$A@Yz!hQNC7a=c-C%Fg@{>6tpgr~U( z5Bosa$whdCf4UdFi``s=$5g#A!VkF!tF(4ugr9N|*84#CITztc9|&$P!bTqmja-Dy WJ`j#_5w`m1i!c}ADY|ihYX1OP>@)!Y literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentUpdatedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/publish/TournamentUpdatedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..3ee52ea02db497b55fcfcb9228b31d9fb16fc048 GIT binary patch literal 3334 zcmcguYg5}s6unCc215cENaB(-g0v~L!Rq@#(j>${fSA&hN2k*d9a&!NC9+ntS_8=+ z>r6Y-e(xXXzv=X>Bx8xF4W7)@Ggte%I`^^n9{JC|fBl_^R%p9G1B_aMwmY5(>(a3` zcYWbXJCv?)yigwM6Gw5|@xv3YLc1+oB}1-`h08-*%Z~D$Huts7kMRucpi>7>%icRX z*$*0y=8ZLTUZ6onccp6DVW5QHJXCo3M5^buD}BvRwY?Gqo^Ty4q@TPo#Ay1+Id*Iz z?Om?mlz8dXJz{7doKb&k7&{60BHw|%CWN9v1`(Gyj8@rhZWu_f8X)V!GQ<`d|L5+ zsUzn@MoTqUw(UbF)LfmmJ^RRUpNG;%D!WSd7S`)hwH*ztLf~kq`i!Pt;J38Bz0_z} z8ym~*_HqckQK#8dyb0qbgo#uLH5ms0hm|&-nOOZsJe%}8?K)RGhtE2Q2#EsJRO;nT zB)-X&Nr3D4F84eR__W4jDLwA^(3tF9cvW#nOO?&#PW_0xCYGx`#qZ#ga7BO%f#q`V zpo$U+`PwOWJH{=9)H~agfpDRIrMLC0BVIBZx-Wd8A29lBAr(VnH?1!K!TV0=>^@yjP`bN=Y?Li;RhVU-3|S$Gl;ozyje# z1!o0Hq5|9DvQ-?}YjLZ=T@RhxumaSb)8y7Xv@1>Sb7lImlIi41(%c-Ik?ciJu0z^|YlxR}wy{Bz#iI^2tT*RT4g#k7E+;Kq_s*?ge6@YK=d2 zZguk#D=V(SPL@VPTOzcCZ?c74AxqYY&@Jmc*8nr>o~Z8^7K=r?Lm!UOZMw^7CS7g` zWthtvuL+-j8+F)T%u1z*BMux;6D%d0gSsV9&PTQXi)qn=Lm?W>H9Wb^bxStTh+tbV z3>+yU)QDdVl}s-zrYbrKZkWDcH22@NhEiiR)`g0h(<}|~B;+~U#m?!%VS<;<*P4i` z`He3VTmsKl(iB?i`WE$jLZZh1-5!rdiRx16G|u9?&amlB2B*s#ab~JLO3kWuxdiDq z89V!2p7Cn-R?e8UoYBsb>y6CWEBrc@Iqp*IWM(&J*&aLKs9Dd_jcB|{??mIRWVDiTE*a;eaU8(6>0LPS9<)D$gJT$rztNo^OAEiy z2Y+BSKudTIK@)!ysDkHMG%u1(A7S+gWNDN>#xMi92DuI?LuMg2A-5nFWDYV9Ve~1C zu`{CvIyU-wj?sHLMj3tC2g1W#gs5+zcl4ULi|5-%XH=j^WA8GB@V zlSbmt@GFo&z&k$*ab|Y1OWL7Lq(qf?nDN*?^PO|PbFu&W``14JpaY*bpvGXB1u;&v z?^`JXDx;MZ-YP8Iuz3QBXnM*S~=^{9cqPM1Uq&{rf7n|LwbAR$9=Dy?xWar zCL4C?546qY4b_i5(8nF=%$X#n%Rdt26BVi8=;9bT7cr z(s;&HetJap-Mwh%r|- z&2%Dj4teHmKf%<0#aoNL5%L&SLJ^92Ce;%;MBXOpXov<~&VO_&Vo@2#XI5uiN0T_V zd2G>hV*^iRP$M2l6F$u-T>sXTg{aJ%sLY$_bl#j*Uo_2|_QWflX4Zw=@tG@JNt0XD zM}ET0L^vDw6RmhS&X8B69Z$!;_$$VNHe|mxTnEqXk>LZKU~6%bbrtwLSS}0T6QOOX zf_29gvSiCNrAjNd@}j5^eIE8Ag#NecFVC?{LvFzmTwQ`iSYvQ)y*n%L{ou42ZsZMM zrAyiP?KthD>q)9<25VibWO5*#&gbRgd>CoZKwm^hI!l+PKDfGrVPxZ7>8QB}>iZ0q zN7BRzmWj>a_IkIPyu!7cdt(!HiU-3-L`q3~JG!ys31WZe^?tO@wE zJV!)PI9=^S-p0tF>VLc%VN4B(j*_A?6dFXw9~-sNgm)PHe3l;iN{g1kLJ<=NHzovk z2E_NKO;a7FTz^x^Sa8(LsTV{6+&Jx{_`3x-pMo!ws@$tK!u#;S z8S(GE7$4w6!mncDqY7gmybNsp9AFP$26pEhV0$kHn}fSVW(__mqLe4Vr!>D$Pn}jj G1Mv?Ee}w4( literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/DeletedSubscription.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/DeletedSubscription.class new file mode 100644 index 0000000000000000000000000000000000000000..5d7188a6542d9c609ac9ece48108c7d5a5493dac GIT binary patch literal 3002 zcmds3TW=dh6h7l5wqu+o4Yc$Eba1((1$JA+10txB35it6p;2NY^#wGZJ+`OaomtI{ zlSbmt@GFo&z&k$*arP3Mv`d>vi7N51J3BLHzjMxaE;E1q{p%k9(1p(%P-8GkeK?7g z8yFFKlv+s>IwO^cof-N=VKEfi9inw%qNFtr?SYc$LT@Hkiv+a~@qpegbd!OTRt+%x z7Gv}n?G+2DS4KBrp25l!aUjB2=u!A+@C2o2P>;=MgqA_G*UP`w8C;%W?-lA8{LniG zLrdes{?zsopOn_WR$BQ748+>Ss=Bk!V1C0y*nlMl%QH$gtu?j@%WUrV@3lo|;scDh zG)byrk#WdVYX&hU?knC}?2nO0C}WCH#8V-kiV^Y-Ne3g;Xa(^foQPOZ#__q;8P`D( z#}1DSIc@va*6P(PO)9MSRdDAJpQb}s8&m5n*!llre zMFZr=%oM`uWDqOK{ZWd%BJFrGars{n4wWYRmFCJjZVojcsu){~Mb=f|b8ooFfsci@ zg!IN9RmhSp)08T$*vyKeLi9!0^AP&qs=qkLE)2N^ZMfWqMObBUWvw?W@I7yp9<65$ zV7W)x_}wHKpzRB)X$Gr3BSpL~tjgx);=CU#$3UG&M>$?-}Rf7ArHr%!Oy4Zp|7=Q87$;6VQ{S= zxH}}iH%yW!Kjr$HO2)jUW=`FR4u#_M4g0ed5kF7iLOK?Xa_q~qgY9I0QWVcZpln{~25@ zZz%&jb|%OwmciXhvM4Prn(VjP$|3s9%8RcJty#yagRLW_3ZpxGBBRim->8?+y4;d&x_#iR)BZ@8~j!d&Zpo@q$=~OgK!%@ zI3@nQm*WF`NcdGud{klVgI9sAodN9OtHAD@0c`i>U~_Pn$gII9d6cpQ_>|`N>8aD| HXAu4arKp70 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/UpdatedSubscription.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/tournament/events/subscribe/UpdatedSubscription.class new file mode 100644 index 0000000000000000000000000000000000000000..7850d0cde051327d9be07d91df38246cfd6a1045 GIT binary patch literal 3002 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 ZXUGeGKq?*=Pl~6I5U&?+5O3U?_yLPkv7Z0{ literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/User.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/User.class new file mode 100644 index 0000000000000000000000000000000000000000..6fbeec45a16341c6b77ce42929fbe3fb624d6079 GIT binary patch literal 2059 zcmcgsU2hvj6uo07{@A!QaUGLFfo&+Q?WEb3PpN^TfKsVyT2-Wt#N&89b*9~&v1Z1u z!V7;1B%(?n!8<<+ab~@CT-T*MAY^%G?(Dtio_p?{{qwJ1eHh8DIf7D;i?}UIwMtom(j)VhRVrQ97!v?iP%$0JMN@b zmeB?Ro^j)NGyq*`olvoe?)DH?@odMiB_2CD0{(>={BN{Q1Xu7ZeJPc6w;A1P&AiX^ z|KrI7qkV)DzSTVuP?1(^RHHRU#a8>MN=RCxWh(jIdX1{%-P<(^Xob=F1F6J=VcHeu z5l3-e0v+?@h#Tqc{BX$~OH_OR;LSq+>&~I@)%(I7>)u0dpuU5Ej2^cB(;QxyW$liI zVN~u5_aJL?qt$*@Ryf#+!}@|oc&HUX_^BJWbp`%sRz=#qyv^$`;ZK^xsEUAzPjfcK z#Kq@WM~?lGQ0{QpwXulnSaz2L#@=&R~Q?Gzskr zr}t)OA=Oqp2xyCL_%Yl>lShDga&lLV{7fllEA$DYAO0tX^T|PuQE;e-CKh+3ANZ;l zV8^S-=n}@_)$6>E>+eo>=`yZR5#O;Q75t5YRfgr)3f6+ZTiJ@6g*Cu=jn3ixEm#TG zC9I9k3p(G~I;DDN`xMsoQ)>R4MQz~O1TN7kJlB9$N8|825F~Z6+)L*Fn6E+3ljP)VJ_!??sha8bX(M@>y;M-U|rBbHNJf{$)T%O@n znsO@TD&(BJe2hL^$f+^U>B>S*R~B<>%yViXbNe-%d_Sf-`3^Aph_2!}uTyiL)AmA6 z*Qm8fr>$3(1@V|+% Y9M|x&|Hq5`LHJW(6zH?*HTj(AZ`caCDgXcg literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserCustomRepository.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserCustomRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..186b3e3c4b39dfd6e7495a333849fc0819428ea8 GIT binary patch literal 391 zcma)2J5B>J5FIZh8v;lvXt+UW@BtQ(00}7)D2Npm)x65 z1qIc7-<$X5(az=R>y)tN{U-AY6Npx{TA@6c8P4r-$vataxC`A^uzFZP*Z(iEzz%r)wr>=aBM zh+lp4h#?E&>{-CL_9-a3Gd7<({mbQfFM23Iz+V0r_ROXZfI^)(EU-$w1P>%nTgb>y+EaNcb*vy+Hd%PF)AqA>S;!(eQP{4z=}y z!_Ck$(5Eo`%09BKrtLPYr?pqQ9vUbpj747^&#Gvj;hmrCT2m_wq%pP*6h^mC)^53> zZfJi=q0hBjbREe|Rzlx#8=Si&An|;`u7}PM9o4>qaYSava~1l7kc0|*+vn%n3akUK zWjk&rwe{m9r_#5IMC9g$Wyf{GRfXR2OqHA)-hnQm7vm-_V?<$S+i~^oVXLP7=W)6t z+g{ynR&ADm=_eoi!k13a9pFWpLBPFo7fE*=b7`hp!c}}=;C+QB=U21qz$QM#HHE>3 z4$~To9LCEtnVugDq6mfSCJbB>nkf?>;bS(Oi+3ZPjGbDQC`A^PO%zd*MYAU6WYGW@ ziF8Fk;_-=zf!M<`v4C6DmlLcfvN}!k!1J2gc9%-Hjn53+QMi1f-DJ|l=eVn2)?(kP z-8`faDVBZN?4Ah|ZwbzliDfB3i8$3*x8ksBVlYl}&E$!FKpHwk;!}9WEYEMjg#;~o zZkiavFtse_N@1sqJ-Ti)1v$KZAyZ}7gO09GlyjOc=gXjH+~V=9OVVNS z7@)^-KNX4+IceUhpvS$FCD|+I_&YnnrAa4Pbq|9?t}ZA?#YR4PnTOuhW?M*%%@Soy2d zBj+!&JY|vP0f;Ph9$9LSc983Mf$}-Z(QR5q+dumU#(s<3CipBd94OX4E_s`8X6%G_ zaD^iY@Gd=icDGHYUa1Xk%LJOdIoJZ@$5&ZMyOGi#Aro-fv^$WY6mn z<_Py3<$h!Q8|U$zz%&Ug?tGVf3(G{jU&6wj@g*LT=_}6lD-HyZcOIVtXwk+Q{*K9C z`Jsmqz{1JLtGU@d!WO+`@hYukSD2k+S|_Y=eXg+qlNqK$%2bG&k}??#q2wy%vy*AM zlW8iK=`o(n4$}|u$ zEwI)9t<&N!oXqy_PA=09c9S}-(@O7(A~lg-C)OebcQ1ogOxsta0H2;ji?x?8sg+KqHI`~U!<3bjoa3}qgAo&dxN?{_I+-?enZCxeq*SM5o;i}S%pZ|y d>kKBjQ&Og(s6&ye!B|{w~ zI+Q$4^u9geMg&|XC&DB_Lxx5t!W_#`BteT-0goahB5p;nP1|K2T5UcT(8EwVfurb8a@ za3kG5JM6IsGGW-N<^S?3gBKb>@wjjpepG7hFjkZmkucRf{l2xjI-4h8wSX&>$@Z(4 zjM1haR~bHRDPAz>+)AzdrqPt##tYvoZXF1}A?$&!`;oAVp_Rv?GV9C4 zZ>hT2Cwl7szBaxkJNvmQe3vGQ!ShhUsH1U=VYD-8o7PAbRUO|0m&LMDXDGP(p{rCW ztEtCzZaDd-LSql`lyi0uI6E~`Gndd`vW6fnau z(=L>iI3^{>izaF{gtAMYLeX^f(T}@C$hJCC`b7Dc9H#txn8ql>pvVZiP|a$t95D=S zS{@#)@@CcmhT7GU7SOkxQA!Q>YVo?~)%{tUNI z&oRAQzH^3q&Ovwj#NpKSI6Y*78raR$(b&c9%n{+>J|2*VN_vZx$dp25bn5)hVlMOh qD)U=B$Gmg+=#&gP-Ns0-<(ARQm$3C%8zPl5m7VMq-C literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserRepository.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/aggregate/UserRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..e453793517b428a967bcccee128fd0b7ae2ef724 GIT binary patch literal 1571 zcmb_cZBG+H5T50&P*4y=M0{C61vTk@@(Tr$ieNCcC@oF=M3%eZSh(I^cK1O1Oa1^8 zP4v5e#D8I&EiEm^n3{OW-tElJ%{=?e%lDt3zXCuB9_65q!EWkA7Aw22rEnM|D$v5} zK*`wZ19vJ73YpkbG*&cJV6;VZq5`xcqn(gZWH6Er#V(0Em};w>HfK2)U@&ncPo#)t z5{d2oBMe*)h8R54CK5I^Dv1t^Y~ZOj$0C$YilbBtbKyd~N@d3(gW;7RR*7kxA=%3!<`Xu}w7hT#f>AG?V`tK$<4d7vAqiltLJ;i=L4F*cTYex!DYJj5WT zONKm^!Lf{x&y#T>!UPRnD=b{PbfHDXZ(2`9;ev{J9%{5a(T+Ey3l6!XF!(+t0=mPb zuF&GW(Bi#tnfKm;<@1n-@iCZyYYc`~S`1Aul{=hNohQ5KIU6sl1S?r%A5BdX4GbpB zI*@T)8s+7da^^rKq0i`6`24IwN!rw=DkI5Y=}(T&V`WaoVmtLD%ecs3q^hDsy3A0N z)gFIa>4LqLIE>H~*T~oGPL?>;z`C-EV3rbMx0z1MK$Lr}sJ9uKGx9cgBIAru8H+Jx zg*Wr3LVjR$gZpbYHT?7t4W1t+!~tLB%gt}8^5R@;34>QJmey-q^^HZ|IU%yD?bFJ3 zZEvfxS1r9PEmGNfAJiPL(H-jWjr2X>sJ$4>b+hhac*_gsKlBXNU#AWDV&8$*hV?I? zhueM__XLDTTlOp%~DQK zyR#HU=YCW>3*_Zk8X8y5&@_;NuXj=F?5Xx?%F&RbHAsWDABG`EiyDHYCnWnP{{`~z zCdWR()o&#A!6dzh$OdRlH8p;MUWaM2ztNOu;3j=X>3o*fEemsShZKVhZiBbI`#0Nr bZCg&i^uv96_rZM2^Fq^eo-_|g{}A98X{zQ& literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/UserEventHandling.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/UserEventHandling.class new file mode 100644 index 0000000000000000000000000000000000000000..e18fb515bed124ba3789cc8bb8f77f2dedf6cb3a GIT binary patch literal 5098 zcmcgwOLH4V5blv5df2fYn}met#t^UrXbG4{B4aR)i~-qk`9J^CNj`32Iude@caKHkd8APsItdsc$y1WzrMsO5dzo zZq;FK&D_VqvKre6N3t+VVCL95u}sIp3HJ|=sVxbNJG@q-g1~Hri<;>>M@z?MMURyT zOepZ(fQi6PWhfgFN?ELJ3o(&mN>S`kkNZq=aZcci;aIF%P0*Sp1D-Z8QjIS&m&p|Z zKP?Q$5x0}!5Fcyx;sJrtHD0Azm?kjY7O*S?7dg00M*iA&1@*aeLaT<&8y<5k$+&BH zf*(4x;omh1`Mo+ds?>HcjH-rb*$=H6HRf@4u|{1g&|TGHM_3iZVCx!HVikj6n>VVQ z`i9G;(XgalHzabTM(9y~F#FDIQd#HKZA(}Uj5C3( zm-NA=p=f$9-J0vYT;FNBk~Qc7^BJ1Hs!qDjafop{s9T=e_>qD><>H}fb63(c8Pr8n zAM6VArOI`s9K1;2n?90ew=GX3eGnMa-a~x?Z>;*~uHACU@G@q&b1l^&ur&nKawNzOIIEUc5Io-XzFntPO!;`p&?#99 zW$Im_>t7|X-P`r5Ty<}`x93}d6ehe@AVm`(22u`Q>tFcj+TQd7){RcVTc(GCN9ofD z9*R8B@)(hG@MhnFMLBzWtR+~O@>+sLQ=k@Xsei$uYkSlGCxW$14+V?T498Wmj&I={MLh+5UPp!2J_NcF(`ZphnF z4})JhI+E51+(=A31TF`lfoT_83C1*go2anMo5H5+Ox4z_Dr4#C8{rSGqN>USZpD@- ztVDuxb?ekwo4c65OIi2~t7LcT%R(g_jqW}RTj6M@6DK#BzPs)rv=29czzF`v9D!$G z492lF3t7nNcODA(p1~OtFbNsBguNx4spc1cg{cR#Gml{Q5A0>&3Vx2`Ou*+VeopFk z0iK3y*t?ETK7+vc!iON)vk3DXw$-BN@qGcF!FB-tLJauL$AFI!FmNFX2=g!sFJU_a zFKdDVwr*lSp5&Dnk~>Kx3$U1I{jC__auV?C@J7q}VjtF_3y`!ocAUHxQmg~4#jq~#0mLz5?Cn(*tbbw%NP}Lmc@zmT@u&_DZsu@ z0$V}F<1C94;D;oz4^x2s*c&VZAK{-{ah81?6B<7!f!#|1_9zLgoC55(-e4oJuJh3+ Tt%SFM{rmXj@F0E)pTp)q@)dLh literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/CreatedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/CreatedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..d680f971b8beed459e5939f0e96b1865d9a2048b GIT binary patch literal 1852 zcmcIlS#J|D5FV#Tnxz*lEsnRaFs40U>%wJUZU##;~zhw$qm1!+!t? zBzWgXA;!CD(?sE@fCqcWlkr^NH_o^3pS}RV2HdVgjX_%Yq?aq#HX`vTwUQ=rMkTKUHeKCHL?DyGOXiy76Cgdnf5Fiv@6-!;$RRvKedrG-~USM7Rf z4fW53cAext&^7PuF__#mDb}IEVBw885J@h`c*|O2XW$&Go!`|KoyiX{<_!D16)}RNN<2JWQzHM~aULRS86>wW=c3EsXV{=s_7=3LSE4BR}p+ zW$fP-$cKr@yFKSQd7(BDWy%Va?x0nn1krZh3Q6Um@0gUIrd2_aA%D1DUT7%z}zfM!90VN_11ti z+ujnVMfl*SjmZX4~6Af7N-Y8ff=Jz-Ti_mz{rqa01^88|+~qcc4WO$PIUVT0LX zupAU4ZD6qYyu{?Cu!7JbMlhHwS%Uq(mxYhwP!@jWRZn}>#XaQ|)wvPaQVtCHxZg51 zOI#7Uv>#j_8v8bph5$bD)x0KUE-mGJ7@Xz|Tx0P54<>LlmtQ0J?>WI>3d6g|Alf!P zEAfd6pf3+kL>#!n;O^KyP{|fe;B2x*lO@nvqsxeQqcurk_zz7*S0DfY literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/DeletedHandler.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/handling/handlers/DeletedHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..67549dab759304748ef7a9517f4baaaf6d0b6565 GIT binary patch literal 1852 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 literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserDeletedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserDeletedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..4de708b40a152575b5c98094de9bde2a82d5d887 GIT binary patch literal 2321 zcmcIk{ZAZ47=8zVgM$`4z!s}@!7tEqqqbUwYJ|Sd&l!$8xUQ-kF#x=N-vXp;ig8T zjBXg);V!pIcMh${MtN zX%6T~uQCacEN8UX7Y--9Wnz(T3orMu!<)r#dpcYkD5qf4rq;$6#ydu9t=MGzP&hB` zX_oRM5kGN8gT8?fe}w&gV>97_Rm+8ksAe?x6yMs;&RUX04<4*%*}4PXl<0J<>_B;N zVRp!cmMI(<)nJp+M2JLxIWA(aj&U|OB>kl^sT3L-$L}bu{1*uN%FzAS?IRicjiHkQ ze`sG)_S}eT&5qR4f|R6rCLfz+mYzEe+%$_oyX7b`(nSbHgaE|d z$U}!b&Efp8j@&oPdT5sQ&^%iYFV@s7>tVU?hstteeQ@=e@ljl)MbwtjfQ!DcmA+?n z&z0p)eLS9FF@ z1oJDa|4hOOYBE}Seoi5!7)=kL`ZXW6e-p>(+5kJ$_qoEKpqq?3FH})v?SBp+qsy>t zbGY~$1KWxYR+fJaQjd!cFojiUz5OOI8D5utVByTeL5-qkc+*b@5WHKEe_w?byKugP zH$wr_5;1xSV~i%~RjT2SmWd`iLUn4;G+n~FL6^&UhGud9GR~$jH!$WL&111ZuM~n; zO2Mn;ya-SNAc8=z0rYk3Zb9KV=K8O6{kul=GcEs)-3Yyn|2QPk;prXxr;Bx+-lY}n z-oscJqt!5BT*0`Cv53LweY%1F06akS!PhFlc?CElegTA=l?Wfuhm|&bRAIyIN`zbV vaV0{P7aNrbpIpF;yOjvHFMzOBiSQ|X_OHG8suE%S0yeZN5jKzkBXs94hL}iF literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserUpdatedEvent.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/publish/UserUpdatedEvent.class new file mode 100644 index 0000000000000000000000000000000000000000..a4730048bd46806291a3ff4c68cb41680e6b98d5 GIT binary patch literal 2321 zcmcIk{ZAZ47=8zVgM$`4z!s}@!7tEqqqbUwYJ;@3ZH{W>BQa5vx!vJz=y2E#Tuw;YLWO2Rw zBnR}QSD6GzmNQ!H3x^ZlGOoL$|=~iskQNi@s812D>fNF5Y9_` zlBN7m#E+fP2;V@6Kg9l?v6=9|s^!8%R5O};f^Thmdo4+#`}fzgY~2BGN_09_cAz}C zFgxTz%M=cbYDAOKM94&cIWA(aj&L?NB>lB9sT3L-$8RaE{8vc&%Fz9{?L!&+jiHkQ ze`sHZ?3pJ6Evx&CMz>5NYxEYQnWu5TYpt;pw8Yp?d)i88(j%Ef2wASuB7{+H4Pu(P z^{76%+m%rw;}rZ$q8yPF9T_b{y4jIh+Q6{pDLyt!mYz8cv1t~GcFR#>q>GS@2nmS0 zk%ta>lEe979l39o_0TNqp?SIH7i&22JJRsP8He@#ogd>=A*N&h%^?Dtue|u1UTVp--STpu7(Eip~&< zV18xwza*TXCZnZia|)DVG(CXo*L>Li8#qQ+2H2s#&lLUyU1!vJu8M-SFPuO|7h&1v zaPc<=wiO+$EdLs$9u*y63aik1`%Pdnye|8|!l{RY8b#0WrcVb1c&7;dJr!E)!r2bq z3%Y_09~#lGwEQP_BlIr*OpG3F9)xA_k)m=^Fk6@Bq;VU#kG;72u5cIS{T_B78(2SK9DNg$*|=5pK|@ ul?YW{Y*ZqAb`CFYS0daz2f|h*!sqnG|MudWN`&=u*wCs(*Z>1Y=+-}psz`zW literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/CreatedSubscription.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/CreatedSubscription.class new file mode 100644 index 0000000000000000000000000000000000000000..4563bc074ece01051850239f8904a730d7149ad1 GIT binary patch literal 2894 zcmds3TW=dh6h7l5zSU_$prtLh!Q9dUyDj1Y5md=UiB!p(NGz)Qpsk5OS_pLOlkKv|4;V{~t zZe&jBv<8a|R-cH82xE~B!^iz6C>?`ptcOE14C>u(KHFe$&7k(f862!%J^bB628)|I!Wt|ySeZG!WsEj;SYZo4AEXAYjwcv#sgq2_!n4FP zqx&%?_FLX;^hU@dlrga%@l434Vu-v=(!mf@G@jw$Of-TbMsvHK&0rd%Hjgw~o@&Pv z;pB)r(t*!13dO&5WG*POAtjosFW;MDOb*NeU$Qnim}<4=28hhcbbbF_(W)1NTbt=I_WffW9@zt0&BEopk)n*vl%=^$l* zxK7Urz0$U@vkspzn7kgYsZcU)-R>EKX!A^>QaqGo(A?K!BXOHLIfI+UUql~I+z5PS zGPqYt6Q!a-?H`ViVwCBWy7op50($bN(XPRLDw+9gBI=VwECK)Dl|5Kht`j4*MEi8KWSBgHw#H1>9=U!qLHNF2Hd3a zZTglN0s7-xaGOBx9M5^S_C~+e3gvPVS_O!b*tC_LW z{0<)ZAxI#>J3k6>cD;#-yEKWK3W2x-e1F8i#hGB)V`+vk)h!y$jvA@1#|I48Ow|Jx2S} zjnpfnYp}>*^@*5>Fcx|kKJGt3=^0dGGaRC2Q15p0*#?7a8S4I21%tirOBq*~8Xn9r zNk#P=rImlk;9&jg;qM+YSll!b)?k^z%FO94YmKeL3S0Q;KwESso?yhKNm3Pyj3u60 z(~mK6U-M?8H$onvjEVh-r$RmzL*#9e4u+`F3Su}o6OEvV(cEsvW-yIWn@0v6*T(Zi zcsb&pbl|g;Lh(->sSk>52#Rb7&St~;nu618Xiq&+NouUmoSm7^rO=rzedH%>Ooh{N zKUR|a;}m&GGVx^W@>>xOlqSoS=E^&6jx-;r7@Li0E|uVOZ@9>TPlUFG^u`{S$Tk~U z(iHl)vLr7NdLHm9g#K5GUxhQT0J8}#c&!BuSYvQwy*n%0eQ%W>Ze(S>(xrU+ew_5t z_5@WbgSD=aB0dyWW$R*d(T|j4AYZPxT%@m3FI(L~KQhs-up*(hYf16)Na!fWB6V|j z<_XK)xxF{mUL|;_oFeNxSvBU3h%g>>jU9$A&3qqZ&_~8T4W-c@pZRIe5A&kvD(AC? zM5dJAadLzaH56+5dA;Wv!j3!Ea-$CKGWhud-EiLitEjSzbO{Dac^nwrnv&WX5XYM) zNt8e3_uEpgyrqUr-Gv%^!B=m#BjS1TmeP@Mlu=(@9&9(eN&L4u+r6#5A2%QG>^$l* zxK7Urz0$6*vkspynEW?fQ=z2B`Q0-H(dL;%rFbaGpt*0xR^m2wat1exzlc7bxDjNP z$>3foO_YijP4;kv6re?GM29Z;^PuyAD#!cehIKg=Yie51lZn-!4}{iky(LH^C)Er@ENTi&{w71&msI9QLt8T literal 0 HcmV?d00001 diff --git a/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/UpdatedSubscription.class b/applications/answers/target/classes/pt/ulisboa/tecnico/socialsoftware/answers/microservices/user/events/subscribe/UpdatedSubscription.class new file mode 100644 index 0000000000000000000000000000000000000000..5ee1a74ec6c1081c192151fcd21b086ef51b88fb GIT binary patch literal 2894 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 M7=OlJ@pml#4_kY<00000 literal 0 HcmV?d00001 diff --git a/applications/quizzes/src/main/java/pt/ulisboa/tecnico/socialsoftware/quizzes/microservices/tournament/aggregate/Tournament.java b/applications/quizzes/src/main/java/pt/ulisboa/tecnico/socialsoftware/quizzes/microservices/tournament/aggregate/Tournament.java index 61e7c71c7..76276f1c7 100644 --- a/applications/quizzes/src/main/java/pt/ulisboa/tecnico/socialsoftware/quizzes/microservices/tournament/aggregate/Tournament.java +++ b/applications/quizzes/src/main/java/pt/ulisboa/tecnico/socialsoftware/quizzes/microservices/tournament/aggregate/Tournament.java @@ -78,26 +78,19 @@ public abstract class Tournament extends Aggregate { private LocalDateTime endTime; private Integer numberOfQuestions; private boolean cancelled; - /* - CREATOR_IS_FINAL - final this.creator.id - */ + @OneToOne(cascade = CascadeType.ALL, mappedBy = "tournament") private TournamentCreator tournamentCreator; + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "tournament") private Set tournamentParticipants = new HashSet<>(); - /* - COURSE_EXECUTION_IS_FINAL - final this.courseExecution.id - */ + @OneToOne(cascade = CascadeType.ALL, mappedBy = "tournament") private TournamentCourseExecution tournamentCourseExecution; + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "tournament") private Set tournamentTopics = new HashSet<>(); - /* - QUIZ_IS_FINAL - final this.tournamentQuiz.id - */ + @OneToOne(cascade = CascadeType.ALL, mappedBy = "tournament") private TournamentQuiz tournamentQuiz; @@ -124,20 +117,16 @@ public Tournament(Integer aggregateId, TournamentDto tournamentDto, UserDto crea setTournamentQuiz(new TournamentQuiz(quizDto.getAggregateId(), quizDto.getVersion())); } - /* used to update the tournament by creating new versions */ public Tournament(Tournament other) { super(other); setStartTime(other.getStartTime()); setEndTime(other.getEndTime()); setNumberOfQuestions(other.getNumberOfQuestions()); setCancelled(other.isCancelled()); - setTournamentCreator(new TournamentCreator(other.getTournamentCreator())); setTournamentCourseExecution(new TournamentCourseExecution(other.getTournamentCourseExecution())); setTournamentTopics(other.getTournamentTopics().stream().map(TournamentTopic::new).collect(Collectors.toSet())); - setTournamentQuiz(new TournamentQuiz(other.getTournamentQuiz())); - setTournamentParticipants(other.getTournamentParticipants().stream().map(TournamentParticipant::new).collect(Collectors.toSet())); } From 4dbc15512242fbc82b90b79d82e4c751b9ffd219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louren=C3=A7o=20Ponces=20Duarte?= Date: Tue, 9 Sep 2025 17:11:26 +0100 Subject: [PATCH 002/194] Added abstractions --- dsl/abstractions/answers/answer.nebula | 112 +++++++++ dsl/abstractions/answers/course.nebula | 50 ++++ dsl/abstractions/answers/exceptions.nebula | 98 ++++++++ dsl/abstractions/answers/execution.nebula | 88 +++++++ dsl/abstractions/answers/question.nebula | 99 ++++++++ dsl/abstractions/answers/quiz.nebula | 93 ++++++++ dsl/abstractions/answers/topic.nebula | 55 +++++ dsl/abstractions/answers/tournament.nebula | 257 +++++++++++++++++++++ dsl/abstractions/answers/user.nebula | 11 + 9 files changed, 863 insertions(+) create mode 100644 dsl/abstractions/answers/answer.nebula create mode 100644 dsl/abstractions/answers/course.nebula create mode 100644 dsl/abstractions/answers/exceptions.nebula create mode 100644 dsl/abstractions/answers/execution.nebula create mode 100644 dsl/abstractions/answers/question.nebula create mode 100644 dsl/abstractions/answers/quiz.nebula create mode 100644 dsl/abstractions/answers/topic.nebula create mode 100644 dsl/abstractions/answers/tournament.nebula create mode 100644 dsl/abstractions/answers/user.nebula diff --git a/dsl/abstractions/answers/answer.nebula b/dsl/abstractions/answers/answer.nebula new file mode 100644 index 000000000..cd6bcfd85 --- /dev/null +++ b/dsl/abstractions/answers/answer.nebula @@ -0,0 +1,112 @@ +import User; + +@Metadata { + requiresUserDto; + requiresAggregateState; + hasEventService; + hasTransactions; + hasJpa; +} +Aggregate Answer { + Entity QuizAnswerStudent { + Integer studentAggregateId; + String studentName; + String studentUsername; + String studentEmail; + } + + Entity QuizAnswerCourseExecution { + Integer courseExecutionAggregateId; + String courseExecutionName; + String courseExecutionAcronym; + String courseExecutionAcademicTerm; + } + + Entity QuestionAnswer { + Integer questionId; + String answer; + String option; + LocalDateTime answerDate; + } + + Entity AnsweredQuiz { + Integer quizAggregateId; + String quizTitle; + String quizType; + LocalDateTime availableDate; + LocalDateTime conclusionDate; + Integer numberOfQuestions; + } + + Root Entity QuizAnswer { + LocalDateTime answerDate; + LocalDateTime completedDate; + Boolean completed; + QuizAnswerStudent quizAnswerStudent; + QuizAnswerCourseExecution quizAnswerCourseExecution; + Set questionAnswers; + AnsweredQuiz answeredQuiz; + + invariants { + check answerDateBeforeCompletedDate { + completedDate == null || answerDate.isBefore(completedDate); + } + + check completedWhenCompletedDate { + completed == (completedDate != null); + } + + check uniqueQuestionAnswers { + questionAnswers.unique(questionId); + } + } + + businessRules { + rule "AUTO_COMPLETE_AFTER_END" { + trigger: completedDate; + affectedFields: [completed]; + conditions: [ + "completedDate != null" + ]; + exception: "CANNOT_COMPLETE_QUIZ_ANSWER"; + } + } + + methods { + method createQuizAnswer(QuizAnswerStudent student, QuizAnswerCourseExecution courseExecution, AnsweredQuiz quiz, UnitOfWork unitOfWork): QuizAnswer; + method getQuizAnswerById(Integer quizAnswerId, UnitOfWork unitOfWork): QuizAnswer; + method getQuizAnswersByStudent(Integer studentId, UnitOfWork unitOfWork): List; + method getQuizAnswersByQuiz(Integer quizId, UnitOfWork unitOfWork): List; + method submitAnswer(Integer quizAnswerId, Integer questionId, String answer, UnitOfWork unitOfWork): QuizAnswer; + method completeQuizAnswer(Integer quizAnswerId, UnitOfWork unitOfWork): QuizAnswer; + } + } + + // Service methods + method getQuizAnswersByCourseExecution(Integer courseExecutionId, UnitOfWork unitOfWork): List; + method getQuizAnswersByStudentAndCourseExecution(Integer studentId, Integer courseExecutionId, UnitOfWork unitOfWork): List; + method getQuizAnswersByQuizAndCourseExecution(Integer quizId, Integer courseExecutionId, UnitOfWork unitOfWork): List; + + // Event processing workflows + saga workflow anonymizeStudent(Integer studentAggregateId, Integer quizAnswerId, UnitOfWork unitOfWork); + saga workflow invalidateQuiz(Integer quizAggregateId, Integer quizAnswerId, UnitOfWork unitOfWork); + saga workflow removeCourseExecution(Integer courseExecutionAggregateId, Integer quizAnswerId, UnitOfWork unitOfWork); + saga workflow unenrollStudentFromCourseExecution(Integer studentAggregateId, Integer courseExecutionAggregateId, Integer quizAnswerId, UnitOfWork unitOfWork); + saga workflow updateStudentName(Integer studentAggregateId, String studentName, String studentUsername, Integer quizAnswerId, UnitOfWork unitOfWork); + + CustomRepository { + Optional findQuizAnswerIdByQuizIdAndUserId(Integer quizAggregateId, Integer studentAggregateId); + } + + Service AnswerService { + @GenerateCrud; + @Transactional; + methods { + createQuizAnswer(QuizAnswer answer, UserDto user): QuizAnswer; + findQuizAnswerById(Integer id): QuizAnswer; + updateQuizAnswerState(Integer id, AggregateState state): QuizAnswer; + deleteQuizAnswer(Integer id): void; + findQuizAnswersByStudent(Integer studentId): List; + } + } +} diff --git a/dsl/abstractions/answers/course.nebula b/dsl/abstractions/answers/course.nebula new file mode 100644 index 000000000..a17733c25 --- /dev/null +++ b/dsl/abstractions/answers/course.nebula @@ -0,0 +1,50 @@ +Aggregate Course { + Root Entity Course { + String name; + String acronym; + String courseType; + LocalDateTime creationDate; + + invariants { + check nameNotEmpty { + name.length() > 0; + } + + check acronymNotEmpty { + acronym.length() > 0; + } + + check creationDateInPast { + creationDate != null; + } + } + + businessRules { + rule "AUTO_SET_CREATION_DATE" { + trigger: creationDate; + affectedFields: [creationDate]; + conditions: [ + "creationDate == null" + ]; + exception: "CANNOT_SET_CREATION_DATE"; + } + } + + methods { + method createCourse(String name, String acronym, String courseType, UnitOfWork unitOfWork): Course; + method getCourseById(Integer courseId, UnitOfWork unitOfWork): Course; + method getAllCourses(UnitOfWork unitOfWork): List; + method updateCourse(Integer courseId, String name, String acronym, String courseType, UnitOfWork unitOfWork): Course; + method deleteCourse(Integer courseId, UnitOfWork unitOfWork): void; + } + } + + // Service methods + method getCoursesByType(String courseType, UnitOfWork unitOfWork): List; + method searchCoursesByName(String name, UnitOfWork unitOfWork): List; + method searchCoursesByAcronym(String acronym, UnitOfWork unitOfWork): List; + + CustomRepository { + Optional findCourseIdByName(String courseName); + } +} diff --git a/dsl/abstractions/answers/exceptions.nebula b/dsl/abstractions/answers/exceptions.nebula new file mode 100644 index 000000000..f26fa7f25 --- /dev/null +++ b/dsl/abstractions/answers/exceptions.nebula @@ -0,0 +1,98 @@ +// Exception messages for the project +exceptions { + // General system messages + UNDEFINED_TRANSACTIONAL_MODEL: "Undefined transactional model"; + AGGREGATE_BEING_USED_IN_OTHER_SAGA: "Aggregate is being used in %s saga"; + INVALID_AGGREGATE_TYPE: "Aggregate type %s does not exist"; + AGGREGATE_DELETED: "Aggregate %s with aggregate id %d already deleted."; + AGGREGATE_NOT_FOUND: "Aggregate with aggregate id %d does not exist."; + VERSION_MANAGER_DOES_NOT_EXIST: "Version manager does not exist."; + AGGREGATE_MERGE_FAILURE: "Two versions of aggregate %d cannot be merged."; + AGGREGATE_MERGE_FAILURE_DUE_TO_INTENSIONS_CONFLICT: "Two versions of aggregate cannot be merged due to intensions conflict: %s"; + + // Tournament specific messages + TOURNAMENT_NOT_FOUND: "Tournament with aggregate Id %d does not exist."; + TOURNAMENT_INVALID: "Tournament version with aggregate id %d and version %d breaks invariants."; + TOURNAMENT_MISSING_USER: "Tournament requires a user."; + TOURNAMENT_MISSING_TOPICS: "Tournament requires topics."; + TOURNAMENT_MISSING_START_TIME: "Tournament requires a start time."; + TOURNAMENT_MISSING_END_TIME: "Tournament requires an end time."; + TOURNAMENT_MISSING_NUMBER_OF_QUESTIONS: "Tournament requires a number of questions."; + TOURNAMENT_DELETED: "Tournament with aggregate id %d already deleted."; + TOURNAMENT_PARTICIPANT_NOT_FOUND: "User %d is not enrolled in tournament %d"; + TOURNAMENT_TOPIC_NOT_FOUND: "Topic %d is not part of tournament %d."; + CANNOT_UPDATE_TOURNAMENT: "Tournament %d cannot be updated."; + CANNOT_DELETE_TOURNAMENT: "Tournament %d cannot be deleted."; + CANNOT_ADD_PARTICIPANT: "Cannot add participant to tournament %d after it has started."; + PARTICIPANT_NOT_STUDENT: "User %d must be a student to be added as participant to tournament %d."; + PARTICIPANT_NOT_ENROLLED_IN_TOURNAMENT_EXECUTION: "User %d not enrolled in tournament's %d course execution."; + TOURNAMENT_PARTICIPANT_ADDING_ANSWER_WITH_WRONG_QUIZ_ANSWER_ID: "Tournament participant is being added a wrong quiz answer id %d"; + TOURNAMENT_IN_SAGA: "Tournament is already in a saga."; + + // Course execution messages + COURSE_EXECUTION_STUDENT_ALREADY_ENROLLED: "Student with aggregate id %d is already enrolled in course execution %d."; + COURSE_EXECUTION_NOT_FOUND: "Course execution with aggregate id %d does not exist."; + COURSE_EXECUTION_DELETED: "Course execution with aggregate id %d already deleted."; + COURSE_EXECUTION_MISSING_COURSE_ID: "Course execution requires a course id."; + COURSE_EXECUTION_MISSING_ACRONYM: "Course execution requires an acronym."; + COURSE_EXECUTION_MISSING_ACADEMIC_TERM: "Course execution requires an academic term."; + COURSE_EXECUTION_MISSING_END_DATE: "Course execution requires an end date."; + COURSE_EXECUTION_INVALID: "Course execution aggregate id %d and version %d breaks invariants."; + CANNOT_DELETE_COURSE_EXECUTION: "Cannot delete course execution with aggregate id %d."; + COURSE_EXECUTION_STUDENT_NOT_FOUND: "Student with aggregate id %d not found in course execution %d."; + + // User messages + USER_IS_ANONYMOUS: "Cant add anonymous user %d."; + CREATOR_IS_ANONYMOUS: "Cant add user %d because creator is anonymous."; + USER_MISSING_NAME: "User requires a name."; + USER_MISSING_USERNAME: "User requires an username."; + USER_MISSING_ROLE: "User requires a role."; + USER_NOT_FOUND: "User with aggregate id %d does not exist."; + USER_DELETED: "User with aggregate id %d alreadt deleted."; + INACTIVE_USER: "Cannot add course execution to inactive user."; + USER_ACTIVE: "User %d is already active."; + USER_MERGE_FAILURE: "Two versions of a user with aggregate id %d cannot be merged."; + + // Quiz messages + QUIZ_NOT_FOUND: "Quiz with aggregate Id %d does not exist."; + QUIZ_DELETED: "Quiz with aggregate id %d already deleted."; + NOT_ENOUGH_QUESTIONS: "Not enough questions to generate quiz."; + QUIZ_MERGE_FAILURE: "Two versions of a quiz with aggregate id %d cannot be merged."; + CANNOT_UPDATE_QUIZ: "Quiz %d cannot be deleted."; + QUIZ_DOES_NOT_BELONG_TO_COURSE_EXECUTION: "Quiz %d does not belong to course execution %d."; + + // Topic messages + TOPIC_MISSING_NAME: "Topic requires a name."; + TOPIC_MISSING_COURSE: "Topic requires a course."; + TOPIC_NOT_FOUND: "Topic with aggregate id %d not found."; + TOPIC_DELETED: "Topic with aggregate id %d already deleted."; + + // Course messages + COURSE_MISSING_TYPE: "Course requires a type."; + COURSE_MISSING_NAME: "Course requires a name."; + COURSE_NOT_FOUND: "Course with aggregate id %d not found."; + COURSE_DELETED: "Course with aggregate id %d already deleted."; + COURSE_INVALID: "Course version with aggregate id %d and version %d breaks invariants."; + + // Question messages + QUESTION_NOT_FOUND: "Question with aggregate id %d does no exist."; + QUESTION_DELETED: "Question with aggregate id %d already deleted."; + QUESTION_TOPIC_INVALID_COURSE: "Topic %d does not belong to course %d."; + QUESTION_ALREADY_ANSWERED: "Question %d of quiz %d already answered."; + INVALID_OPTION_SELECTED: "Invalid option %d for question %d."; + + // Quiz answer messages + QUIZ_ANSWER_NOT_FOUND: "Answer with aggregate id %d not found."; + NO_USER_ANSWER_FOR_QUIZ: "Answer for user aggregate id %d and quiz aggregate id %d not found."; + QUIZ_ANSWER_DELETED: "Answer with aggregate id %d already deleted."; + + // Causal consistency messages + CANNOT_PERFORM_CAUSAL_READ: "Cannot causally read object with aggregate id %d."; + CANNOT_PERFORM_CAUSAL_READ_DUE_TO_EMITTED_EVENT_NOT_PROCESSED: "Cannot causally read object of class %s to causal snapshot because emitted event %s was not processed"; + INVALID_PREV: "Prev does not match the type of the aggregate."; + NO_PRIMARY_AGGREGATE_FOUND: "No primary aggregate was found within the transactional context."; + TOO_MANY_PRIMARY_AGGREGATE_FOUND: "More than one primary aggregates were found within the transactional context"; + INVARIANT_BREAK: "Aggregate %d breaks invariants"; + INVALID_EVENT_TYPE: "Invalid event type %s."; + CANNOT_MODIFY_INACTIVE_AGGREGATE: "Cannot update aggregate %d because it is INACTIVE."; +} \ No newline at end of file diff --git a/dsl/abstractions/answers/execution.nebula b/dsl/abstractions/answers/execution.nebula new file mode 100644 index 000000000..4e0acbf9b --- /dev/null +++ b/dsl/abstractions/answers/execution.nebula @@ -0,0 +1,88 @@ +import User; + +Aggregate CourseExecution { + Entity CourseExecutionCourse { + Integer courseAggregateId; + String courseName; + String courseAcronym; + String courseType; + } + + Entity CourseExecutionStudent { + Integer studentAggregateId; + String studentName; + String studentUsername; + String studentEmail; + LocalDateTime enrollmentDate; + } + + Root Entity CourseExecution { + String name; + String acronym; + String academicTerm; + LocalDateTime startDate; + LocalDateTime endDate; + CourseExecutionCourse course; + Set students; + + invariants { + check startDateBeforeEndDate { + startDate.isBefore(endDate); + } + + check nameNotEmpty { + name.length() > 0; + } + + check acronymNotEmpty { + acronym.length() > 0; + } + + check academicTermNotEmpty { + academicTerm.length() > 0; + } + + check uniqueStudents { + students.unique(studentAggregateId); + } + } + + businessRules { + rule "AUTO_ENROLL_STUDENT" { + trigger: students; + affectedFields: [students]; + conditions: [ + "students.size() > 0" + ]; + exception: "CANNOT_ENROLL_STUDENT"; + } + } + + methods { + method createCourseExecution(String name, String acronym, String academicTerm, LocalDateTime startDate, LocalDateTime endDate, CourseExecutionCourse course, UnitOfWork unitOfWork): void; + method getCourseExecutionById(Integer courseExecutionId, UnitOfWork unitOfWork): void; + method getAllCourseExecutions(UnitOfWork unitOfWork): void; + method getCourseExecutionsByCourse(Integer courseId, UnitOfWork unitOfWork): void; + method enrollStudent(Integer courseExecutionId, Integer studentId, UnitOfWork unitOfWork): void; + method unenrollStudent(Integer courseExecutionId, Integer studentId, UnitOfWork unitOfWork): void; + method updateCourseExecution(Integer courseExecutionId, String name, String acronym, String academicTerm, LocalDateTime startDate, LocalDateTime endDate, UnitOfWork unitOfWork): void; + method deleteCourseExecution(Integer courseExecutionId, UnitOfWork unitOfWork): void; + } + } + + // Service methods + method getCourseExecutionsByStudent(Integer studentId, UnitOfWork unitOfWork): void; + method getActiveCourseExecutions(UnitOfWork unitOfWork): void; + method getCourseExecutionsByAcademicTerm(String academicTerm, UnitOfWork unitOfWork): void; + + // Event processing workflows + saga workflow removeUser(Integer userAggregateId, Integer courseExecutionId, UnitOfWork unitOfWork); + saga workflow anonymizeStudent(Integer studentAggregateId, Integer courseExecutionId, UnitOfWork unitOfWork); + saga workflow updateStudentName(Integer studentAggregateId, String studentName, String studentUsername, Integer courseExecutionId, UnitOfWork unitOfWork); + saga workflow deleteCourseExecution(Integer courseExecutionId, UnitOfWork unitOfWork); + saga workflow disenrollStudentFromCourseExecution(Integer studentAggregateId, Integer courseExecutionId, UnitOfWork unitOfWork); + + CustomRepository { + Optional findCourseExecutionIdByCourseIdAndAcademicTerm(Integer courseId, String academicTerm); + } +} diff --git a/dsl/abstractions/answers/question.nebula b/dsl/abstractions/answers/question.nebula new file mode 100644 index 000000000..c4013ad45 --- /dev/null +++ b/dsl/abstractions/answers/question.nebula @@ -0,0 +1,99 @@ +Aggregate Question { + Entity QuestionCourse { + Integer courseAggregateId; + String courseName; + String courseAcronym; + } + + Entity QuestionTopic { + Integer topicId; + String topicName; + } + + Entity Option { + Integer optionNumber; + String content; + Boolean isCorrect; + } + + Root Entity Question { + String title; + String content; + Integer numberOfOptions; + Integer correctOption; + Integer order; + QuestionCourse course; + Set topics; + Set