From 0409bd02380906803f46c9db661aaf4439867dc4 Mon Sep 17 00:00:00 2001 From: soimugeowb Date: Fri, 3 Jan 2025 15:58:22 +0200 Subject: [PATCH] fixed https://github.com/who-icatx/icatx-api-gateway/issues/19 --- .../controllers/ProjectsController.java | 21 +-- .../protege/gateway/dto/EntityChildren.java | 13 +- .../gateway/dto/EntityHistorySummary.java | 14 +- .../gateway/history/EntityHistoryService.java | 26 +++- .../gateway/ontology/OntologyService.java | 128 ++++------------- .../FilterExistingEntitiesResponse.java | 9 +- .../GetIsExistingProjectResponse.java | 4 + .../gateway/validators/ValidatorService.java | 129 ++++++++++++++++++ .../ontology/OwlOntologyServiceTest.java | 9 +- .../validators/ValidatorServiceTest.java | 119 ++++++++++++++++ 10 files changed, 341 insertions(+), 131 deletions(-) create mode 100644 src/main/java/edu/stanford/protege/gateway/validators/ValidatorService.java create mode 100644 src/test/java/edu/stanford/protege/gateway/validators/ValidatorServiceTest.java diff --git a/src/main/java/edu/stanford/protege/gateway/controllers/ProjectsController.java b/src/main/java/edu/stanford/protege/gateway/controllers/ProjectsController.java index 943ccad..bde885c 100644 --- a/src/main/java/edu/stanford/protege/gateway/controllers/ProjectsController.java +++ b/src/main/java/edu/stanford/protege/gateway/controllers/ProjectsController.java @@ -7,7 +7,8 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.NotNull; -import org.springframework.http.*; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -40,26 +41,26 @@ public ResponseEntity> getProjects() { @GetMapping(value = "/{projectId}") @Operation(summary = "Reading an entity", operationId = "2_getEntity") - public ResponseEntity getEntity(@PathVariable @javax.annotation.Nonnull String projectId, @RequestParam String entityIRI){ + public ResponseEntity getEntity(@PathVariable @javax.annotation.Nonnull String projectId, @RequestParam String entityIRI) { OWLEntityDto dto = owlEntityService.getEntityInfo(entityIRI, projectId); return getOwlEntityDtoResponseEntity(dto); } @PutMapping(value = "/{projectId}/entities") @Operation(summary = "Updating an entity", operationId = "3_updateEntity") - public ResponseEntity updateEntity( @RequestHeader(value = "If-Match", required = false) String ifMatch, - @PathVariable @Nonnull String projectId, - @RequestBody OWLEntityDto owlEntityDto){ - OWLEntityDto response = owlEntityService.updateEntity(owlEntityDto, projectId,ifMatch); + public ResponseEntity updateEntity(@RequestHeader(value = "If-Match", required = false) String ifMatch, + @PathVariable @Nonnull String projectId, + @RequestBody OWLEntityDto owlEntityDto) { + OWLEntityDto response = owlEntityService.updateEntity(owlEntityDto, projectId, ifMatch); return getOwlEntityDtoResponseEntity(response); } @PostMapping(value = "/{projectId}/entities") @Operation(summary = "Adding a new entity", operationId = "4_createEntity") public ResponseEntity createEntity(@PathVariable("projectId") - @NotNull(message = "Project ID cannot be null") - String projectId, - @RequestBody CreateEntityDto createEntityDto) { + @NotNull(message = "Project ID cannot be null") + String projectId, + @RequestBody CreateEntityDto createEntityDto) { var newCreatedIri = owlEntityService.createClassEntity(projectId, createEntityDto); OWLEntityDto result = owlEntityService.getEntityInfo(newCreatedIri, projectId); return getOwlEntityDtoResponseEntity(result); @@ -72,7 +73,7 @@ public ResponseEntity getEntityChildren(@PathVariable String pro List children = owlEntityService.getEntityChildren(entityIRI, projectId); return ResponseEntity.ok() - .body(EntityChildren.create(children)); + .body(EntityChildren.create(entityIRI, projectId, children)); } diff --git a/src/main/java/edu/stanford/protege/gateway/dto/EntityChildren.java b/src/main/java/edu/stanford/protege/gateway/dto/EntityChildren.java index 545cce6..7cb9cf4 100644 --- a/src/main/java/edu/stanford/protege/gateway/dto/EntityChildren.java +++ b/src/main/java/edu/stanford/protege/gateway/dto/EntityChildren.java @@ -4,8 +4,15 @@ import java.util.List; -public record EntityChildren(@JsonProperty("children") List children) { - public static EntityChildren create(List children) { - return new EntityChildren(children); +public record EntityChildren( + @JsonProperty("entityUri") String entityUri, + @JsonProperty("projectId") String projectId, + @JsonProperty("children") List children +) { + public static EntityChildren create( + String entityUri, + String projectId, + List children) { + return new EntityChildren(entityUri, projectId, children); } } diff --git a/src/main/java/edu/stanford/protege/gateway/dto/EntityHistorySummary.java b/src/main/java/edu/stanford/protege/gateway/dto/EntityHistorySummary.java index 843996f..c3d4522 100644 --- a/src/main/java/edu/stanford/protege/gateway/dto/EntityHistorySummary.java +++ b/src/main/java/edu/stanford/protege/gateway/dto/EntityHistorySummary.java @@ -1,9 +1,17 @@ package edu.stanford.protege.gateway.dto; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.List; -public record EntityHistorySummary(List changes) { - public static EntityHistorySummary create(List changes) { - return new EntityHistorySummary(changes); +public record EntityHistorySummary( + @JsonProperty("entityUri") String entityUri, + @JsonProperty("projectId") String projectId, + @JsonProperty("changes") List changes +) { + public static EntityHistorySummary create(String entityUri, + String projectId, + List changes) { + return new EntityHistorySummary(entityUri, projectId, changes); } } diff --git a/src/main/java/edu/stanford/protege/gateway/history/EntityHistoryService.java b/src/main/java/edu/stanford/protege/gateway/history/EntityHistoryService.java index e9c7f3b..0502e0e 100644 --- a/src/main/java/edu/stanford/protege/gateway/history/EntityHistoryService.java +++ b/src/main/java/edu/stanford/protege/gateway/history/EntityHistoryService.java @@ -1,12 +1,19 @@ package edu.stanford.protege.gateway.history; import edu.stanford.protege.gateway.SecurityContextHelper; -import edu.stanford.protege.gateway.dto.*; -import edu.stanford.protege.gateway.history.commands.*; +import edu.stanford.protege.gateway.dto.ChangedEntities; +import edu.stanford.protege.gateway.dto.EntityChange; +import edu.stanford.protege.gateway.dto.EntityHistorySummary; +import edu.stanford.protege.gateway.history.commands.GetChangedEntitiesRequest; +import edu.stanford.protege.gateway.history.commands.GetChangedEntitiesResponse; +import edu.stanford.protege.gateway.history.commands.GetEntityHistorySummaryRequest; +import edu.stanford.protege.gateway.history.commands.GetEntityHistorySummaryResponse; +import edu.stanford.protege.gateway.validators.ValidatorService; import edu.stanford.protege.webprotege.common.ProjectId; import edu.stanford.protege.webprotege.ipc.CommandExecutor; import edu.stanford.protege.webprotege.ipc.ExecutionContext; -import org.slf4j.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -21,16 +28,20 @@ public class EntityHistoryService { private final static Logger LOGGER = LoggerFactory.getLogger(EntityHistoryService.class); + private final ValidatorService validatorService; + private final CommandExecutor changedEntitiesExecutor; private final CommandExecutor entityHistorySummaryExecutor; - public EntityHistoryService(CommandExecutor changedEntitiesExecutor, + public EntityHistoryService(ValidatorService validatorService, CommandExecutor changedEntitiesExecutor, CommandExecutor entityHistorySummaryExecutor) { + this.validatorService = validatorService; this.changedEntitiesExecutor = changedEntitiesExecutor; this.entityHistorySummaryExecutor = entityHistorySummaryExecutor; } public ChangedEntities getChangedEntities(String projectId, Timestamp timestamp) { + validatorService.validateProjectId(projectId); try { return changedEntitiesExecutor.execute(GetChangedEntitiesRequest.create(ProjectId.valueOf(projectId), timestamp), SecurityContextHelper.getExecutionContext()) @@ -44,6 +55,8 @@ public ChangedEntities getChangedEntities(String projectId, Timestamp timestamp) } public EntityHistorySummary getEntityHistorySummary(String projectId, String entityIri) { + validatorService.validateProjectId(projectId); + validatorService.validateEntityExists(projectId, entityIri); try { return entityHistorySummaryExecutor.execute(GetEntityHistorySummaryRequest.create(projectId, entityIri), SecurityContextHelper.getExecutionContext()) .thenApply(GetEntityHistorySummaryResponse::entityHistorySummary) @@ -53,13 +66,16 @@ public EntityHistorySummary getEntityHistorySummary(String projectId, String ent throw new RuntimeException(e); } } - public CompletableFuture getEntityLatestChangeTime(String projectId, String entityIri){ + + public CompletableFuture getEntityLatestChangeTime(String projectId, String entityIri) { return getEntityLatestChangeTime(projectId, entityIri, SecurityContextHelper.getExecutionContext()); } @Async public CompletableFuture getEntityLatestChangeTime(String projectId, String entityIri, ExecutionContext executionContext) { + validatorService.validateProjectId(projectId); + validatorService.validateEntityExists(projectId, entityIri); return entityHistorySummaryExecutor.execute(new GetEntityHistorySummaryRequest(projectId, entityIri), executionContext) .thenApply(response -> { if (response.entityHistorySummary() != null && response.entityHistorySummary().changes() != null && response.entityHistorySummary().changes().size() > 0) { diff --git a/src/main/java/edu/stanford/protege/gateway/ontology/OntologyService.java b/src/main/java/edu/stanford/protege/gateway/ontology/OntologyService.java index ac8625a..130c3fb 100644 --- a/src/main/java/edu/stanford/protege/gateway/ontology/OntologyService.java +++ b/src/main/java/edu/stanford/protege/gateway/ontology/OntologyService.java @@ -1,31 +1,39 @@ package edu.stanford.protege.gateway.ontology; -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableSet; -import edu.stanford.protege.gateway.*; +import edu.stanford.protege.gateway.ApplicationException; +import edu.stanford.protege.gateway.SecurityContextHelper; import edu.stanford.protege.gateway.config.ApplicationBeans; import edu.stanford.protege.gateway.dto.*; import edu.stanford.protege.gateway.ontology.commands.*; -import edu.stanford.protege.webprotege.common.*; +import edu.stanford.protege.gateway.validators.ValidatorService; +import edu.stanford.protege.webprotege.common.ChangeRequestId; +import edu.stanford.protege.webprotege.common.ProjectId; import edu.stanford.protege.webprotege.ipc.CommandExecutor; import edu.stanford.protege.webprotege.ipc.ExecutionContext; -import org.semanticweb.owlapi.model.*; -import org.slf4j.*; +import org.semanticweb.owlapi.model.IRI; +import org.semanticweb.owlapi.model.OWLClass; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import uk.ac.manchester.cs.owl.owlapi.OWLClassImpl; -import java.text.MessageFormat; -import java.util.*; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @Service public class OntologyService { private final static Logger LOGGER = LoggerFactory.getLogger(OntologyService.class); + + private final ValidatorService validatorService; private final CommandExecutor ancestorsExecutor; private final CommandExecutor logicalDefinitionExecutor; private final CommandExecutor formDataExecutor; @@ -34,25 +42,22 @@ public class OntologyService { private final CommandExecutor updateLanguageTermsExecutor; private final CommandExecutor entityChildrenExecutor; - private final CommandExecutor isExistingProjectExecutor; - private final CommandExecutor filterExistingEntitiesExecutor; private final CommandExecutor createClassEntityExecutor; private final CommandExecutor getProjectsExecutor; private final CommandExecutor entityDiscussionExecutor; - public OntologyService(CommandExecutor ancestorsExecutor, + public OntologyService(ValidatorService validatorService, CommandExecutor ancestorsExecutor, CommandExecutor logicalDefinitionExecutor, CommandExecutor formDataExecutor, CommandExecutor entityChildrenExecutor, - CommandExecutor isExistingProjectExecutor, - CommandExecutor filterExistingEntitiesExecutor, CommandExecutor createClassEntityExecutor, CommandExecutor getProjectsExecutor, CommandExecutor entityDiscussionExecutor, CommandExecutor updateLogicalDefinitionExecutor, CommandExecutor updateParentsExecutor, CommandExecutor updateLanguageTermsExecutor) { + this.validatorService = validatorService; this.ancestorsExecutor = ancestorsExecutor; this.logicalDefinitionExecutor = logicalDefinitionExecutor; this.formDataExecutor = formDataExecutor; @@ -60,8 +65,6 @@ public OntologyService(CommandExecutor> getEntityParents(String entityIri, String @Async public CompletableFuture> getEntityParents(String entityIri, String projectId, ExecutionContext executionContext) { - + validatorService.validateProjectId(projectId); + validatorService.validateEntityExists(projectId,entityIri); return ancestorsExecutor.execute(new GetClassAncestorsRequest(IRI.create(entityIri), ProjectId.valueOf(projectId)),executionContext) .thenApply(response -> response.getAncestorClassHierarchy().getChildren().stream().map(child -> child.getNode().getEntity().getIRI().toString()) @@ -164,8 +168,8 @@ public void updateLanguageTerms(String entityIri, String projectId, String formI } public CompletableFuture> getEntityChildren(String entityIri, String projectId) { - validateProjectId(projectId); - validateEntityExists(projectId, entityIri); + validatorService.validateProjectId(projectId); + validatorService.validateEntityExists(projectId, entityIri); return entityChildrenExecutor.execute(GetEntityChildrenRequest.create(IRI.create(entityIri), ProjectId.valueOf(projectId)), SecurityContextHelper.getExecutionContext()) .thenApply( response -> response.childrenIris() @@ -175,24 +179,10 @@ public CompletableFuture> getEntityChildren(String entityIri, Strin ); } - public CompletableFuture isExistingProject(String projectId) { - return isExistingProjectExecutor.execute(GetIsExistingProjectRequest.create(ProjectId.valueOf(projectId)), SecurityContextHelper.getExecutionContext()) - .thenApply(GetIsExistingProjectResponse::isExistingProject); - } - public CompletableFuture> getExistingEntities(String projectId, String entity) { - var entityIri = IRI.create(entity); - return filterExistingEntitiesExecutor.execute(FilterExistingEntitiesRequest.create(ProjectId.valueOf(projectId), ImmutableSet.of(entityIri)), SecurityContextHelper.getExecutionContext()) - .thenApply( - response -> response.existingEntities() - .stream() - .map(IRI::toString) - .collect(Collectors.toSet()) - ); - } public CompletableFuture createClassEntity(String projectId, CreateEntityDto createEntityDto) { - validateCreateEntityRequest(projectId, createEntityDto); + validatorService.validateCreateEntityRequest(projectId, createEntityDto); return createClassEntityExecutor.execute( CreateClassesFromApiRequest.create( ChangeRequestId.generate(), @@ -222,77 +212,5 @@ public CompletableFuture getEntityDiscussionThreads(String entit } - public void validateCreateEntityRequest(String projectId, CreateEntityDto createEntityDto) { - validateTitle(createEntityDto.title()); - validateProjectId(projectId); - validateEntityParents(projectId, createEntityDto.parent()); - } - - private void validateTitle(String title) { - if(title == null || title.isBlank()){ - throw new IllegalArgumentException("Title title cannot be empty"); - } - if (hasEscapeCharacters(title)) { - throw new IllegalArgumentException(MessageFormat.format("Title has escape characters: {0}. please remove any escape characters", title)); - } - } - - private void validateProjectId(String projectId) { - boolean projectExists; - try { - projectExists = this.isExistingProject(projectId).get(); - } catch (InterruptedException | ExecutionException e) { - LOGGER.error("Could not verify if projectId:" + projectId + " is valid!", e); - throw new RuntimeException(e); - } - if (!projectExists) { - throw new IllegalArgumentException("Invalid Project ID: " + projectId); - } - } - - private void validateEntityParents(String projectId, String parent) { - if (parent == null || parent.isEmpty()) { - throw new IllegalArgumentException("At least a parent should be specified!"); - } - Set existingParents; - try { - existingParents = this.getExistingEntities(projectId, parent).get(); - } catch (InterruptedException | ExecutionException e) { - LOGGER.error("Could not verify if parent:" + parent + " is valid!", e); - throw new RuntimeException(e); - } - boolean isValid = existingParents.stream().anyMatch(existingParent -> existingParent.equals(parent)); - if (!isValid) { - throw new IllegalArgumentException("Invalid Entity Parent: " + parent); - } - } - - private void validateEntityExists(String projectId, String entity){ - if (entity == null || entity.isEmpty()) { - throw new IllegalArgumentException("At least an entityUri should be specified!"); - } - Set existingEntities; - try { - existingEntities = this.getExistingEntities(projectId, entity).get(); - } catch (InterruptedException | ExecutionException e) { - LOGGER.error("Could not verify if parent:" + entity + " is valid!", e); - throw new RuntimeException(e); - } - boolean isValid = existingEntities.stream().anyMatch(existingParent -> existingParent.equals(entity)); - if (!isValid) { - throw new EntityIsMissingException("Invalid Entity IRI: " + entity); - } - } - public static boolean hasEscapeCharacters(String input) { - for (int i = 0; i < input.length() - 1; i++) { - if (input.charAt(i) == '\\') { - char nextChar = input.charAt(i + 1); - if ("ntbrf\"'\\".indexOf(nextChar) != -1) { - return true; - } - } - } - return false; - } } diff --git a/src/main/java/edu/stanford/protege/gateway/ontology/commands/FilterExistingEntitiesResponse.java b/src/main/java/edu/stanford/protege/gateway/ontology/commands/FilterExistingEntitiesResponse.java index 7e5bb78..495a4c9 100644 --- a/src/main/java/edu/stanford/protege/gateway/ontology/commands/FilterExistingEntitiesResponse.java +++ b/src/main/java/edu/stanford/protege/gateway/ontology/commands/FilterExistingEntitiesResponse.java @@ -1,6 +1,7 @@ package edu.stanford.protege.gateway.ontology.commands; -import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; import edu.stanford.protege.webprotege.common.Response; import org.semanticweb.owlapi.model.IRI; @@ -9,5 +10,9 @@ import static edu.stanford.protege.gateway.ontology.commands.FilterExistingEntitiesRequest.CHANNEL; @JsonTypeName(CHANNEL) -public record FilterExistingEntitiesResponse(@JsonProperty("existingEntities") Set existingEntities) implements Response { +public record FilterExistingEntitiesResponse( + @JsonProperty("existingEntities") Set existingEntities) implements Response { + public static FilterExistingEntitiesResponse create(Set existingEntities) { + return new FilterExistingEntitiesResponse(existingEntities); + } } diff --git a/src/main/java/edu/stanford/protege/gateway/ontology/commands/GetIsExistingProjectResponse.java b/src/main/java/edu/stanford/protege/gateway/ontology/commands/GetIsExistingProjectResponse.java index 7cd007e..8ddeb51 100644 --- a/src/main/java/edu/stanford/protege/gateway/ontology/commands/GetIsExistingProjectResponse.java +++ b/src/main/java/edu/stanford/protege/gateway/ontology/commands/GetIsExistingProjectResponse.java @@ -9,4 +9,8 @@ public record GetIsExistingProjectResponse( @JsonProperty("isExistingProject") boolean isExistingProject ) implements Response { + + public static GetIsExistingProjectResponse create(boolean isExistingProject){ + return new GetIsExistingProjectResponse(isExistingProject); + } } diff --git a/src/main/java/edu/stanford/protege/gateway/validators/ValidatorService.java b/src/main/java/edu/stanford/protege/gateway/validators/ValidatorService.java new file mode 100644 index 0000000..f657fd8 --- /dev/null +++ b/src/main/java/edu/stanford/protege/gateway/validators/ValidatorService.java @@ -0,0 +1,129 @@ +package edu.stanford.protege.gateway.validators; + +import com.google.common.collect.ImmutableSet; +import edu.stanford.protege.gateway.EntityIsMissingException; +import edu.stanford.protege.gateway.SecurityContextHelper; +import edu.stanford.protege.gateway.dto.CreateEntityDto; +import edu.stanford.protege.gateway.ontology.OntologyService; +import edu.stanford.protege.gateway.ontology.commands.FilterExistingEntitiesRequest; +import edu.stanford.protege.gateway.ontology.commands.FilterExistingEntitiesResponse; +import edu.stanford.protege.gateway.ontology.commands.GetIsExistingProjectRequest; +import edu.stanford.protege.gateway.ontology.commands.GetIsExistingProjectResponse; +import edu.stanford.protege.webprotege.common.ProjectId; +import edu.stanford.protege.webprotege.ipc.CommandExecutor; +import org.semanticweb.owlapi.model.IRI; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.text.MessageFormat; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +@Service +public class ValidatorService { + + private final static Logger LOGGER = LoggerFactory.getLogger(OntologyService.class); + + private final CommandExecutor isExistingProjectExecutor; + private final CommandExecutor filterExistingEntitiesExecutor; + + + public ValidatorService(CommandExecutor isExistingProjectExecutor, CommandExecutor filterExistingEntitiesExecutor) { + this.isExistingProjectExecutor = isExistingProjectExecutor; + this.filterExistingEntitiesExecutor = filterExistingEntitiesExecutor; + } + + + public void validateCreateEntityRequest(String projectId, CreateEntityDto createEntityDto) { + validateTitle(createEntityDto.title()); + validateProjectId(projectId); + validateEntityParents(projectId, createEntityDto.parent()); + } + + private CompletableFuture isExistingProject(String projectId) { + return isExistingProjectExecutor.execute(GetIsExistingProjectRequest.create(ProjectId.valueOf(projectId)), SecurityContextHelper.getExecutionContext()) + .thenApply(GetIsExistingProjectResponse::isExistingProject); + } + + private CompletableFuture> getExistingEntities(String projectId, String entity) { + var entityIri = IRI.create(entity); + return filterExistingEntitiesExecutor.execute(FilterExistingEntitiesRequest.create(ProjectId.valueOf(projectId), ImmutableSet.of(entityIri)), SecurityContextHelper.getExecutionContext()) + .thenApply( + response -> response.existingEntities() + .stream() + .map(IRI::toString) + .collect(Collectors.toSet()) + ); + } + + private void validateTitle(String title) { + if(title == null || title.isBlank()){ + throw new IllegalArgumentException("Title cannot be empty"); + } + if (hasEscapeCharacters(title)) { + throw new IllegalArgumentException(MessageFormat.format("Title has escape characters: {0}. please remove any escape characters", title)); + } + } + + public void validateProjectId(String projectId) { + boolean projectExists; + try { + projectExists = this.isExistingProject(projectId).get(); + } catch (InterruptedException | ExecutionException e) { + LOGGER.error("Could not verify if projectId:" + projectId + " is valid!", e); + throw new RuntimeException(e); + } + if (!projectExists) { + throw new IllegalArgumentException("Invalid Project ID: " + projectId); + } + } + + private void validateEntityParents(String projectId, String parent) { + if (parent == null || parent.isEmpty()) { + throw new IllegalArgumentException("At least a parent should be specified!"); + } + Set existingParents; + try { + existingParents = this.getExistingEntities(projectId, parent).get(); + } catch (InterruptedException | ExecutionException e) { + LOGGER.error("Could not verify if parent:" + parent + " is valid!", e); + throw new RuntimeException(e); + } + boolean isValid = existingParents.stream().anyMatch(existingParent -> existingParent.equals(parent)); + if (!isValid) { + throw new IllegalArgumentException("Invalid Entity Parent: " + parent); + } + } + + public void validateEntityExists(String projectId, String entity){ + if (entity == null || entity.isEmpty()) { + throw new IllegalArgumentException("At least an entityUri should be specified!"); + } + Set existingEntities; + try { + existingEntities = this.getExistingEntities(projectId, entity).get(); + } catch (InterruptedException | ExecutionException e) { + LOGGER.error("Could not verify if parent:" + entity + " is valid!", e); + throw new RuntimeException(e); + } + boolean isValid = existingEntities.stream().anyMatch(existingParent -> existingParent.equals(entity)); + if (!isValid) { + throw new EntityIsMissingException("Invalid Entity IRI: " + entity); + } + } + + public static boolean hasEscapeCharacters(String input) { + for (int i = 0; i < input.length() - 1; i++) { + if (input.charAt(i) == '\\') { + char nextChar = input.charAt(i + 1); + if ("ntbrf\"'\\".indexOf(nextChar) != -1) { + return true; + } + } + } + return false; + } +} diff --git a/src/test/java/edu/stanford/protege/gateway/ontology/OwlOntologyServiceTest.java b/src/test/java/edu/stanford/protege/gateway/ontology/OwlOntologyServiceTest.java index 49f5ca2..441178a 100644 --- a/src/test/java/edu/stanford/protege/gateway/ontology/OwlOntologyServiceTest.java +++ b/src/test/java/edu/stanford/protege/gateway/ontology/OwlOntologyServiceTest.java @@ -5,6 +5,7 @@ import edu.stanford.protege.gateway.config.ApplicationBeans; import edu.stanford.protege.gateway.dto.*; import edu.stanford.protege.gateway.ontology.commands.*; +import edu.stanford.protege.gateway.validators.ValidatorService; import edu.stanford.protege.webprotege.common.ProjectId; import edu.stanford.protege.webprotege.ipc.CommandExecutor; import org.junit.jupiter.api.*; @@ -61,6 +62,9 @@ public class OwlOntologyServiceTest { private CommandExecutor entityDiscussionExecutor; private GetLogicalDefinitionsResponse response; + @Mock + private ValidatorService validatorService; + private ProjectId projectId; private String entityIri; @@ -72,12 +76,11 @@ public void setUp() throws IOException { .setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY)); File specFile = new File("src/test/resources/dummyLogicalDefinitionResponse.json"); response = objectMapper.readValue(specFile, GetLogicalDefinitionsResponse.class); - service = new OntologyService(ancestorsExecutor, + service = new OntologyService(validatorService, + ancestorsExecutor, logicalDefinitionExecutor, formDataExecutor, entityChildrenExecutor, - isExistingProjectExecutor, - filterExistingEntitiesExecutor, createClassEntityExecutor, getAvailableProjectsExecutor, entityDiscussionExecutor, diff --git a/src/test/java/edu/stanford/protege/gateway/validators/ValidatorServiceTest.java b/src/test/java/edu/stanford/protege/gateway/validators/ValidatorServiceTest.java new file mode 100644 index 0000000..8a7e7f1 --- /dev/null +++ b/src/test/java/edu/stanford/protege/gateway/validators/ValidatorServiceTest.java @@ -0,0 +1,119 @@ +package edu.stanford.protege.gateway.validators; + +import edu.stanford.protege.gateway.EntityIsMissingException; +import edu.stanford.protege.gateway.dto.CreateEntityDto; +import edu.stanford.protege.gateway.ontology.commands.FilterExistingEntitiesRequest; +import edu.stanford.protege.gateway.ontology.commands.FilterExistingEntitiesResponse; +import edu.stanford.protege.gateway.ontology.commands.GetIsExistingProjectRequest; +import edu.stanford.protege.gateway.ontology.commands.GetIsExistingProjectResponse; +import edu.stanford.protege.webprotege.ipc.CommandExecutor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.semanticweb.owlapi.model.IRI; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +class ValidatorServiceTest { + + @Mock + private CommandExecutor isExistingProjectExecutor; + + @Mock + private CommandExecutor filterExistingEntitiesExecutor; + + private ValidatorService validatorService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + validatorService = new ValidatorService(isExistingProjectExecutor, filterExistingEntitiesExecutor); + } + + @Test + void GIVEN_validInputs_WHEN_validateCreateEntityRequestCalled_THEN_noExceptionThrown() { + // GIVEN + String projectId = "76bdeeb8-2d8e-4220-9185-0265af09c448"; + CreateEntityDto createEntityDto = new CreateEntityDto("Valid Title", "http://example.com/parent", null); + IRI parentIri = IRI.create(createEntityDto.parent()); + + when(isExistingProjectExecutor.execute(any(), any())) + .thenReturn(CompletableFuture.completedFuture(GetIsExistingProjectResponse.create(true))); + + when(filterExistingEntitiesExecutor.execute(any(), any())) + .thenReturn(CompletableFuture.completedFuture(FilterExistingEntitiesResponse.create(Set.of(parentIri)))); + + assertDoesNotThrow(() -> validatorService.validateCreateEntityRequest(projectId, createEntityDto), "should not throw exception"); + } + + + @Test + void GIVEN_invalidProjectId_WHEN_validateCreateEntityRequestCalled_THEN_throwsIllegalArgumentException() { + String projectId = "76bdeeb8-2d8e-4220-9185-0265af09c448"; + CreateEntityDto createEntityDto = new CreateEntityDto("Valid Title", "http://example.com/parent", null); + + when(isExistingProjectExecutor.execute(any(), any())) + .thenReturn(CompletableFuture.completedFuture(GetIsExistingProjectResponse.create(false))); + + assertThrows(IllegalArgumentException.class, () -> validatorService.validateCreateEntityRequest(projectId, createEntityDto)); + } + + @Test + void GIVEN_invalidParent_WHEN_validateCreateEntityRequestCalled_THEN_throwsIllegalArgumentException() { + String projectId = "76bdeeb8-2d8e-4220-9185-0265af09c448"; + CreateEntityDto createEntityDto = new CreateEntityDto("Valid Title", "http://example.com/invalid-parent", null); + + when(isExistingProjectExecutor.execute(any(), any())) + .thenReturn(CompletableFuture.completedFuture(GetIsExistingProjectResponse.create(true))); + + when(filterExistingEntitiesExecutor.execute(any(), any())) + .thenReturn(CompletableFuture.completedFuture(FilterExistingEntitiesResponse.create(Collections.emptySet()))); + + assertThrows(IllegalArgumentException.class, () -> validatorService.validateCreateEntityRequest(projectId, createEntityDto)); + } + + @Test + void GIVEN_invalidTitle_WHEN_validateCreateEntityRequestCalled_THEN_throwsIllegalArgumentException() { + String projectId = "76bdeeb8-2d8e-4220-9185-0265af09c448"; + CreateEntityDto createEntityDto = new CreateEntityDto("Invalid\\nTitle", "http://example.com/parent", null); + + when(isExistingProjectExecutor.execute(any(), any())) + .thenReturn(CompletableFuture.completedFuture(GetIsExistingProjectResponse.create(true))); + + when(filterExistingEntitiesExecutor.execute(any(), any())) + .thenReturn(CompletableFuture.completedFuture(FilterExistingEntitiesResponse.create(Set.of(IRI.create(createEntityDto.parent()))))); + + assertThrows(IllegalArgumentException.class, () -> validatorService.validateCreateEntityRequest(projectId, createEntityDto)); + } + + @Test + void GIVEN_validInputs_WHEN_validateEntityExistsCalled_THEN_noExceptionThrown() throws Exception { + String projectId = "76bdeeb8-2d8e-4220-9185-0265af09c448"; + String entity = "http://example.com/entity"; + IRI entityIri = IRI.create(entity); + + when(filterExistingEntitiesExecutor.execute(any(), any())) + .thenReturn(CompletableFuture.completedFuture(FilterExistingEntitiesResponse.create(Set.of(entityIri)))); + + assertDoesNotThrow(() -> validatorService.validateEntityExists(projectId, entity)); + + } + + @Test + void GIVEN_nonexistentEntity_WHEN_validateEntityExistsCalled_THEN_throwsEntityIsMissingException() { + String projectId = "76bdeeb8-2d8e-4220-9185-0265af09c448"; + String entity = "http://example.com/nonexistent-entity"; + + when(filterExistingEntitiesExecutor.execute(any(), any())) + .thenReturn(CompletableFuture.completedFuture(FilterExistingEntitiesResponse.create(Collections.emptySet()))); + + assertThrows(EntityIsMissingException.class, () -> validatorService.validateEntityExists(projectId, entity)); + } +}