diff --git a/docker-compose-acdh-develop.yml b/docker-compose-acdh-develop.yml index 0f6c4dad..e49e3dea 100644 --- a/docker-compose-acdh-develop.yml +++ b/docker-compose-acdh-develop.yml @@ -1,10 +1,10 @@ version: "2" services: marketplace: - build: + build: context: . dockerfile: Dockerfile-acdh-ch - args: + args: - GID=1032 - UID=1032 image: docker.gitlab.gwdg.de/sshoc/sshoc-marketplace-backend/api:latest @@ -23,7 +23,7 @@ services: POSTGRES_PORT: 5432 SOLR_HOST: solr SOLR_PORT: 8983 - aai_base_uri: https://aai.eosc-portal.eu/oidc + aai_base_uri: https://proxy.acc.myaccessid.org/OIDC egi_id: ${egi_id} egi_secret: ${egi_secret} token_secret: ${token_secret} diff --git a/docker-compose-acdh-prod.yml b/docker-compose-acdh-prod.yml index dfede0b2..11314c85 100644 --- a/docker-compose-acdh-prod.yml +++ b/docker-compose-acdh-prod.yml @@ -25,7 +25,7 @@ services: POSTGRES_PORT: 5432 SOLR_HOST: solr-prod SOLR_PORT: 8983 - aai_base_uri: https://aai.eosc-portal.eu/oidc + aai_base_uri: https://proxy.acc.myaccessid.org/OIDC egi_id: ${egi_id} egi_secret: ${egi_secret} token_secret: ${token_secret} diff --git a/docker-compose-acdh-stage.yml b/docker-compose-acdh-stage.yml index 64c47042..10bf90d4 100644 --- a/docker-compose-acdh-stage.yml +++ b/docker-compose-acdh-stage.yml @@ -1,10 +1,10 @@ version: "2" services: marketplace-stage: - build: + build: context: . dockerfile: Dockerfile-acdh-ch - args: + args: - GID=1057 - UID=1057 image: docker.gitlab.gwdg.de/sshoc/sshoc-marketplace-backend/api-stage:latest @@ -23,7 +23,7 @@ services: POSTGRES_PORT: 5432 SOLR_HOST: solr-stage SOLR_PORT: 8983 - aai_base_uri: https://aai.eosc-portal.eu/oidc + aai_base_uri: https://proxy.acc.myaccessid.org/OIDC egi_id: ${egi_id} egi_secret: ${egi_secret} token_secret: ${token_secret} diff --git a/docker-compose.yml b/docker-compose.yml index b3221eb7..35e0fbe6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: - solr environment: APPLICATION_PROFILE: dev - aai_base_uri: https://aai.eosc-portal.eu/oidc + aai_base_uri: https://proxy.acc.myaccessid.org/OIDC egi_id: ${egi_id} egi_secret: ${egi_secret} token_secret: ${token_secret} diff --git a/src/main/java/eu/sshopencloud/marketplace/conf/auth/WebSecurityConfig.java b/src/main/java/eu/sshopencloud/marketplace/conf/auth/WebSecurityConfig.java index 7e5f0d9b..33cf5f0c 100644 --- a/src/main/java/eu/sshopencloud/marketplace/conf/auth/WebSecurityConfig.java +++ b/src/main/java/eu/sshopencloud/marketplace/conf/auth/WebSecurityConfig.java @@ -115,6 +115,7 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers(HttpMethod.GET, "/api/datasets/*/merge").hasAuthority(Authority.MODERATOR.name()) .antMatchers(HttpMethod.POST, "/api/datasets/merge").hasAuthority(Authority.MODERATOR.name()) .antMatchers(HttpMethod.PUT, "/api/datasets/*/versions/*/revert").hasAuthority(Authority.MODERATOR.name()) + .antMatchers(HttpMethod.PUT, "/api/datasets/*/revert").hasAuthority(Authority.ADMINISTRATOR.name()) .antMatchers(HttpMethod.POST, "/api/datasets/**").hasAuthority(Authority.CONTRIBUTOR.name()) .antMatchers(HttpMethod.PUT, "/api/datasets/**").hasAuthority(Authority.CONTRIBUTOR.name()) .antMatchers(HttpMethod.DELETE, "/api/datasets/**").hasAuthority(Authority.CONTRIBUTOR.name()); @@ -123,6 +124,7 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers(HttpMethod.GET, "/api/tools-services/*/merge").hasAuthority(Authority.MODERATOR.name()) .antMatchers(HttpMethod.POST, "/api/tools-services/merge").hasAuthority(Authority.MODERATOR.name()) .antMatchers(HttpMethod.PUT, "/api/tools-services/*/versions/*/revert").hasAuthority(Authority.MODERATOR.name()) + .antMatchers(HttpMethod.PUT, "/api/tools-services/*/revert").hasAuthority(Authority.ADMINISTRATOR.name()) .antMatchers(HttpMethod.POST, "/api/tools-services/**").hasAuthority(Authority.CONTRIBUTOR.name()) .antMatchers(HttpMethod.PUT, "/api/tools-services/**").hasAuthority(Authority.CONTRIBUTOR.name()) .antMatchers(HttpMethod.DELETE, "/api/tools-services/**").hasAuthority(Authority.CONTRIBUTOR.name()); @@ -131,6 +133,7 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers(HttpMethod.GET, "/api/training-materials/*/merge").hasAuthority(Authority.MODERATOR.name()) .antMatchers(HttpMethod.POST, "/api/training-materials/merge").hasAuthority(Authority.MODERATOR.name()) .antMatchers(HttpMethod.PUT, "/api/training-materials/*/versions/*/revert").hasAuthority(Authority.MODERATOR.name()) + .antMatchers(HttpMethod.PUT, "/api/training-materials/*/revert").hasAuthority(Authority.ADMINISTRATOR.name()) .antMatchers(HttpMethod.POST, "/api/training-materials/**").hasAuthority(Authority.CONTRIBUTOR.name()) .antMatchers(HttpMethod.PUT, "/api/training-materials/**").hasAuthority(Authority.CONTRIBUTOR.name()) .antMatchers(HttpMethod.DELETE, "/api/training-materials/**").hasAuthority(Authority.CONTRIBUTOR.name()); @@ -139,6 +142,7 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers(HttpMethod.GET, "/api/publications/*/merge").hasAuthority(Authority.MODERATOR.name()) .antMatchers(HttpMethod.POST, "/api/publications/merge").hasAuthority(Authority.MODERATOR.name()) .antMatchers(HttpMethod.PUT, "/api/publications/*/versions/*/revert").hasAuthority(Authority.MODERATOR.name()) + .antMatchers(HttpMethod.PUT, "/api/publications/*/revert").hasAuthority(Authority.ADMINISTRATOR.name()) .antMatchers(HttpMethod.POST, "/api/publications/**").hasAuthority(Authority.CONTRIBUTOR.name()) .antMatchers(HttpMethod.PUT, "/api/publications/**").hasAuthority(Authority.CONTRIBUTOR.name()) .antMatchers(HttpMethod.DELETE, "/api/publications/**").hasAuthority(Authority.MODERATOR.name()); @@ -147,6 +151,7 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers(HttpMethod.GET, "/api/workflows/*/merge").hasAuthority(Authority.MODERATOR.name()) .antMatchers(HttpMethod.POST, "/api/workflows/merge").hasAuthority(Authority.MODERATOR.name()) .antMatchers(HttpMethod.PUT, "/api/workflows/*/versions/*/revert").hasAuthority(Authority.MODERATOR.name()) + .antMatchers(HttpMethod.PUT, "/api/workflows/*/revert").hasAuthority(Authority.ADMINISTRATOR.name()) .antMatchers(HttpMethod.GET, "/api/workflows/*/steps/*/merge").hasAuthority(Authority.MODERATOR.name()) .antMatchers(HttpMethod.POST, "/api/workflows/*/steps/merge").hasAuthority(Authority.MODERATOR.name()) .antMatchers(HttpMethod.PUT, "/api/workflows/*/steps/*/versions/*/revert").hasAuthority(Authority.MODERATOR.name()) diff --git a/src/main/java/eu/sshopencloud/marketplace/controllers/datasets/DatasetController.java b/src/main/java/eu/sshopencloud/marketplace/controllers/datasets/DatasetController.java index 7800c33a..88a9ade8 100644 --- a/src/main/java/eu/sshopencloud/marketplace/controllers/datasets/DatasetController.java +++ b/src/main/java/eu/sshopencloud/marketplace/controllers/datasets/DatasetController.java @@ -87,6 +87,13 @@ public ResponseEntity revertDataset(@PathVariable("persistentId") St return ResponseEntity.ok(datasetService.revertDataset(persistentId, versionId)); } + @Operation(summary = "Revert dataset by its persistentId. This method will set its status to ACTIVE " + + "and its versioned item to REVIEWED status and active state.") + @PutMapping(path = "/{persistentId}/revert", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity revertDataset(@PathVariable("persistentId") String persistentId) { + return ResponseEntity.ok(datasetService.revertDataset(persistentId)); + } + @Operation(summary = "Delete dataset by its persistentId") @DeleteMapping(path = "/{persistentId}") public void deleteDataset(@PathVariable("persistentId") String persistentId, diff --git a/src/main/java/eu/sshopencloud/marketplace/controllers/publications/PublicationController.java b/src/main/java/eu/sshopencloud/marketplace/controllers/publications/PublicationController.java index 2e0c3bdb..ac474baf 100644 --- a/src/main/java/eu/sshopencloud/marketplace/controllers/publications/PublicationController.java +++ b/src/main/java/eu/sshopencloud/marketplace/controllers/publications/PublicationController.java @@ -96,6 +96,13 @@ public void deletePublication(@PathVariable("persistentId") String persistentId, publicationService.deletePublication(persistentId, draft); } + @Operation(summary = "Revert publication by its persistentId. This method will set its status to ACTIVE " + + "and its versioned item to REVIEWED status and active state.") + @PutMapping(path = "/{persistentId}/revert", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity revertPublication(@PathVariable("persistentId") String persistentId) { + return ResponseEntity.ok(publicationService.revertPublication(persistentId)); + } + @Operation(summary = "Delete publication by its persistentId and versionId") @DeleteMapping(path = "/{persistentId}/versions/{versionId}") public void deletePublicationVersion(@PathVariable("persistentId") String persistentId, @PathVariable("versionId") long versionId) { diff --git a/src/main/java/eu/sshopencloud/marketplace/controllers/tools/ToolController.java b/src/main/java/eu/sshopencloud/marketplace/controllers/tools/ToolController.java index 6b739bfd..11245ff9 100644 --- a/src/main/java/eu/sshopencloud/marketplace/controllers/tools/ToolController.java +++ b/src/main/java/eu/sshopencloud/marketplace/controllers/tools/ToolController.java @@ -88,6 +88,13 @@ public ResponseEntity revertTool(@PathVariable("persistentId") String p return ResponseEntity.ok(toolService.revertTool(persistentId, versionId)); } + @Operation(summary = "Revert tool by its persistentId. This method will set its status to ACTIVE " + + "and its versioned item to REVIEWED status and active state.") + @PutMapping(path = "/{persistentId}/revert", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity revertTool(@PathVariable("persistentId") String persistentId) { + return ResponseEntity.ok(toolService.revertTool(persistentId)); + } + @Operation(summary = "Delete tool by its persistentId") @DeleteMapping(path = "/{persistentId}") public void deleteTool(@PathVariable("persistentId") String persistentId, @RequestParam(value = "draft", defaultValue = "false") boolean draft) { diff --git a/src/main/java/eu/sshopencloud/marketplace/controllers/trainings/TrainingMaterialController.java b/src/main/java/eu/sshopencloud/marketplace/controllers/trainings/TrainingMaterialController.java index 92f5c260..228cf9fb 100644 --- a/src/main/java/eu/sshopencloud/marketplace/controllers/trainings/TrainingMaterialController.java +++ b/src/main/java/eu/sshopencloud/marketplace/controllers/trainings/TrainingMaterialController.java @@ -91,6 +91,13 @@ public ResponseEntity revertTrainingMaterial(@PathVariable( return ResponseEntity.ok(trainingMaterialService.revertTrainingMaterial(persistentId, versionId)); } + @Operation(summary = ("Revert training material by its persistentId. This method " + + "will set its status to ACTIVE and its versioned item to REVIEWED status and active state.")) + @PutMapping(path = "/{persistentId}/revert", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity revertTrainingMaterial(@PathVariable("persistentId") String persistentId) { + return ResponseEntity.ok(trainingMaterialService.revertTrainingMaterial(persistentId)); + } + @Operation(summary = "Delete training material by its persistentId") @DeleteMapping(path = "/{persistentId}") public void deleteTrainingMaterial(@PathVariable("persistentId") String persistentId, diff --git a/src/main/java/eu/sshopencloud/marketplace/controllers/workflows/WorkflowController.java b/src/main/java/eu/sshopencloud/marketplace/controllers/workflows/WorkflowController.java index d760c5ce..b9db3d0b 100644 --- a/src/main/java/eu/sshopencloud/marketplace/controllers/workflows/WorkflowController.java +++ b/src/main/java/eu/sshopencloud/marketplace/controllers/workflows/WorkflowController.java @@ -88,6 +88,13 @@ public ResponseEntity revertWorkflow(@PathVariable("persistentId") return ResponseEntity.ok(workflowService.revertWorkflow(workflowPersistentId, versionId)); } + @Operation(summary = "Revert workflow by its persistentId. This method will set its status to ACTIVE " + + "and its versioned item to REVIEWED status and active state.") + @PutMapping(path = "/{persistentId}/revert", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity revertWorkflow(@PathVariable("persistentId") String workflowPersistentId) { + return ResponseEntity.ok(workflowService.revertWorkflow(workflowPersistentId)); + } + @Operation(summary = "Delete workflow by its persistentId") @DeleteMapping(path = "/{persistentId}") public void deleteWorkflow(@PathVariable("persistentId") String workflowPersistentId, diff --git a/src/main/java/eu/sshopencloud/marketplace/repositories/items/ItemVersionRepository.java b/src/main/java/eu/sshopencloud/marketplace/repositories/items/ItemVersionRepository.java index 7111d598..d6d37c2a 100644 --- a/src/main/java/eu/sshopencloud/marketplace/repositories/items/ItemVersionRepository.java +++ b/src/main/java/eu/sshopencloud/marketplace/repositories/items/ItemVersionRepository.java @@ -59,8 +59,14 @@ public interface ItemVersionRepository extends JpaRepository findCurrentVersion(@Param("persistentId") String persistentId); + Optional findCurrentActiveVersion(@Param("persistentId") String persistentId); + @Query( + "select v from #{#entityName} v " + + "join VersionedItem i on i.currentVersion = v " + + "where i.persistentId = :persistentId" + ) + Optional findCurrentVersion(@Param("persistentId") String persistentId); @Query( "select v from #{#entityName} v " + diff --git a/src/main/java/eu/sshopencloud/marketplace/services/items/DatasetService.java b/src/main/java/eu/sshopencloud/marketplace/services/items/DatasetService.java index 2800d26a..beeb5b4c 100644 --- a/src/main/java/eu/sshopencloud/marketplace/services/items/DatasetService.java +++ b/src/main/java/eu/sshopencloud/marketplace/services/items/DatasetService.java @@ -90,6 +90,10 @@ public DatasetDto revertDataset(String persistentId, long versionId) { return prepareItemDto(dataset); } + public DatasetDto revertDataset(String persistentId) { + Dataset dataset = revertItemVersion(persistentId); + return prepareItemDto(dataset); + } public DatasetDto commitDraftDataset(String persistentId) { Dataset dataset = publishDraftItem(persistentId); diff --git a/src/main/java/eu/sshopencloud/marketplace/services/items/ItemCrudService.java b/src/main/java/eu/sshopencloud/marketplace/services/items/ItemCrudService.java index 402d2dfc..b1384425 100644 --- a/src/main/java/eu/sshopencloud/marketplace/services/items/ItemCrudService.java +++ b/src/main/java/eu/sshopencloud/marketplace/services/items/ItemCrudService.java @@ -474,6 +474,18 @@ protected I revertItemVersion(String persistentId, long versionId) { return targetVersion; } + protected I revertItemVersion(String persistentId) { + I currentVersion = loadCurrentItem(persistentId, false); + + currentVersion.setStatus(ItemStatus.APPROVED); + currentVersion.getVersionedItem().setActive(true); + currentVersion.getVersionedItem().setStatus(VersionedItemStatus.REVIEWED); + + indexItemService.indexItem(currentVersion); + + return currentVersion; + } + protected I liftItemVersion(String persistentId, boolean draft) { return liftItemVersion(persistentId, draft, true); } @@ -691,7 +703,7 @@ protected D prepareMergeItems(String persistentId, List mergeList) { for (int i = 0; i < mergeList.size(); i++) { - Optional toMergeHolder = itemRepository.findCurrentVersion(mergeList.get(i)); + Optional toMergeHolder = itemRepository.findCurrentActiveVersion(mergeList.get(i)); if (toMergeHolder.isEmpty()) continue; Item toMerge = toMergeHolder.get(); @@ -791,7 +803,7 @@ protected ItemsDifferencesDto getDifferences(String persistentId, Long versionId Optional otherHolder; if (Objects.isNull(otherVersionId)) - otherHolder = itemRepository.findCurrentVersion(otherPersistentId); + otherHolder = itemRepository.findCurrentActiveVersion(otherPersistentId); else otherHolder = itemRepository.findByVersionedItemPersistentIdAndId(otherPersistentId, otherVersionId); diff --git a/src/main/java/eu/sshopencloud/marketplace/services/items/ItemVersionService.java b/src/main/java/eu/sshopencloud/marketplace/services/items/ItemVersionService.java index d84e1701..5fa0d882 100644 --- a/src/main/java/eu/sshopencloud/marketplace/services/items/ItemVersionService.java +++ b/src/main/java/eu/sshopencloud/marketplace/services/items/ItemVersionService.java @@ -56,15 +56,26 @@ protected Optional tryLoadLatestItem(String persistentId) { } /** - * Loads the most recent item for update. Does not necessarily need to be approved + * Loads the most recent item for update. Does not need to be approved. * For the internal use only, as this method does not validate user access privileges + * @param onlyActive if true seeks for active items only */ - protected I loadCurrentItem(String persistentId) { + protected I loadCurrentItem(String persistentId, boolean onlyActive) { // Here - why not to load VersionedItem, and then do getCurrentVersion() ? - // Because getCurrentVersion() returns Item, and we want the generic item type I + // Because getCurrentVersion() and getCurrentActiveVersion() returns Item, and we want the generic item type I // Hence, there is a dedicated method in the repository - do not remove - return getItemRepository().findCurrentVersion(persistentId).orElseThrow(() -> new EntityNotFoundException( - String.format("Unable to find current %s with id %s", getItemTypeName(), persistentId))); + return (onlyActive ? getItemRepository().findCurrentActiveVersion( + persistentId) : getItemRepository().findCurrentVersion(persistentId)).orElseThrow( + () -> new EntityNotFoundException( + String.format("Unable to find current %s with id %s", getItemTypeName(), persistentId))); + } + + /** + * Loads the most recent item for update. Does not need to be approved, but needs to be active. + * For the internal use only, as this method does not validate user access privileges + */ + protected I loadCurrentItem(String persistentId) { + return loadCurrentItem(persistentId, true); } @@ -166,6 +177,6 @@ protected Optional tryLoadLatestMergedItem(String persistentId) { while (isMerged(itemPersistentId)) { itemPersistentId = getItemRepository().findMergedWithPersistentId(itemPersistentId); } - return getItemRepository().findCurrentVersion(itemPersistentId); + return getItemRepository().findCurrentActiveVersion(itemPersistentId); } } diff --git a/src/main/java/eu/sshopencloud/marketplace/services/items/PublicationService.java b/src/main/java/eu/sshopencloud/marketplace/services/items/PublicationService.java index f6820020..2b487236 100644 --- a/src/main/java/eu/sshopencloud/marketplace/services/items/PublicationService.java +++ b/src/main/java/eu/sshopencloud/marketplace/services/items/PublicationService.java @@ -3,14 +3,12 @@ import eu.sshopencloud.marketplace.domain.media.MediaStorageService; import eu.sshopencloud.marketplace.dto.PageCoords; import eu.sshopencloud.marketplace.dto.auth.UserDto; -import eu.sshopencloud.marketplace.dto.datasets.DatasetDto; import eu.sshopencloud.marketplace.dto.items.ItemExtBasicDto; import eu.sshopencloud.marketplace.dto.items.ItemsDifferencesDto; import eu.sshopencloud.marketplace.dto.publications.PaginatedPublications; import eu.sshopencloud.marketplace.dto.publications.PublicationCore; import eu.sshopencloud.marketplace.dto.publications.PublicationDto; import eu.sshopencloud.marketplace.dto.sources.SourceDto; -import eu.sshopencloud.marketplace.mappers.datasets.DatasetMapper; import eu.sshopencloud.marketplace.mappers.publications.PublicationMapper; import eu.sshopencloud.marketplace.model.items.Item; import eu.sshopencloud.marketplace.model.publications.Publication; @@ -86,6 +84,11 @@ public PublicationDto revertPublication(String persistentId, long versionId) { return prepareItemDto(publication); } + public PublicationDto revertPublication(String persistentId) { + Publication publication = revertItemVersion(persistentId); + return prepareItemDto(publication); + } + public PublicationDto commitDraftPublication(String persistentId) { Publication publication = publishDraftItem(persistentId); return prepareItemDto(publication); diff --git a/src/main/java/eu/sshopencloud/marketplace/services/items/ToolService.java b/src/main/java/eu/sshopencloud/marketplace/services/items/ToolService.java index 398a848c..b551c673 100644 --- a/src/main/java/eu/sshopencloud/marketplace/services/items/ToolService.java +++ b/src/main/java/eu/sshopencloud/marketplace/services/items/ToolService.java @@ -3,14 +3,12 @@ import eu.sshopencloud.marketplace.domain.media.MediaStorageService; import eu.sshopencloud.marketplace.dto.PageCoords; import eu.sshopencloud.marketplace.dto.auth.UserDto; -import eu.sshopencloud.marketplace.dto.datasets.DatasetDto; import eu.sshopencloud.marketplace.dto.items.ItemExtBasicDto; import eu.sshopencloud.marketplace.dto.items.ItemsDifferencesDto; import eu.sshopencloud.marketplace.dto.sources.SourceDto; import eu.sshopencloud.marketplace.dto.tools.PaginatedTools; import eu.sshopencloud.marketplace.dto.tools.ToolCore; import eu.sshopencloud.marketplace.dto.tools.ToolDto; -import eu.sshopencloud.marketplace.mappers.datasets.DatasetMapper; import eu.sshopencloud.marketplace.mappers.tools.ToolMapper; import eu.sshopencloud.marketplace.model.items.Item; import eu.sshopencloud.marketplace.model.tools.Tool; @@ -86,6 +84,11 @@ public ToolDto revertTool(String persistentId, long versionId) { return prepareItemDto(tool); } + public ToolDto revertTool(String persistentId) { + Tool tool = revertItemVersion(persistentId); + return prepareItemDto(tool); + } + public ToolDto commitDraftTool(String persistentId) { Tool tool = publishDraftItem(persistentId); return prepareItemDto(tool); diff --git a/src/main/java/eu/sshopencloud/marketplace/services/items/TrainingMaterialService.java b/src/main/java/eu/sshopencloud/marketplace/services/items/TrainingMaterialService.java index fe3351fa..6d924ff4 100644 --- a/src/main/java/eu/sshopencloud/marketplace/services/items/TrainingMaterialService.java +++ b/src/main/java/eu/sshopencloud/marketplace/services/items/TrainingMaterialService.java @@ -3,14 +3,12 @@ import eu.sshopencloud.marketplace.domain.media.MediaStorageService; import eu.sshopencloud.marketplace.dto.PageCoords; import eu.sshopencloud.marketplace.dto.auth.UserDto; -import eu.sshopencloud.marketplace.dto.datasets.DatasetDto; import eu.sshopencloud.marketplace.dto.items.ItemExtBasicDto; import eu.sshopencloud.marketplace.dto.items.ItemsDifferencesDto; import eu.sshopencloud.marketplace.dto.sources.SourceDto; import eu.sshopencloud.marketplace.dto.trainings.PaginatedTrainingMaterials; import eu.sshopencloud.marketplace.dto.trainings.TrainingMaterialCore; import eu.sshopencloud.marketplace.dto.trainings.TrainingMaterialDto; -import eu.sshopencloud.marketplace.mappers.datasets.DatasetMapper; import eu.sshopencloud.marketplace.mappers.trainings.TrainingMaterialMapper; import eu.sshopencloud.marketplace.model.items.Item; import eu.sshopencloud.marketplace.model.trainings.TrainingMaterial; @@ -90,6 +88,11 @@ public TrainingMaterialDto revertTrainingMaterial(String persistentId, long vers return prepareItemDto(trainingMaterial); } + public TrainingMaterialDto revertTrainingMaterial(String persistentId) { + TrainingMaterial trainingMaterial = revertItemVersion(persistentId); + return prepareItemDto(trainingMaterial); + } + public TrainingMaterialDto commitDraftTrainingMaterial(String persistentId) { TrainingMaterial trainingMaterial = publishDraftItem(persistentId); return prepareItemDto(trainingMaterial); diff --git a/src/main/java/eu/sshopencloud/marketplace/services/items/WorkflowService.java b/src/main/java/eu/sshopencloud/marketplace/services/items/WorkflowService.java index cfca670f..5d1ac11b 100644 --- a/src/main/java/eu/sshopencloud/marketplace/services/items/WorkflowService.java +++ b/src/main/java/eu/sshopencloud/marketplace/services/items/WorkflowService.java @@ -148,6 +148,27 @@ public WorkflowDto revertWorkflow(String persistentId, long versionId) { Workflow revWorkflow = revertItemVersion(persistentId, versionId); return prepareItemDto(revWorkflow); } + public WorkflowDto revertWorkflow(String persistentId) { + User currentUser = LoggedInUserHolder.getLoggedInUser(); + if (!currentUser.isAdministrator()) + throw new AccessDeniedException("Current user is not an Administrator and is not allowed to revert workflow"); + + Workflow revWorkflow = revertItemVersion(persistentId); + + revWorkflow.getStepsTree().visit(new StepsTreeVisitor() { + @Override + public void onNextStep(StepsTree stepTree) { + Step step = stepTree.getStep(); + stepService.revertItemVersion(step.getPersistentId()); + } + + @Override + public void onBackToParent() { + } + }); + + return prepareItemDto(revWorkflow); + } public void deleteWorkflow(String persistentId, boolean draft) { if (draft) { diff --git a/src/main/java/eu/sshopencloud/marketplace/services/search/query/QueryParser.java b/src/main/java/eu/sshopencloud/marketplace/services/search/query/QueryParser.java index 8f9e190b..ce6afa39 100644 --- a/src/main/java/eu/sshopencloud/marketplace/services/search/query/QueryParser.java +++ b/src/main/java/eu/sshopencloud/marketplace/services/search/query/QueryParser.java @@ -26,7 +26,7 @@ public List parsePhrase(String phrase) { expression.append(token); } else { if (isAcceptableExpression(expression.toString())) { - result.add(new QueryPart(ClientUtils.escapeQueryChars(expression.toString()), false)); + result.add(new QueryPart(expression.toString(), false)); } expression = new StringBuilder(); } @@ -46,7 +46,9 @@ public List parsePhrase(String phrase) { } } } else { - expression.append(ClientUtils.escapeQueryChars(token)); + if (isAcceptableExpression(token)) { + expression.append(ClientUtils.escapeQueryChars(token)); + } } } if (isAcceptableExpression(expression.toString())) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ed630586..6fe8be07 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -26,7 +26,7 @@ spring: hibernate: dialect: org.hibernate.dialect.PostgreSQL9Dialect jdbc: - lob: + lob: non_contextual_creation: true jackson: @@ -66,13 +66,13 @@ spring: - eduperson_scoped_affiliation - eduperson_entitlement redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}" - client-name: EOSC + client-name: MyAccessId provider: eosc: - token-uri: ${aai_base_uri:https://aai-dev.egi.eu/oidc}/token - authorization-uri: ${aai_base_uri:https://aai-dev.egi.eu/oidc}/authorize - user-info-uri: ${aai_base_uri:https://aai-dev.egi.eu/oidc}/userinfo - jwk-set-uri: ${aai_base_uri:https://aai-dev.egi.eu/oidc}/jwk + token-uri: ${aai_base_uri:https://proxy.acc.myaccessid.org/OIDC}/token + authorization-uri: ${aai_base_uri:https://proxy.acc.myaccessid.org/OIDC}/authorization + user-info-uri: ${aai_base_uri:https://proxy.acc.myaccessid.org/OIDC}/userinfo + jwk-set-uri: ${aai_base_uri:https://proxy.acc.myaccessid.org/OIDC}/jwks user-name-attribute: sub profiles: @@ -177,4 +177,4 @@ management: endpoint: health: show-details: when_authorized - roles: 'ADMINISTRATOR' \ No newline at end of file + roles: 'ADMINISTRATOR' diff --git a/src/test/java/eu/sshopencloud/marketplace/controllers/datasets/DatasetControllerITCase.java b/src/test/java/eu/sshopencloud/marketplace/controllers/datasets/DatasetControllerITCase.java index 86b93fce..95e54d22 100644 --- a/src/test/java/eu/sshopencloud/marketplace/controllers/datasets/DatasetControllerITCase.java +++ b/src/test/java/eu/sshopencloud/marketplace/controllers/datasets/DatasetControllerITCase.java @@ -2468,4 +2468,62 @@ public void shouldNotRedirectToMergedDataset() throws Exception { .andExpect(status().is4xxClientError()); } + @Test + public void shouldDeleteAndRevertDataset() throws Exception { + + DatasetCore dataset = new DatasetCore(); + dataset.setLabel("Dataset to revert"); + dataset.setDescription("Lorem ipsum dolor"); + + String datasetPayload = mapper.writeValueAsString(dataset); + + String datasetJSON = mvc.perform( + post("/api/datasets") + .contentType(MediaType.APPLICATION_JSON) + .content(datasetPayload) + .header("Authorization", CONTRIBUTOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", notNullValue())) + .andExpect(jsonPath("id", notNullValue())) + .andExpect(jsonPath("category", is("dataset"))) + .andExpect(jsonPath("status", is("suggested"))) + .andExpect(jsonPath("label", is(dataset.getLabel()))) + .andExpect(jsonPath("description", is(dataset.getDescription()))) + .andReturn().getResponse().getContentAsString(); + + DatasetDto datasetDto = mapper.readValue(datasetJSON, DatasetDto.class); + + mvc.perform(delete("/api/datasets/{id}", datasetDto.getPersistentId()) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", ADMINISTRATOR_JWT)) + .andExpect(status().isOk()); + + mvc.perform( + put("/api/datasets/{id}/revert", datasetDto.getPersistentId()) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", ADMINISTRATOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", notNullValue())) + .andExpect(jsonPath("id", notNullValue())) + .andExpect(jsonPath("category", is("dataset"))) + .andExpect(jsonPath("status", is("approved"))) + .andExpect(jsonPath("label", is(dataset.getLabel()))) + .andExpect(jsonPath("description", is(dataset.getDescription()))) + .andReturn().getResponse().getContentAsString(); + + mvc.perform( + get("/api/datasets/{id}", datasetDto.getPersistentId()) + .param("approved", "true") + .header("Authorization", CONTRIBUTOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", is(datasetDto.getPersistentId()))) + .andExpect(jsonPath("id", is(datasetDto.getId().intValue()))) + .andExpect(jsonPath("category", is("dataset"))) + .andExpect(jsonPath("status", is("approved"))) + .andExpect(jsonPath("label", is(dataset.getLabel()))) + .andExpect(jsonPath("description", is(dataset.getDescription()))); + } } diff --git a/src/test/java/eu/sshopencloud/marketplace/controllers/publications/PublicationControllerITCase.java b/src/test/java/eu/sshopencloud/marketplace/controllers/publications/PublicationControllerITCase.java index 32bfcfca..e10a5c21 100644 --- a/src/test/java/eu/sshopencloud/marketplace/controllers/publications/PublicationControllerITCase.java +++ b/src/test/java/eu/sshopencloud/marketplace/controllers/publications/PublicationControllerITCase.java @@ -68,12 +68,14 @@ public class PublicationControllerITCase { private String CONTRIBUTOR_JWT; private String IMPORTER_JWT; private String MODERATOR_JWT; + private String ADMINISTRATOR_JWT; @Before public void init() throws Exception { CONTRIBUTOR_JWT = LogInTestClient.getJwt(mvc, "Contributor", "q1w2e3r4t5"); IMPORTER_JWT = LogInTestClient.getJwt(mvc, "System importer", "q1w2e3r4t5"); MODERATOR_JWT = LogInTestClient.getJwt(mvc, "Moderator", "q1w2e3r4t5"); + ADMINISTRATOR_JWT = LogInTestClient.getJwt(mvc, "Administrator", "q1w2e3r4t5"); } @Test @@ -188,7 +190,6 @@ public void shouldReturnPublicationsAndTheProposedOnes() throws Exception { .andExpect(jsonPath("publications[1].status", is("suggested"))); } - @Test public void shouldCreateSimplePublicationAsDraft() throws Exception { PublicationCore publication = new PublicationCore(); @@ -813,5 +814,64 @@ public void shouldReturnPublicationInformationContributorsForVersion() throws Ex .andExpect(jsonPath("$[1].email", is("contributor@example.com"))) .andExpect(jsonPath("$[1].config", is(true))); } + @Test + public void shouldDeleteAndRevertPublication() throws Exception { + PublicationCore publication1 = new PublicationCore(); + publication1.setLabel("Publication to revert"); + publication1.setDescription("Lorem ipsum dolor"); + + String payload1 = mapper.writeValueAsString(publication1); + + String publicationJson1 = mvc.perform( + post("/api/publications") + .contentType(MediaType.APPLICATION_JSON) + .content(payload1) + .header("Authorization", MODERATOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", notNullValue())) + .andExpect(jsonPath("id", notNullValue())) + .andExpect(jsonPath("category", is("publication"))) + .andExpect(jsonPath("status", is("approved"))) + .andExpect(jsonPath("label", is(publication1.getLabel()))) + .andExpect(jsonPath("description", is(publication1.getDescription()))) + .andReturn().getResponse().getContentAsString(); + + PublicationDto publicationDto1 = mapper.readValue(publicationJson1, PublicationDto.class); + String publicationId = publicationDto1.getPersistentId(); + + mvc.perform(delete("/api/publications/{id}", publicationId) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", ADMINISTRATOR_JWT)) + .andExpect(status().isOk()); + String publicationJson2 = mvc.perform( + put("/api/publications/{id}/revert", publicationId) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", ADMINISTRATOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", notNullValue())) + .andExpect(jsonPath("id", notNullValue())) + .andExpect(jsonPath("category", is("publication"))) + .andExpect(jsonPath("status", is("approved"))) + .andExpect(jsonPath("label", is(publication1.getLabel()))) + .andExpect(jsonPath("description", is(publication1.getDescription()))) + .andReturn().getResponse().getContentAsString(); + + PublicationDto publicationDto2 = mapper.readValue(publicationJson2, PublicationDto.class); + int publicationVersionId1 = publicationDto1.getId().intValue(); + mvc.perform( + get("/api/publications/{id}", publicationId) + .param("approved", "true") + .header("Authorization", CONTRIBUTOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", is(publicationId))) + .andExpect(jsonPath("id", is(publicationVersionId1))) + .andExpect(jsonPath("category", is("publication"))) + .andExpect(jsonPath("status", is("approved"))) + .andExpect(jsonPath("label", is(publication1.getLabel()))) + .andExpect(jsonPath("description", is(publication1.getDescription()))); + } } diff --git a/src/test/java/eu/sshopencloud/marketplace/controllers/tools/ToolControllerITCase.java b/src/test/java/eu/sshopencloud/marketplace/controllers/tools/ToolControllerITCase.java index 3f6a27c4..48019f4e 100644 --- a/src/test/java/eu/sshopencloud/marketplace/controllers/tools/ToolControllerITCase.java +++ b/src/test/java/eu/sshopencloud/marketplace/controllers/tools/ToolControllerITCase.java @@ -1677,6 +1677,62 @@ public void shouldCreateToolWithoutLineBreaksInLabel() throws Exception { .andExpect(jsonPath("properties", hasSize(0))); } + @Test + public void shouldDeleteAndRevertTool() throws Exception { + + ToolCore tool = new ToolCore(); + tool.setLabel("Tool to revert"); + tool.setDescription("Lorem ipsum dolor"); + + String toolPayload = mapper.writeValueAsString(tool); + + String toolJSON = mvc.perform( + post("/api/tools-services") + .contentType(MediaType.APPLICATION_JSON) + .content(toolPayload) + .header("Authorization", CONTRIBUTOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", notNullValue())) + .andExpect(jsonPath("id", notNullValue())) + .andExpect(jsonPath("category", is("tool-or-service"))) + .andExpect(jsonPath("status", is("suggested"))) + .andExpect(jsonPath("label", is(tool.getLabel()))) + .andExpect(jsonPath("description", is(tool.getDescription()))) + .andReturn().getResponse().getContentAsString(); + + ToolDto toolDto = mapper.readValue(toolJSON, ToolDto.class); + mvc.perform(delete("/api/tools-services/{id}", toolDto.getPersistentId()) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", ADMINISTRATOR_JWT)) + .andExpect(status().isOk()); + mvc.perform( + put("/api/tools-services/{id}/revert", toolDto.getPersistentId()) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", ADMINISTRATOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", notNullValue())) + .andExpect(jsonPath("id", notNullValue())) + .andExpect(jsonPath("category", is("tool-or-service"))) + .andExpect(jsonPath("status", is("approved"))) + .andExpect(jsonPath("label", is(tool.getLabel()))) + .andExpect(jsonPath("description", is(tool.getDescription()))) + .andReturn().getResponse().getContentAsString(); + + mvc.perform( + get("/api/tools-services/{id}", toolDto.getPersistentId()) + .param("approved", "true") + .header("Authorization", CONTRIBUTOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", is(toolDto.getPersistentId()))) + .andExpect(jsonPath("id", is(toolDto.getId().intValue()))) + .andExpect(jsonPath("category", is("tool-or-service"))) + .andExpect(jsonPath("status", is("approved"))) + .andExpect(jsonPath("label", is(tool.getLabel()))) + .andExpect(jsonPath("description", is(tool.getDescription()))); + } } diff --git a/src/test/java/eu/sshopencloud/marketplace/controllers/trainings/TrainingMaterialControllerITCase.java b/src/test/java/eu/sshopencloud/marketplace/controllers/trainings/TrainingMaterialControllerITCase.java index 910a466d..1fce99b7 100644 --- a/src/test/java/eu/sshopencloud/marketplace/controllers/trainings/TrainingMaterialControllerITCase.java +++ b/src/test/java/eu/sshopencloud/marketplace/controllers/trainings/TrainingMaterialControllerITCase.java @@ -2681,4 +2681,62 @@ public void shouldReturnDifferenceBetweenVersionsOfTrainingMaterials() throws Ex .andExpect(jsonPath("other.informationContributor.id", is(1))); } + @Test + public void shouldDeleteAndRevertTrainingMaterial() throws Exception { + + TrainingMaterialCore trainingMaterial1 = new TrainingMaterialCore(); + trainingMaterial1.setLabel("Abc: Test proposed training material"); + trainingMaterial1.setDescription("Lorem ipsum dolor"); + + String payload1 = mapper.writeValueAsString(trainingMaterial1); + + String trainingMaterialJson1 = mvc.perform( + post("/api/training-materials") + .contentType(MediaType.APPLICATION_JSON) + .content(payload1) + .header("Authorization", CONTRIBUTOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", notNullValue())) + .andExpect(jsonPath("id", notNullValue())) + .andExpect(jsonPath("category", is("training-material"))) + .andExpect(jsonPath("status", is("suggested"))) + .andExpect(jsonPath("label", is(trainingMaterial1.getLabel()))) + .andExpect(jsonPath("description", is(trainingMaterial1.getDescription()))) + .andReturn().getResponse().getContentAsString(); + + TrainingMaterialDto trainingMaterialDto1 = mapper.readValue(trainingMaterialJson1, TrainingMaterialDto.class); + + mvc.perform(delete("/api/training-materials/{id}", trainingMaterialDto1.getPersistentId()) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", ADMINISTRATOR_JWT)) + .andExpect(status().isOk()); + + mvc.perform( + put("/api/training-materials/{id}/revert", trainingMaterialDto1.getPersistentId()) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", ADMINISTRATOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", notNullValue())) + .andExpect(jsonPath("id", notNullValue())) + .andExpect(jsonPath("category", is("training-material"))) + .andExpect(jsonPath("status", is("approved"))) + .andExpect(jsonPath("label", is(trainingMaterial1.getLabel()))) + .andExpect(jsonPath("description", is(trainingMaterial1.getDescription()))) + .andReturn().getResponse().getContentAsString(); + + mvc.perform( + get("/api/training-materials/{id}", trainingMaterialDto1.getPersistentId()) + .param("approved", "true") + .header("Authorization", CONTRIBUTOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", is(trainingMaterialDto1.getPersistentId()))) + .andExpect(jsonPath("id", is(trainingMaterialDto1.getId().intValue()))) + .andExpect(jsonPath("category", is("training-material"))) + .andExpect(jsonPath("status", is("approved"))) + .andExpect(jsonPath("label", is(trainingMaterial1.getLabel()))) + .andExpect(jsonPath("description", is(trainingMaterial1.getDescription()))); + } } diff --git a/src/test/java/eu/sshopencloud/marketplace/controllers/workflows/WorkflowControllerITCase.java b/src/test/java/eu/sshopencloud/marketplace/controllers/workflows/WorkflowControllerITCase.java index b01fb3cc..7068e8f5 100644 --- a/src/test/java/eu/sshopencloud/marketplace/controllers/workflows/WorkflowControllerITCase.java +++ b/src/test/java/eu/sshopencloud/marketplace/controllers/workflows/WorkflowControllerITCase.java @@ -3008,4 +3008,62 @@ public void shouldNotReturnDifferenceBetweenComposedOf() throws Exception { .andExpect(jsonPath("other.composedOf[3]", nullValue())); } + @Test + public void shouldDeleteAndRevertWorkflow() throws Exception { + + WorkflowCore workflow = new WorkflowCore(); + workflow.setLabel("Workflow to revert"); + workflow.setDescription("Lorem ipsum dolor"); + + String workflowPayload = mapper.writeValueAsString(workflow); + + String workflowJSON = mvc.perform( + post("/api/workflows") + .contentType(MediaType.APPLICATION_JSON) + .content(workflowPayload) + .header("Authorization", CONTRIBUTOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", notNullValue())) + .andExpect(jsonPath("id", notNullValue())) + .andExpect(jsonPath("category", is("workflow"))) + .andExpect(jsonPath("status", is("suggested"))) + .andExpect(jsonPath("label", is(workflow.getLabel()))) + .andExpect(jsonPath("description", is(workflow.getDescription()))) + .andReturn().getResponse().getContentAsString(); + + WorkflowDto workflowDto = mapper.readValue(workflowJSON, WorkflowDto.class); + + mvc.perform(delete("/api/workflows/{id}", workflowDto.getPersistentId()) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", ADMINISTRATOR_JWT)) + .andExpect(status().isOk()); + + mvc.perform( + put("/api/workflows/{id}/revert", workflowDto.getPersistentId()) + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", ADMINISTRATOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", notNullValue())) + .andExpect(jsonPath("id", notNullValue())) + .andExpect(jsonPath("category", is("workflow"))) + .andExpect(jsonPath("status", is("approved"))) + .andExpect(jsonPath("label", is(workflow.getLabel()))) + .andExpect(jsonPath("description", is(workflow.getDescription()))) + .andReturn().getResponse().getContentAsString(); + + mvc.perform( + get("/api/workflows/{id}", workflowDto.getPersistentId()) + .param("approved", "true") + .header("Authorization", CONTRIBUTOR_JWT) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("persistentId", is(workflowDto.getPersistentId()))) + .andExpect(jsonPath("id", is(workflowDto.getId().intValue()))) + .andExpect(jsonPath("category", is("workflow"))) + .andExpect(jsonPath("status", is("approved"))) + .andExpect(jsonPath("label", is(workflow.getLabel()))) + .andExpect(jsonPath("description", is(workflow.getDescription()))); + } }