diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 330cc57..ea00849 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -28,6 +28,7 @@ jobs: env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} uses: gradle/gradle-build-action@v3 + with: arguments: cleanTest test jacocoTestReport sonar diff --git a/build.gradle b/build.gradle index 50d2c6b..772e0fb 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.+' + + id 'org.springframework.boot' version '3.2.+' id 'io.spring.dependency-management' version '1.+' id "io.github.kobylynskyi.graphql.codegen" version "5.+" id "org.sonarqube" version "5.+" @@ -21,6 +22,7 @@ if (jacocoEnabled.toBoolean()) { } sonarqube { + properties { property("sonar.projectKey", "MEITREX_flashcard_service") property("sonar.organization", "meitrex") @@ -108,7 +110,8 @@ repositories { } dependencies { - implementation 'de.unistuttgart.iste.meitrex:meitrex-common:1.0.6' + implementation 'de.unistuttgart.iste.meitrex:meitrex-common:1.2' + implementation 'com.google.code.findbugs:jsr305:3.0.2' // removes a gradle warning about an unknown annotation implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-graphql' implementation 'org.springframework.boot:spring-boot-starter-validation' @@ -124,7 +127,7 @@ dependencies { runtimeOnly 'org.postgresql:postgresql' annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' annotationProcessor 'org.projectlombok:lombok' - testImplementation 'de.unistuttgart.iste.meitrex:meitrex-common-test:1.0.6' + testImplementation 'de.unistuttgart.iste.meitrex:meitrex-common-test:1.2' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework:spring-webflux' testImplementation 'org.springframework.graphql:spring-graphql-test' @@ -137,3 +140,6 @@ dependencies { tasks.named('test') { useJUnitPlatform() } + +tasks.withType(Test).configureEach { testLogging.showStandardStreams = true } + diff --git a/settings.gradle b/settings.gradle index aa4f16f..e6dcd44 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,5 +6,6 @@ sourceControl { } gitRepository(uri('https://github.com/MEITREX/common_test')) { producesModule('de.unistuttgart.iste.meitrex:meitrex-common-test') + } } \ No newline at end of file diff --git a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/FlashcardServiceApplication.java b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/FlashcardServiceApplication.java index 3b894e1..e4cd13f 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/FlashcardServiceApplication.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/FlashcardServiceApplication.java @@ -4,7 +4,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; - /** * This is the entry point of the application. */ diff --git a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/controller/FlashcardController.java b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/controller/FlashcardController.java index 226a5c8..6cad2ed 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/controller/FlashcardController.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/controller/FlashcardController.java @@ -1,5 +1,8 @@ package de.unistuttgart.iste.meitrex.flashcard_service.controller; +import de.unistuttgart.iste.meitrex.generated.dto.Flashcard; +import de.unistuttgart.iste.meitrex.generated.dto.FlashcardSet; + import de.unistuttgart.iste.meitrex.common.exception.NoAccessToCourseException; import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser; import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser.UserRoleInCourse; @@ -28,7 +31,7 @@ public class FlashcardController { private final FlashcardUserProgressDataService progressDataService; @QueryMapping - public List flashcardsByIds(@Argument(name = "ids") final List ids, + public List flashcardsByIds(@Argument(name = "itemIds") final List ids, @ContextValue final LoggedInUser currentUser) { final List courseIds = flashcardService.getCourseIdsForFlashcardIds(ids); @@ -67,7 +70,7 @@ public List dueFlashcardsByCourseId(@Argument final UUID courseId, @SchemaMapping(typeName = "Flashcard", field = "userProgressData") public FlashcardProgressData flashcardUserProgressData(final Flashcard flashcard, @ContextValue final LoggedInUser currentUser) { - return progressDataService.getProgressData(flashcard.getId(), currentUser.getId()); + return progressDataService.getProgressData(flashcard.getItemId(), currentUser.getId()); } @MutationMapping @@ -82,11 +85,16 @@ public FlashcardSetMutation mutateFlashcardSet(@Argument final UUID assessmentId } @SchemaMapping(typeName = "FlashcardSetMutation") - public Flashcard createFlashcard(@Argument(name = "input") final CreateFlashcardInput input, - final FlashcardSetMutation mutation) { + public Flashcard _internal_noauth_createFlashcard(@Argument(name = "input") final CreateFlashcardInput input, + final FlashcardSetMutation mutation) { return flashcardService.createFlashcard(mutation.getAssessmentId(), input); } + @SchemaMapping(typeName = "FlashcardSetMutation") + public Flashcard _internal_noauth_updateFlashcard(@Argument(name = "input") final UpdateFlashcardInput input) { + return flashcardService.updateFlashcard(input); + } + @SchemaMapping(typeName = "FlashcardSetMutation") public Flashcard updateFlashcard(@Argument(name = "input") final UpdateFlashcardInput input) { return flashcardService.updateFlashcard(input); diff --git a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/controller/SubscriptionController.java b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/controller/SubscriptionController.java index 267b32a..42d3931 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/controller/SubscriptionController.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/controller/SubscriptionController.java @@ -21,7 +21,7 @@ public class SubscriptionController { private final FlashcardService flashcardService; - @Topic(name = "content-changed", pubsubName = "gits") + @Topic(name = "content-changed", pubsubName = "meitrex") @PostMapping(path = "/flashcard-service/content-changed-pubsub") public Mono updateAssociation(@RequestBody CloudEvent cloudEvent) { diff --git a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/entity/FlashcardEntity.java b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/entity/FlashcardEntity.java index 1197e3c..202537e 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/entity/FlashcardEntity.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/entity/FlashcardEntity.java @@ -14,8 +14,7 @@ public class FlashcardEntity { @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private UUID id; + private UUID itemId; @OneToMany(mappedBy = "flashcard", cascade = CascadeType.ALL, orphanRemoval = true) private List sides; diff --git a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/entity/FlashcardProgressDataEntity.java b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/entity/FlashcardProgressDataEntity.java index 76d51c1..da8ba0f 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/entity/FlashcardProgressDataEntity.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/entity/FlashcardProgressDataEntity.java @@ -1,10 +1,7 @@ package de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.*; import java.io.Serializable; import java.time.OffsetDateTime; @@ -14,6 +11,7 @@ @Entity(name = "FlashcardProgressData") @Data @Builder +@Getter @NoArgsConstructor @AllArgsConstructor public class FlashcardProgressDataEntity { diff --git a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/entity/FlashcardProgressDataLogEntity.java b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/entity/FlashcardProgressDataLogEntity.java index 404fe95..16a92eb 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/entity/FlashcardProgressDataLogEntity.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/entity/FlashcardProgressDataLogEntity.java @@ -8,6 +8,9 @@ @Entity(name = "FlashcardProgressDataLog") @Data +@Builder +@Getter +@Setter @NoArgsConstructor @AllArgsConstructor public class FlashcardProgressDataLogEntity { @@ -30,4 +33,8 @@ public class FlashcardProgressDataLogEntity { @EqualsAndHashCode.Exclude @ToString.Exclude private FlashcardProgressDataEntity flashcardProgressData; + + public boolean getSuccess() { + return success; + } } diff --git a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/mapper/FlashcardMapper.java b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/mapper/FlashcardMapper.java index 4122c31..9a0af75 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/mapper/FlashcardMapper.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/mapper/FlashcardMapper.java @@ -1,8 +1,8 @@ package de.unistuttgart.iste.meitrex.flashcard_service.persistence.mapper; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardEntity; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSetEntity; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSideEntity; + +import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.*; + import de.unistuttgart.iste.meitrex.generated.dto.*; import org.modelmapper.ModelMapper; import org.springframework.stereotype.Component; diff --git a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/repository/FlashcardRepository.java b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/repository/FlashcardRepository.java index c6d9b02..37a7727 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/repository/FlashcardRepository.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/repository/FlashcardRepository.java @@ -6,6 +6,7 @@ import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; import java.util.UUID; @@ -14,7 +15,8 @@ */ @Repository public interface FlashcardRepository extends JpaRepository, JpaSpecificationExecutor { - List findByIdIn(List ids); + List findByItemIdIn(List ids); + Optional findByItemId(UUID itemId); } diff --git a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/repository/FlashcardSetRepository.java b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/repository/FlashcardSetRepository.java index 1580426..43da1da 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/repository/FlashcardSetRepository.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/persistence/repository/FlashcardSetRepository.java @@ -14,6 +14,7 @@ public interface FlashcardSetRepository extends MeitrexRepository, JpaSpecificationExecutor { diff --git a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/service/FlashcardService.java b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/service/FlashcardService.java index d244386..9465089 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/service/FlashcardService.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/service/FlashcardService.java @@ -3,6 +3,9 @@ import de.unistuttgart.iste.meitrex.common.event.ContentChangeEvent; import de.unistuttgart.iste.meitrex.common.event.CrudOperation; import de.unistuttgart.iste.meitrex.common.exception.IncompleteEventMessageException; + +import de.unistuttgart.iste.meitrex.common.dapr.TopicPublisher; + import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardEntity; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSetEntity; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.mapper.FlashcardMapper; @@ -17,6 +20,7 @@ import org.springframework.stereotype.Service; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; @@ -31,6 +35,8 @@ public class FlashcardService { private final FlashcardMapper flashcardMapper; private final FlashcardValidator flashcardValidator; + private final TopicPublisher topicPublisher; + public Flashcard createFlashcard(final UUID assessmentId, final CreateFlashcardInput flashcardInput) { flashcardValidator.validateCreateFlashcardInput(flashcardInput); @@ -50,8 +56,8 @@ public Flashcard createFlashcard(final UUID assessmentId, final CreateFlashcardI public Flashcard updateFlashcard(final UpdateFlashcardInput input) { flashcardValidator.validateUpdateFlashcardInput(input); - final FlashcardEntity oldFlashcard = flashcardRepository.findById(input.getId()) - .orElseThrow(() -> new EntityNotFoundException("Flashcard with id %s not found.".formatted(input.getId()))); + final FlashcardEntity oldFlashcard = flashcardRepository.findById(input.getItemId()) + .orElseThrow(() -> new EntityNotFoundException("Flashcard with id %s not found.".formatted(input.getItemId()))); FlashcardEntity updatedFlashcard = flashcardMapper.dtoToEntity(input); updatedFlashcard.setParentSet(oldFlashcard.getParentSet()); @@ -63,10 +69,11 @@ public Flashcard updateFlashcard(final UpdateFlashcardInput input) { public UUID deleteFlashcard(final UUID assessmentId, final UUID flashcardId) { final FlashcardSetEntity set = requireFlashcardSetExisting(assessmentId); - if (!set.getFlashcards().removeIf(x -> x.getId().equals(flashcardId))) { + if (!set.getFlashcards().removeIf(x -> x.getItemId().equals(flashcardId))) { throw new EntityNotFoundException("Flashcard with id %s not found.".formatted(flashcardId)); } flashcardSetRepository.save(set); + publishItemChangeEvent(flashcardId); return flashcardId; } @@ -82,6 +89,7 @@ public FlashcardSet createFlashcardSet(final UUID courseId, final UUID assessmen public UUID deleteFlashcardSet(final UUID uuid) { requireFlashcardSetExisting(uuid); + publishDeletedFlashcardSet(uuid); flashcardSetRepository.deleteById(uuid); return uuid; } @@ -92,10 +100,10 @@ public FlashcardSetEntity requireFlashcardSetExisting(final UUID uuid) { } public List getFlashcardsByIds(final List ids) { - final List entities = flashcardRepository.findByIdIn(ids); + final List entities = flashcardRepository.findByItemIdIn(ids); - ids.removeAll(entities.stream().map(FlashcardEntity::getId).toList()); - if(!ids.isEmpty()) { + ids.removeAll(entities.stream().map(FlashcardEntity::getItemId).toList()); + if (!ids.isEmpty()) { throw new EntityNotFoundException("Flashcards with ids " + ids.stream().map(UUID::toString).collect(Collectors.joining(", ")) + " not found."); @@ -148,6 +156,7 @@ public void deleteFlashcardSetIfContentIsDeleted(final ContentChangeEvent dto) t flashcardSetRepository.deleteAllById(dto.getContentIds()); } + /** * helper function to make sure received event message is complete * @@ -159,4 +168,28 @@ private void checkCompletenessOfDto(final ContentChangeEvent dto) throws Incompl throw new IncompleteEventMessageException(IncompleteEventMessageException.ERROR_INCOMPLETE_MESSAGE); } } + + /*** + * helper function, that creates a ItemChange Event and publish it, when a flashcard was deleted + * @param itemId the id of the item + */ + private void publishItemChangeEvent(final UUID itemId) { + topicPublisher.notifyItemChanges(itemId, CrudOperation.DELETE); + + } + + /** + * for each flashcard of the deleted flashcard set publish a itemchanged event + * + * @param flashcardSetId the id of the flashcardset to delete + */ + private void publishDeletedFlashcardSet(UUID flashcardSetId) { + Optional flashcardSetOptional = flashcardSetRepository.findById(flashcardSetId); + if(flashcardSetOptional.isPresent()){ + FlashcardSetEntity flashcardSet=flashcardSetOptional.get(); + for (FlashcardEntity flashcard : flashcardSet.getFlashcards()) { + publishItemChangeEvent(flashcard.getItemId()); + } + } + } } diff --git a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/service/FlashcardUserProgressDataService.java b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/service/FlashcardUserProgressDataService.java index 31aeaa9..86b90f9 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/service/FlashcardUserProgressDataService.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/service/FlashcardUserProgressDataService.java @@ -2,15 +2,11 @@ import de.unistuttgart.iste.meitrex.common.dapr.TopicPublisher; import de.unistuttgart.iste.meitrex.common.event.ContentProgressedEvent; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardEntity; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardProgressDataEntity; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardProgressDataLogEntity; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSetEntity; +import de.unistuttgart.iste.meitrex.common.event.Response; +import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.*; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.mapper.FlashcardMapper; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardProgressDataLogRepository; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardProgressDataRepository; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardRepository; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardSetRepository; +import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.*; + import de.unistuttgart.iste.meitrex.generated.dto.*; import lombok.RequiredArgsConstructor; import org.modelmapper.ModelMapper; @@ -18,6 +14,7 @@ import java.time.OffsetDateTime; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.UUID; import java.util.stream.Stream; @@ -123,7 +120,7 @@ private Stream getDueFlashcardsOfFlashcardSet(final FlashcardSe final OffsetDateTime now = OffsetDateTime.now(); return flashcards.stream() .filter(flashcardEntity -> { - final var progressData = getProgressDataEntity(flashcardEntity.getId(), userId); + final var progressData = getProgressDataEntity(flashcardEntity.getItemId(), userId); if (progressData.getNextLearn() == null) { // if the flashcard has never been learned, it's not due for review return false; @@ -147,7 +144,7 @@ private FlashcardLearnedFeedback createFeedback( int correctlyLearnedFlashcards = 0; for (final FlashcardEntity flashcardEntity : flashcards) { - final FlashcardProgressDataEntity progressDataOfCurrentCard = getProgressDataEntity(flashcardEntity.getId(), userId); + final FlashcardProgressDataEntity progressDataOfCurrentCard = getProgressDataEntity(flashcardEntity.getItemId(), userId); if (wasNotLearnedInCurrentIteration(flashcardSetEntity, progressDataOfCurrentCard)) { numberOfFlashcardsNotLearnedInSet++; } else if (wasLearnedSuccessful(progressDataOfCurrentCard)) { @@ -166,16 +163,14 @@ private FlashcardLearnedFeedback createFeedback( } private static boolean wasLearnedSuccessful(final FlashcardProgressDataEntity progressData) { - return progressData.getFlashcardProgressDataLogs().stream() - .findFirst() - .map(FlashcardProgressDataLogEntity::isSuccess) - .orElse(false); + List progress = progressData.getFlashcardProgressDataLogs(); + return progress.get(0).getSuccess(); } private static boolean wasNotLearnedInCurrentIteration(final FlashcardSetEntity flashcardSetEntity, final FlashcardProgressDataEntity progressData) { return progressData.getLastLearned() == null - // if the flashcard was learned before the last time the set was learned, it was not learned in the current iteration - || progressData.getNextLearn().isBefore(flashcardSetEntity.getLastLearned().orElse(OffsetDateTime.MIN)); + // if the flashcard was learned before the last time the set was learned, it was not learned in the current iteration + || progressData.getNextLearn().isBefore(flashcardSetEntity.getLastLearned().orElse(OffsetDateTime.MIN)); } private void updateProgressDataEntity(final FlashcardProgressDataEntity progressData, final boolean success) { @@ -222,14 +217,20 @@ private void publishFlashcardSetLearned(final UUID userId, final UUID flashcardS if (dataLogEntities.size() < flashcardSetEntity.getFlashcards().size()) { // not all flashcards have been learned yet + return; } - + List responses = new ArrayList<>(); + for (FlashcardProgressDataLogEntity log : dataLogEntities) { + FlashcardProgressDataEntity progressDataEntity = log.getFlashcardProgressData(); + UUID flashcardID = progressDataEntity.getPrimaryKey().getFlashcardID(); + Response response = new Response(flashcardID, log.getSuccess() ? 1 : 0); + responses.add(response); + } final List dataLogs = dataLogEntities .stream() .map(this::mapLogEntityToDto) .toList(); - final int total = dataLogs.size(); final int correct = dataLogs.stream().mapToInt(log -> log.getSuccess() ? 1 : 0).sum(); @@ -237,11 +238,10 @@ private void publishFlashcardSetLearned(final UUID userId, final UUID flashcardS flashcardSetEntity.setLastLearned(OffsetDateTime.now()); flashcardSetRepository.save(flashcardSetEntity); - - publishUserProgressEvent(userId, flashcardSetId, correctness); + publishUserProgressEvent(userId, flashcardSetId, correctness, responses); } - private void publishUserProgressEvent(final UUID userId, final UUID assessmentId, final float correctness) { + private void publishUserProgressEvent(final UUID userId, final UUID assessmentId, final float correctness, List responses) { topicPublisher.notifyUserWorkedOnContent( ContentProgressedEvent.builder() .contentId(assessmentId) @@ -250,6 +250,7 @@ private void publishUserProgressEvent(final UUID userId, final UUID assessmentId .success(true) .timeToComplete(null) .correctness(correctness) + .responses(responses) .build() ); } diff --git a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/validation/FlashcardValidator.java b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/validation/FlashcardValidator.java index d3d389a..1c7e146 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/validation/FlashcardValidator.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/flashcard_service/validation/FlashcardValidator.java @@ -1,9 +1,8 @@ package de.unistuttgart.iste.meitrex.flashcard_service.validation; -import de.unistuttgart.iste.meitrex.generated.dto.CreateFlashcardInput; -import de.unistuttgart.iste.meitrex.generated.dto.CreateFlashcardSetInput; -import de.unistuttgart.iste.meitrex.generated.dto.FlashcardSideInput; -import de.unistuttgart.iste.meitrex.generated.dto.UpdateFlashcardInput; + +import de.unistuttgart.iste.meitrex.generated.dto.*; + import jakarta.validation.ValidationException; import org.springframework.stereotype.Component; @@ -14,6 +13,7 @@ public class FlashcardValidator { /** * validation function for newly created flashcard inputs + * * @param flashcardInput new flashcard input */ public void validateCreateFlashcardInput(CreateFlashcardInput flashcardInput) { @@ -22,10 +22,11 @@ public void validateCreateFlashcardInput(CreateFlashcardInput flashcardInput) { /** * validation of flashcard set inputs + * * @param flashcardSetInput a Flashcard-Set input */ public void validateCreateFlashcardSetInput(CreateFlashcardSetInput flashcardSetInput) { - for (CreateFlashcardInput flashcardInput: flashcardSetInput.getFlashcards()) { + for (CreateFlashcardInput flashcardInput : flashcardSetInput.getFlashcards()) { validateCreateFlashcardInput(flashcardInput); } } @@ -33,6 +34,7 @@ public void validateCreateFlashcardSetInput(CreateFlashcardSetInput flashcardSet /** * validation function for updated flashcard inputs + * * @param input an updated flashcard input */ public void validateUpdateFlashcardInput(UpdateFlashcardInput input) { @@ -41,23 +43,24 @@ public void validateUpdateFlashcardInput(UpdateFlashcardInput input) { /** * validates if flashcard has at least one side labeled as question and at least one side labeled as answer + * * @param flashcardSideInputs list of flashcard sides * @throws ValidationException if flashcard has no question or no answer or a side that is neither a question nor an answer */ private void validateSides(List flashcardSideInputs) { boolean hasQuestion = false; boolean hasAnswer = false; - for (FlashcardSideInput flashcardInputSide: flashcardSideInputs) { + for (FlashcardSideInput flashcardInputSide : flashcardSideInputs) { - if (flashcardInputSide.getIsQuestion()){ + if (flashcardInputSide.getIsQuestion()) { hasQuestion = true; } - if (flashcardInputSide.getIsAnswer()){ + if (flashcardInputSide.getIsAnswer()) { hasAnswer = true; } // finds invalid flashcard side - if ((!flashcardInputSide.getIsQuestion()) && (!flashcardInputSide.getIsAnswer())){ + if ((!flashcardInputSide.getIsQuestion()) && (!flashcardInputSide.getIsAnswer())) { throw new ValidationException("Flashcard side must must be at least a question or an answer"); } } diff --git a/src/main/resources/graphql/service/flashcards.graphqls b/src/main/resources/graphql/service/flashcards.graphqls index 82cf46f..bcc9967 100644 --- a/src/main/resources/graphql/service/flashcards.graphqls +++ b/src/main/resources/graphql/service/flashcards.graphqls @@ -5,9 +5,9 @@ and which sides he has to guess. """ type Flashcard { """ - Unique identifier of this flashcard. + Unique identifier of this flashcard, which is the id of the corresponding item """ - id: UUID! + itemId: UUID! """ List of sides of this flashcard. """ @@ -77,6 +77,10 @@ type FlashcardSide { input CreateFlashcardInput { + """ + id of the item the flashcard belongs to + """ + itemId:UUID """ List of sides of this flashcard. Must be at least two sides. """ @@ -104,9 +108,9 @@ input FlashcardSideInput { input UpdateFlashcardInput { """ - Id of the flashcard to update. + Id of the flashcard to update, which is the id of the corresponding item. """ - id: UUID! + itemId: UUID! """ List of sides of this flashcard. Must be at least two sides. """ diff --git a/src/main/resources/graphql/service/mutation.graphqls b/src/main/resources/graphql/service/mutation.graphqls index 97b1dcf..3432ce9 100644 --- a/src/main/resources/graphql/service/mutation.graphqls +++ b/src/main/resources/graphql/service/mutation.graphqls @@ -39,13 +39,17 @@ type FlashcardSetMutation { """ Creates a new flashcard. Throws an error if the flashcard set does not exist. + ⚠️ This mutation is only accessible internally in the system and allows the caller to create Flashcards without + any permissions check and should not be called without any validation of the caller's permissions. ⚠️ """ - createFlashcard(input: CreateFlashcardInput!): Flashcard! + _internal_noauth_createFlashcard(input: CreateFlashcardInput!): Flashcard! """ Updates a flashcard. Throws an error if the flashcard does not exist. + ⚠️ This mutation is only accessible internally in the system and allows the caller to update Flashcards without + any permissions check and should not be called without any validation of the caller's permissions. ⚠️ """ - updateFlashcard(input: UpdateFlashcardInput!): Flashcard! + _internal_noauth_updateFlashcard(input: UpdateFlashcardInput!): Flashcard! """ Deletes the flashcard with the specified ID. Throws an error if the flashcard does not exist. diff --git a/src/main/resources/graphql/service/query.graphqls b/src/main/resources/graphql/service/query.graphqls index 807b7ac..7938aa2 100644 --- a/src/main/resources/graphql/service/query.graphqls +++ b/src/main/resources/graphql/service/query.graphqls @@ -3,7 +3,7 @@ type Query { Get flashcards by their ids. 🔒 The user must be enrolled in the course the flashcards belong to. Otherwise an error is thrown. """ - flashcardsByIds(ids: [UUID!]!): [Flashcard!]! + flashcardsByIds(itemIds: [UUID!]!): [Flashcard!]! """ Get flashcard sets by their assessment ids. Returns a list of flashcard sets in the same order as the provided ids. diff --git a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationCreateFlashcardSetTest.java b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationCreateFlashcardSetTest.java index 7939ba5..7798c10 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationCreateFlashcardSetTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationCreateFlashcardSetTest.java @@ -2,9 +2,8 @@ import de.unistuttgart.iste.meitrex.common.testutil.GraphQlApiTest; import de.unistuttgart.iste.meitrex.common.testutil.TablesToDelete; -import de.unistuttgart.iste.meitrex.generated.dto.Flashcard; -import de.unistuttgart.iste.meitrex.generated.dto.FlashcardSet; -import de.unistuttgart.iste.meitrex.generated.dto.FlashcardSide; + +import de.unistuttgart.iste.meitrex.generated.dto.*; import jakarta.transaction.Transactional; import org.junit.jupiter.api.Test; import org.springframework.graphql.test.tester.GraphQlTester; @@ -27,64 +26,72 @@ class MutationCreateFlashcardSetTest { void testCreateFlashcardSet(final GraphQlTester tester) { final UUID assessmentId = UUID.randomUUID(); final UUID courseId = UUID.randomUUID(); + final UUID itemId1 = UUID.randomUUID(); + final UUID itemId2 = UUID.randomUUID(); final String query = - """ - mutation ($courseId: UUID!, $assessmentId: UUID!){ - _internal_noauth_createFlashcardSet(courseId: $courseId, assessmentId: $assessmentId, input: { - flashcards: [ - { - sides: [ - { - label: "Side 1", - isQuestion: true, - isAnswer: false, - text: "Question 1" - }, - { - label: "Side 2", - isQuestion: false, - isAnswer: true, - text: "Answer 1" - } - ] - }, - { - sides: [ - { - label: "Side 1", - isQuestion: true, - isAnswer: false, - text: "Question 2" - }, - { - label: "Side 2", - isQuestion: false, - isAnswer: true, - text: "Answer 2" + """ + mutation ($courseId: UUID!, $assessmentId: UUID!,$itemId1:UUID!,$itemId2:UUID!){ + _internal_noauth_createFlashcardSet(courseId: $courseId, assessmentId: $assessmentId, input: { + flashcards: [ + { + itemId:$itemId1, + sides: [ + { + label: "Side 1", + isQuestion: true, + isAnswer: false, + text: "Question 1" + }, + { + label: "Side 2", + isQuestion: false, + isAnswer: true, + text: "Answer 1" + } + ] + }, + { + itemId:$itemId2, + sides: [ + { + label: "Side 1", + isQuestion: true, + isAnswer: false, + text: "Question 2" + }, + { + label: "Side 2", + isQuestion: false, + isAnswer: true, + text: "Answer 2" + } + ] + } + ] } - ] + ) + { + assessmentId + courseId + flashcards { + itemId + sides { + label + isQuestion + isAnswer + text + } + } + } } - ] - } - ) - { - assessmentId - courseId - flashcards { - sides { - label - isQuestion - isAnswer - text - } - } - } - } - """; + """; + final FlashcardSet createdFlashcardSet = tester.document(query) .variable("assessmentId", assessmentId) .variable("courseId", courseId) + .variable("itemId1", itemId1) + .variable("itemId2", itemId2) .execute() .path("_internal_noauth_createFlashcardSet").entity(FlashcardSet.class).get(); @@ -98,6 +105,7 @@ void testCreateFlashcardSet(final GraphQlTester tester) { final Flashcard flashcard1 = flashcards.get(0); assertThat(flashcard1.getSides(), hasSize(2)); + assertThat(flashcard1.getItemId(), is(itemId1)); final FlashcardSide flashcard1Side1 = flashcard1.getSides().get(0); assertThat(flashcard1Side1.getLabel(), is("Side 1")); @@ -113,6 +121,7 @@ void testCreateFlashcardSet(final GraphQlTester tester) { final Flashcard flashcard2 = flashcards.get(1); assertThat(flashcard2.getSides(), hasSize(2)); + assertThat(flashcard2.getItemId(), is(itemId2)); final FlashcardSide flashcard2Side1 = flashcard2.getSides().get(0); assertThat(flashcard2Side1.getLabel(), is("Side 1")); diff --git a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationCreateFlashcardTest.java b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationCreateFlashcardTest.java index 2750635..72a5445 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationCreateFlashcardTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationCreateFlashcardTest.java @@ -4,9 +4,8 @@ import de.unistuttgart.iste.meitrex.common.testutil.InjectCurrentUserHeader; import de.unistuttgart.iste.meitrex.common.testutil.TablesToDelete; import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardEntity; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSetEntity; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSideEntity; +import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.*; + import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardRepository; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardSetRepository; import de.unistuttgart.iste.meitrex.flashcard_service.test_utils.TestUtils; @@ -21,8 +20,10 @@ import java.util.List; import java.util.UUID; -import static de.unistuttgart.iste.meitrex.common.testutil.TestUsers.userWithMembershipInCourseWithId; + import static org.assertj.core.api.Assertions.assertThat; +import static de.unistuttgart.iste.meitrex.common.testutil.TestUsers.userWithMembershipInCourseWithId; + @GraphQlApiTest @TablesToDelete({"flashcard_side", "flashcard", "flashcard_set"}) @@ -46,50 +47,53 @@ class MutationCreateFlashcardTest { @Commit void testCreateFlashcard(final GraphQlTester graphQlTester) { final List sets = testUtils.populateFlashcardSetRepository(flashcardSetRepository, courseId); - + final UUID itemId = UUID.randomUUID(); final String query = """ - mutation ($setId: UUID!) { - mutateFlashcardSet(assessmentId: $setId) { - createFlashcard(input: { - sides: [ - { - label: "Side 11", - isQuestion: true, - isAnswer: false, - text: "Question 1" - }, - { - label: "Side 21", - isQuestion: false, - isAnswer: true, - text: "Answer 1" + mutation ($setId: UUID!,$itemId:UUID!) { + mutateFlashcardSet(assessmentId: $setId) { + _internal_noauth_createFlashcard(input: { + itemId:$itemId, + sides: [ + { + label: "Side 11", + isQuestion: true, + isAnswer: false, + text: "Question 1" + }, + { + label: "Side 21", + isQuestion: false, + isAnswer: true, + text: "Answer 1" + } + ] + }) { + itemId + sides { + label + isQuestion + isAnswer + text + } + } + } } - ] - }) { - id - sides { - label - isQuestion - isAnswer - text - } - } - } - } - """; + """; + final UUID setId = sets.get(0).getAssessmentId(); // Execute the mutation and extract the created flashcard final Flashcard createdFlashcard = graphQlTester.document(query) .variable("setId", setId) + .variable("itemId", itemId) .execute() - .path("mutateFlashcardSet.createFlashcard") + .path("mutateFlashcardSet._internal_noauth_createFlashcard") .entity(Flashcard.class) .get(); // Assert the values of the data returned by the createFlashcard mutation - assertThat(createdFlashcard.getId()).isNotNull(); + assertThat(createdFlashcard.getItemId()).isNotNull(); assertThat(createdFlashcard.getSides()).containsExactlyInAnyOrder( new FlashcardSide("Question 1", "Side 11", true, false), new FlashcardSide("Answer 1", "Side 21", false, true) @@ -100,10 +104,10 @@ void testCreateFlashcard(final GraphQlTester graphQlTester) { .getReferenceById(setId) .getFlashcards() .stream() - .map(FlashcardEntity::getId)) - .contains(createdFlashcard.getId()); + .map(FlashcardEntity::getItemId)) + .contains(createdFlashcard.getItemId()); - final FlashcardEntity flashcardFromRepo = flashcardRepository.getReferenceById(createdFlashcard.getId()); + final FlashcardEntity flashcardFromRepo = flashcardRepository.getReferenceById(createdFlashcard.getItemId()); assertThat(flashcardFromRepo.getParentSet().getAssessmentId()).isEqualTo(setId); assertThat(flashcardFromRepo.getSides().get(0)) @@ -119,46 +123,50 @@ void testCreateFlashcard(final GraphQlTester graphQlTester) { @Test void testCreateInvalidFlashcard(final GraphQlTester graphQlTester) { final List sets = testUtils.populateFlashcardSetRepository(flashcardSetRepository, courseId); - + final UUID itemId = UUID.randomUUID(); final String query = """ - mutation ($setId: UUID!) { - mutateFlashcardSet(assessmentId: $setId) { - createFlashcard(input: { - sides: [ - { - label: "Side 11", - isQuestion: false, - isAnswer: true, - text: "Question 1" - }, - { - label: "Side 21", - isQuestion: false, - isAnswer: true, - text: "Answer 1" - } - ] - }) { - id - sides { - label - isQuestion - isAnswer - text + mutation ($setId: UUID!,$itemId:UUID!) { + mutateFlashcardSet(assessmentId: $setId) { + _internal_noauth_createFlashcard(input: { + itemId:$itemId + sides: [ + { + label: "Side 11", + isQuestion: false, + isAnswer: true, + text: "Question 1" + }, + { + label: "Side 21", + isQuestion: false, + isAnswer: true, + text: "Answer 1" + } + ] + }) { + itemId + sides { + label + isQuestion + isAnswer + text + } + } + } } - } - } - } - """; + """; + final UUID setId = sets.get(0).getAssessmentId(); // Execute the mutation and check for expected errors graphQlTester.document(query) .variable("setId", setId) + .variable("itemId", itemId) .execute() .errors() - .expect(responseError -> responseError.getMessage() != null && responseError.getMessage().toLowerCase().contains("flashcards must have at least one question side and one answer side")); + .expect(responseError -> responseError.getMessage() != null && responseError.getMessage().toLowerCase().contains("flashcards must have at least one question side and one answer side")); + } @@ -166,46 +174,49 @@ void testCreateInvalidFlashcard(final GraphQlTester graphQlTester) { @Test void testCreateInvalidFlashcardSide(final GraphQlTester graphQlTester) { final List sets = testUtils.populateFlashcardSetRepository(flashcardSetRepository, courseId); - + final UUID itemId = UUID.randomUUID(); final String query = """ - mutation ($setId: UUID!) { - mutateFlashcardSet(assessmentId: $setId) { - createFlashcard(input: { - sides: [ - { - label: "Side 11", - isQuestion: true, - isAnswer: false, - text: "Question 1" - }, - { - label: "Side 21", - isQuestion: false, - isAnswer: false, - text: "Answer 1" - } - ] - }) { - id - sides { - label - isQuestion - isAnswer - text + mutation ($setId: UUID!) { + mutateFlashcardSet(assessmentId: $setId) { + _internal_noauth_createFlashcard(input: { + sides: [ + { + label: "Side 11", + isQuestion: true, + isAnswer: false, + text: "Question 1" + }, + { + label: "Side 21", + isQuestion: false, + isAnswer: false, + text: "Answer 1" + } + ] + }) { + itemId + sides { + label + isQuestion + isAnswer + text + } + } + } } - } - } - } - """; + """; + final UUID setId = sets.get(0).getAssessmentId(); // Execute the mutation and check for expected errors graphQlTester.document(query) .variable("setId", setId) + .variable("itemId", itemId) .execute() .errors() - .expect(responseError -> responseError.getMessage() != null && responseError.getMessage().toLowerCase().contains("flashcard side must must be at least a question or an answer")); + .expect(responseError -> responseError.getMessage() != null && responseError.getMessage().toLowerCase().contains("flashcard side must must be at least a question or an answer")); + } diff --git a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationDeleteFlashcardSetTest.java b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationDeleteFlashcardSetTest.java index a9a9434..920822e 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationDeleteFlashcardSetTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationDeleteFlashcardSetTest.java @@ -5,9 +5,9 @@ import de.unistuttgart.iste.meitrex.common.testutil.TablesToDelete; import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSetEntity; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardRepository; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardSetRepository; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardSideRepository; + +import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.*; + import de.unistuttgart.iste.meitrex.flashcard_service.test_utils.TestUtils; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationDeleteFlashcardTest.java b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationDeleteFlashcardTest.java index df874cd..e399363 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationDeleteFlashcardTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationDeleteFlashcardTest.java @@ -56,7 +56,7 @@ void testDeleteFlashcard(final GraphQlTester tester) { """; final UUID setToDeleteFrom = expectedSets.get(0).getAssessmentId(); - final UUID flashcardToDelete = expectedSets.get(0).getFlashcards().stream().findAny().orElseThrow().getId(); + final UUID flashcardToDelete = expectedSets.get(0).getFlashcards().stream().findAny().orElseThrow().getItemId(); tester.document(query) .variable("assessmentId", setToDeleteFrom) @@ -74,7 +74,7 @@ void testDeleteFlashcard(final GraphQlTester tester) { .orElseThrow() .getFlashcards() .stream() - .filter(x -> x.getId() == flashcardToDelete)) + .filter(x -> x.getItemId() == flashcardToDelete)) .isEmpty(); // assert that the flashcard is missing from the flashcard repository diff --git a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationLogFlashcardProgressTest.java b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationLogFlashcardProgressTest.java index b0ef17e..d5ce1b5 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationLogFlashcardProgressTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationLogFlashcardProgressTest.java @@ -2,11 +2,9 @@ import de.unistuttgart.iste.meitrex.common.dapr.TopicPublisher; import de.unistuttgart.iste.meitrex.common.event.ContentProgressedEvent; -import de.unistuttgart.iste.meitrex.common.testutil.GraphQlApiTest; -import de.unistuttgart.iste.meitrex.common.testutil.InjectCurrentUserHeader; -import de.unistuttgart.iste.meitrex.common.testutil.MockTestPublisherConfiguration; -import de.unistuttgart.iste.meitrex.common.testutil.TablesToDelete; +import de.unistuttgart.iste.meitrex.common.testutil.*; import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser; +import de.unistuttgart.iste.meitrex.common.event.Response; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSetEntity; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardSetRepository; import de.unistuttgart.iste.meitrex.flashcard_service.test_utils.TestUtils; @@ -44,23 +42,23 @@ class MutationLogFlashcardProgressTest { private final LoggedInUser loggedInUser = userWithMembershipInCourseWithId(courseId, LoggedInUser.UserRoleInCourse.ADMINISTRATOR); private static final String mutation = """ - mutation logFlashcardProgress($id: UUID!, $successful: Boolean!) { - logFlashcardLearned( - input: { - flashcardId: $id, - successful: $successful - } - ) { - success - nextLearnDate - flashcardSetProgress { - correctness - percentageLearned - } + mutation logFlashcardProgress($id: UUID!, $successful: Boolean!) { + logFlashcardLearned( + input: { + flashcardId: $id, + successful: $successful + } + ) { + success + nextLearnDate + flashcardSetProgress { + correctness + percentageLearned } - } - """; + + } + """; /** * Tests that the mutation "logFlashcardProgress" works as expected @@ -73,8 +71,8 @@ void testLogFlashcardProgress(final HttpGraphQlTester graphQlTester) { FlashcardSetEntity flashcardSetEntity = flashcardSet.get(0); final UUID flashcardSetId = flashcardSetEntity.getAssessmentId(); - final UUID flashcardId1 = flashcardSetEntity.getFlashcards().get(0).getId(); - final UUID flashcardId2 = flashcardSetEntity.getFlashcards().get(1).getId(); + final UUID flashcardId1 = flashcardSetEntity.getFlashcards().get(0).getItemId(); + final UUID flashcardId2 = flashcardSetEntity.getFlashcards().get(1).getItemId(); runMutationLogFlashcardLearned(graphQlTester, flashcardId1, true) .path("logFlashcardLearned.success").entity(Boolean.class).isEqualTo(true) @@ -98,6 +96,7 @@ void testLogFlashcardProgress(final HttpGraphQlTester graphQlTester) { .success(true) .timeToComplete(null) .hintsUsed(0) + .responses(List.of(Response.builder().itemId(flashcardId1).response(1).build(), Response.builder().itemId(flashcardId2).response(0).build())) .build(); verify(topicPublisher).notifyUserWorkedOnContent(expectedEvent); @@ -115,8 +114,17 @@ void testLogFlashcardProgress(final HttpGraphQlTester graphQlTester) { runMutationLogFlashcardLearned(graphQlTester, flashcardId2, true) .errors().verify(); + final ContentProgressedEvent expectedEvent2 = ContentProgressedEvent.builder() + .userId(loggedInUser.getId()) + .contentId(flashcardSetId) + .correctness(1.0) + .success(true) + .timeToComplete(null) + .hintsUsed(0) + .responses(List.of(Response.builder().itemId(flashcardId1).response(1).build(), Response.builder().itemId(flashcardId2).response(1).build())) + .build(); - verify(topicPublisher).notifyUserWorkedOnContent(expectedEvent); + verify(topicPublisher).notifyUserWorkedOnContent(expectedEvent2); } @NotNull @@ -129,4 +137,4 @@ private static GraphQlTester.Response runMutationLogFlashcardLearned(final HttpG .variable("successful", success) .execute(); } -} +} \ No newline at end of file diff --git a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationUpdateFlashcardTest.java b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationUpdateFlashcardTest.java index acd5d49..147851d 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationUpdateFlashcardTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/MutationUpdateFlashcardTest.java @@ -1,15 +1,22 @@ package de.unistuttgart.iste.meitrex.flashcard_service.api; + +import de.unistuttgart.iste.meitrex.common.testutil.AuthorizationAsserts; import de.unistuttgart.iste.meitrex.common.testutil.GraphQlApiTest; import de.unistuttgart.iste.meitrex.common.testutil.InjectCurrentUserHeader; import de.unistuttgart.iste.meitrex.common.testutil.TablesToDelete; import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSetEntity; + import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardRepository; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardSetRepository; import de.unistuttgart.iste.meitrex.flashcard_service.test_utils.TestUtils; import de.unistuttgart.iste.meitrex.generated.dto.Flashcard; import de.unistuttgart.iste.meitrex.generated.dto.FlashcardSide; + +import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardSetRepository; +import de.unistuttgart.iste.meitrex.flashcard_service.test_utils.TestUtils; + import jakarta.transaction.Transactional; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -26,6 +33,8 @@ @TablesToDelete({"flashcard_side", "flashcard", "flashcard_set"}) class MutationUpdateFlashcardTest { + + @Autowired private FlashcardSetRepository flashcardSetRepository; @@ -47,51 +56,52 @@ void testUpdateFlashcard(final GraphQlTester tester) { final UUID setOfFlashcard = set.get(0).getAssessmentId(); // Perform the update operation - final UUID flashcardToUpdate = set.get(0).getFlashcards().stream().findAny().orElseThrow().getId(); + final UUID flashcardToUpdate = set.get(0).getFlashcards().stream().findAny().orElseThrow().getItemId(); final String query = """ - mutation ($assessmentId: UUID!, $flashcardId: UUID!) { - mutateFlashcardSet(assessmentId: $assessmentId) { - updateFlashcard(input: { - id: $flashcardId, - sides: [ - { - label: "New_Side 1", - isQuestion: true, - isAnswer: false, - text: "{text: \\"New_Question 1\\"}" - }, - { - label: "New_Side 2", - isQuestion: false, - isAnswer: true, - text: "{text: \\"New_Answer 1\\"}" + mutation ($assessmentId: UUID!, $flashcardId: UUID!) { + mutateFlashcardSet(assessmentId: $assessmentId) { + _internal_noauth_updateFlashcard(input: { + itemId: $flashcardId, + sides: [ + { + label: "New_Side 1", + isQuestion: true, + isAnswer: false, + text: "{text: \\"New_Question 1\\"}" + }, + { + label: "New_Side 2", + isQuestion: false, + isAnswer: true, + text: "{text: \\"New_Answer 1\\"}" + } + ] + }) { + itemId + sides { + label + isQuestion + isAnswer + text + } + } + } } - ] - }) { - id - sides { - label - isQuestion - isAnswer - text - } - } - } - } - """; + """; + // Execute the update mutation query final Flashcard updatedFlashcard = tester.document(query) .variable("assessmentId", setOfFlashcard) .variable("flashcardId", flashcardToUpdate) .execute() - .path("mutateFlashcardSet.updateFlashcard") + .path("mutateFlashcardSet._internal_noauth_updateFlashcard") .entity(Flashcard.class) .get(); // Assert the values of the data returned by the updateFlashcard mutation - assertThat(updatedFlashcard.getId()).isEqualTo(flashcardToUpdate); + assertThat(updatedFlashcard.getItemId()).isEqualTo(flashcardToUpdate); assertThat(updatedFlashcard.getSides()).containsExactlyInAnyOrder( new FlashcardSide("{text: \"New_Question 1\"}", "New_Side 1", diff --git a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/QueryDueFlashcardsByCourseIdTest.java b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/QueryDueFlashcardsByCourseIdTest.java index b342095..b2feb00 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/QueryDueFlashcardsByCourseIdTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/QueryDueFlashcardsByCourseIdTest.java @@ -1,13 +1,10 @@ package de.unistuttgart.iste.meitrex.flashcard_service.api; -import de.unistuttgart.iste.meitrex.common.testutil.GraphQlApiTest; -import de.unistuttgart.iste.meitrex.common.testutil.InjectCurrentUserHeader; -import de.unistuttgart.iste.meitrex.common.testutil.TablesToDelete; +import de.unistuttgart.iste.meitrex.common.testutil.*; import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser; import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser.UserRoleInCourse; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardEntity; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardProgressDataEntity; -import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSetEntity; +import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.*; +import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardProgressDataEntity.PrimaryKey; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardProgressDataRepository; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.repository.FlashcardSetRepository; import de.unistuttgart.iste.meitrex.flashcard_service.test_utils.TestUtils; @@ -53,22 +50,22 @@ void testDueFlashcardsByCourseId(final GraphQlTester tester) { flashcardProgressDataRepository.saveAll(List.of( // due FlashcardProgressDataEntity.builder() - .primaryKey(new FlashcardProgressDataEntity.PrimaryKey(flashCardsSet1.get(0).getId(), loggedInUser.getId())) + .primaryKey(new PrimaryKey(flashCardsSet1.get(0).getItemId(), loggedInUser.getId())) .nextLearn(OffsetDateTime.now().minusDays(2)) .build(), // not due FlashcardProgressDataEntity.builder() - .primaryKey(new FlashcardProgressDataEntity.PrimaryKey(flashCardsSet1.get(1).getId(), loggedInUser.getId())) + .primaryKey(new PrimaryKey(flashCardsSet1.get(1).getItemId(), loggedInUser.getId())) .nextLearn(null) .build(), // due FlashcardProgressDataEntity.builder() - .primaryKey(new FlashcardProgressDataEntity.PrimaryKey(flashCardsSet2.get(0).getId(), loggedInUser.getId())) + .primaryKey(new PrimaryKey(flashCardsSet2.get(0).getItemId(), loggedInUser.getId())) .nextLearn(OffsetDateTime.now().minusDays(1)) .build(), // not due FlashcardProgressDataEntity.builder() - .primaryKey(new FlashcardProgressDataEntity.PrimaryKey(flashCardsSet2.get(1).getId(), loggedInUser.getId())) + .primaryKey(new PrimaryKey(flashCardsSet2.get(1).getItemId(), loggedInUser.getId())) .nextLearn(OffsetDateTime.now().plusDays(1)) .build() )); @@ -76,7 +73,7 @@ void testDueFlashcardsByCourseId(final GraphQlTester tester) { final String query = """ query($courseId: UUID!) { dueFlashcardsByCourseId(courseId: $courseId) { - id + itemId } } """; @@ -84,9 +81,9 @@ void testDueFlashcardsByCourseId(final GraphQlTester tester) { tester.document(query) .variable("courseId", courseId) .execute() - .path("dueFlashcardsByCourseId[*].id") + .path("dueFlashcardsByCourseId[*].itemId") .entityList(UUID.class) .hasSize(2) - .contains(flashCardsSet1.get(0).getId(), flashCardsSet2.get(0).getId()); + .contains(flashCardsSet1.get(0).getItemId(), flashCardsSet2.get(0).getItemId()); } } diff --git a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/QueryFlashcardSetTest.java b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/QueryFlashcardSetTest.java index 851f76d..a6e9e26 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/QueryFlashcardSetTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/QueryFlashcardSetTest.java @@ -1,8 +1,8 @@ package de.unistuttgart.iste.meitrex.flashcard_service.api; -import de.unistuttgart.iste.meitrex.common.testutil.GraphQlApiTest; -import de.unistuttgart.iste.meitrex.common.testutil.InjectCurrentUserHeader; -import de.unistuttgart.iste.meitrex.common.testutil.TablesToDelete; + +import de.unistuttgart.iste.meitrex.common.testutil.*; + import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser; import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser.UserRoleInCourse; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSetEntity; @@ -49,7 +49,7 @@ void testFlashcardSetsByAssessmentIds(final GraphQlTester tester) { assessmentId, courseId, flashcards { - id, + itemId, sides { label, isQuestion, diff --git a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/QueryFlashcardTest.java b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/QueryFlashcardTest.java index af5ae72..d7a8057 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/QueryFlashcardTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/QueryFlashcardTest.java @@ -1,8 +1,8 @@ package de.unistuttgart.iste.meitrex.flashcard_service.api; -import de.unistuttgart.iste.meitrex.common.testutil.GraphQlApiTest; -import de.unistuttgart.iste.meitrex.common.testutil.InjectCurrentUserHeader; -import de.unistuttgart.iste.meitrex.common.testutil.TablesToDelete; + +import de.unistuttgart.iste.meitrex.common.testutil.*; + import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser; import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser.UserRoleInCourse; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardEntity; @@ -43,16 +43,14 @@ class QueryFlashcardTest { @Transactional void testQueryFlashcardsByIds(final GraphQlTester tester) { final List expectedSets = testUtils.populateFlashcardSetRepository(flashcardSetRepository, courseId); - final List flashcardsToQuery = List.of( expectedSets.get(0).getFlashcards().get(0), expectedSets.get(1).getFlashcards().get(1) ); - final String query = """ query($ids: [UUID!]!) { - flashcardsByIds(ids: $ids) { - id + flashcardsByIds(itemIds: $ids) { + itemId sides { label isQuestion @@ -64,7 +62,7 @@ void testQueryFlashcardsByIds(final GraphQlTester tester) { """; final List actualFlashcards = tester.document(query) - .variable("ids", flashcardsToQuery.stream().map(FlashcardEntity::getId)) + .variable("ids", flashcardsToQuery.stream().map(FlashcardEntity::getItemId)) .execute() .path("flashcardsByIds") .entityList(Flashcard.class) diff --git a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/TestAuthorization.java b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/TestAuthorization.java index 86c7696..0f006ce 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/TestAuthorization.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/api/TestAuthorization.java @@ -21,7 +21,7 @@ @GraphQlApiTest @TablesToDelete({"flashcard_side", "flashcard", "flashcard_set"}) -class TestAuthorization { +public class TestAuthorization { @Autowired private FlashcardSetRepository flashcardSetRepository; @@ -37,49 +37,23 @@ class TestAuthorization { @Test @Transactional @Commit - void testUpdateFlashcardOnlyForAdmins(final GraphQlTester tester) { - final List set = testUtils.populateFlashcardSetRepository(flashcardSetRepository, courseId); - - final UUID setOfFlashcard = set.get(0).getAssessmentId(); - // Perform the update operation - final UUID flashcardToUpdate = set.get(0).getFlashcards().stream().findAny().orElseThrow().getId(); + void testDeleteFlashcardOnlyForAdmins(final GraphQlTester tester) { + final List expectedSets = testUtils.populateFlashcardSetRepository(flashcardSetRepository, courseId); final String query = """ - mutation ($assessmentId: UUID!, $flashcardId: UUID!) { + mutation($assessmentId: UUID!, $flashcardId: UUID!) { mutateFlashcardSet(assessmentId: $assessmentId) { - updateFlashcard(input: { - id: $flashcardId, - sides: [ - { - label: "New_Side 1", - isQuestion: true, - isAnswer: false, - text: "{text: \\"New_Question 1\\"}" - }, - { - label: "New_Side 2", - isQuestion: false, - isAnswer: true, - text: "{text: \\"New_Answer 1\\"}" - } - ] - }) { - id - sides { - label - isQuestion - isAnswer - text - } - } + deleteFlashcard(id: $flashcardId) } - } + } """; - // Execute the update mutation query + final UUID setToDeleteFrom = expectedSets.get(0).getAssessmentId(); + final UUID flashcardToDelete = expectedSets.get(0).getFlashcards().stream().findAny().orElseThrow().getItemId(); + tester.document(query) - .variable("assessmentId", setOfFlashcard) - .variable("flashcardId", flashcardToUpdate) + .variable("assessmentId", setToDeleteFrom) + .variable("flashcardId", flashcardToDelete) .execute() .errors() .satisfy(AuthorizationAsserts::assertIsMissingUserRoleError); @@ -89,3 +63,4 @@ void testUpdateFlashcardOnlyForAdmins(final GraphQlTester tester) { } + diff --git a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/service/FlashcardServiceTest.java b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/service/FlashcardServiceTest.java index 2a22f67..d14b857 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/service/FlashcardServiceTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/service/FlashcardServiceTest.java @@ -1,5 +1,8 @@ package de.unistuttgart.iste.meitrex.flashcard_service.service; + +import de.unistuttgart.iste.meitrex.common.dapr.TopicPublisher; + import de.unistuttgart.iste.meitrex.common.event.ContentChangeEvent; import de.unistuttgart.iste.meitrex.common.event.CrudOperation; import de.unistuttgart.iste.meitrex.common.exception.IncompleteEventMessageException; @@ -27,7 +30,10 @@ class FlashcardServiceTest { private final FlashcardSetRepository flashcardSetRepository = Mockito.mock(FlashcardSetRepository.class); private final FlashcardMapper flashcardMapper = new FlashcardMapper(new ModelMapper()); private final FlashcardValidator flashcardValidator = new FlashcardValidator(); - private final FlashcardService flashcardService = new FlashcardService(flashcardRepository, flashcardSetRepository, flashcardMapper, flashcardValidator); + + private final TopicPublisher topicPublisher = Mockito.mock(TopicPublisher.class); + private final FlashcardService flashcardService = new FlashcardService(flashcardRepository, flashcardSetRepository, flashcardMapper, flashcardValidator, topicPublisher); + @Test void removeContentIds() { //init @@ -51,6 +57,7 @@ void removeContentIds() { assertDoesNotThrow(() -> flashcardService.deleteFlashcardSetIfContentIsDeleted(contentChangeEvent)); verify(flashcardSetRepository, times(1)).deleteAllById(any()); } + @Test void removeContentIdsWithNoIdsToBeRemovedTest() { //init diff --git a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/test_utils/TestUtils.java b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/test_utils/TestUtils.java index 8580670..64e36ad 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/test_utils/TestUtils.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/flashcard_service/test_utils/TestUtils.java @@ -1,5 +1,6 @@ package de.unistuttgart.iste.meitrex.flashcard_service.test_utils; + import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardEntity; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSetEntity; import de.unistuttgart.iste.meitrex.flashcard_service.persistence.entity.FlashcardSideEntity; @@ -15,6 +16,7 @@ public class TestUtils { /** * Helper method which creates some flashcard sets and saves them to the repository. + * * @param repo The repository to save the flashcard sets to. * @return Returns the created flashcard sets. */ @@ -26,7 +28,7 @@ public List populateFlashcardSetRepository(FlashcardSetRepos set1.setCourseId(courseId); FlashcardEntity flashcard1 = new FlashcardEntity(); - flashcard1.setId(UUID.randomUUID()); + flashcard1.setItemId(UUID.randomUUID()); flashcard1.setParentSet(set1); flashcard1.setSides(List.of( new FlashcardSideEntity(UUID.randomUUID(), @@ -44,7 +46,7 @@ public List populateFlashcardSetRepository(FlashcardSetRepos )); FlashcardEntity flashcard2 = new FlashcardEntity(); - flashcard2.setId(UUID.randomUUID()); + flashcard2.setItemId(UUID.randomUUID()); flashcard2.setParentSet(set1); flashcard2.setSides(List.of( new FlashcardSideEntity(UUID.randomUUID(), @@ -68,7 +70,7 @@ public List populateFlashcardSetRepository(FlashcardSetRepos set2.setCourseId(courseId); FlashcardEntity flashcard3 = new FlashcardEntity(); - flashcard3.setId(UUID.randomUUID()); + flashcard3.setItemId(UUID.randomUUID()); flashcard3.setParentSet(set2); flashcard3.setSides(List.of( new FlashcardSideEntity(UUID.randomUUID(), @@ -86,7 +88,7 @@ public List populateFlashcardSetRepository(FlashcardSetRepos )); FlashcardEntity flashcard4 = new FlashcardEntity(); - flashcard4.setId(UUID.randomUUID()); + flashcard4.setItemId(UUID.randomUUID()); flashcard4.setParentSet(set2); flashcard4.setSides(List.of( new FlashcardSideEntity(UUID.randomUUID(),