From c6dc453e912eb5109c4ff15b75b566c3b9db055f Mon Sep 17 00:00:00 2001 From: soimugeowb Date: Mon, 30 Dec 2024 13:53:59 +0200 Subject: [PATCH 1/2] Added validation on get-entity-children --- pom.xml | 2 +- .../protege/gateway/OwlEntityService.java | 13 ++- .../controllers/ProjectsController.java | 6 +- .../gateway/ontology/OntologyService.java | 86 ++++++++++++++++++- .../CreateEntityValidatorService.java | 79 ----------------- src/main/resources/application.yaml | 2 +- 6 files changed, 91 insertions(+), 97 deletions(-) delete mode 100644 src/main/java/edu/stanford/protege/gateway/ontology/validators/CreateEntityValidatorService.java diff --git a/pom.xml b/pom.xml index b9c91cb..5fea976 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ edu.stanford.protege icatx-api-gateway - 0.0.2 + 0.0.3 icatx-api-gateway This microservice is the api gateway for the iCAT-X project. diff --git a/src/main/java/edu/stanford/protege/gateway/OwlEntityService.java b/src/main/java/edu/stanford/protege/gateway/OwlEntityService.java index 91b3a67..5463191 100644 --- a/src/main/java/edu/stanford/protege/gateway/OwlEntityService.java +++ b/src/main/java/edu/stanford/protege/gateway/OwlEntityService.java @@ -12,20 +12,17 @@ import edu.stanford.protege.webprotege.common.ProjectId; import edu.stanford.protege.webprotege.ipc.EventDispatcher; import edu.stanford.protege.webprotege.ipc.ExecutionContext; -import edu.stanford.protege.webprotege.ipc.ExecutionContext; -import org.slf4j.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import javax.annotation.Nonnull; import java.nio.charset.StandardCharsets; -import java.time.Instant; import java.time.LocalDateTime; -import java.time.ZoneOffset; -import javax.annotation.Nonnull; import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.ExecutionException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; @Service @@ -113,7 +110,7 @@ public String createClassEntity(String projectId, CreateEntityDto createEntityDt CompletableFuture newCreatedEntityIri = ontologyService.createClassEntity(projectId, createEntityDto); try { return newCreatedEntityIri.get(); - } catch (Exception e) { + } catch (InterruptedException | ExecutionException e) { LOGGER.error("Error creating new class entity " + createEntityDto.title(), e); /* ToDo: 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 3347646..943ccad 100644 --- a/src/main/java/edu/stanford/protege/gateway/controllers/ProjectsController.java +++ b/src/main/java/edu/stanford/protege/gateway/controllers/ProjectsController.java @@ -4,7 +4,6 @@ import com.google.common.hash.Hashing; import edu.stanford.protege.gateway.OwlEntityService; import edu.stanford.protege.gateway.dto.*; -import edu.stanford.protege.gateway.ontology.validators.CreateEntityValidatorService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.NotNull; @@ -24,11 +23,9 @@ public class ProjectsController { private final OwlEntityService owlEntityService; - private final CreateEntityValidatorService createEntityValidator; - public ProjectsController(OwlEntityService owlEntityService, CreateEntityValidatorService createEntityValidator) { + public ProjectsController(OwlEntityService owlEntityService) { this.owlEntityService = owlEntityService; - this.createEntityValidator = createEntityValidator; } @@ -63,7 +60,6 @@ public ResponseEntity createEntity(@PathVariable("projectId") @NotNull(message = "Project ID cannot be null") String projectId, @RequestBody CreateEntityDto createEntityDto) { - createEntityValidator.validateCreateEntityRequest(projectId, createEntityDto); var newCreatedIri = owlEntityService.createClassEntity(projectId, createEntityDto); OWLEntityDto result = owlEntityService.getEntityInfo(newCreatedIri, projectId); return getOwlEntityDtoResponseEntity(result); 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 6a358ac..ac8625a 100644 --- a/src/main/java/edu/stanford/protege/gateway/ontology/OntologyService.java +++ b/src/main/java/edu/stanford/protege/gateway/ontology/OntologyService.java @@ -16,8 +16,10 @@ import org.springframework.stereotype.Service; import uk.ac.manchester.cs.owl.owlapi.OWLClassImpl; +import java.text.MessageFormat; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @Service @@ -162,6 +164,8 @@ public void updateLanguageTerms(String entityIri, String projectId, String formI } public CompletableFuture> getEntityChildren(String entityIri, String projectId) { + validateProjectId(projectId); + validateEntityExists(projectId, entityIri); return entityChildrenExecutor.execute(GetEntityChildrenRequest.create(IRI.create(entityIri), ProjectId.valueOf(projectId)), SecurityContextHelper.getExecutionContext()) .thenApply( response -> response.childrenIris() @@ -176,9 +180,9 @@ public CompletableFuture isExistingProject(String projectId) { .thenApply(GetIsExistingProjectResponse::isExistingProject); } - public CompletableFuture> getExistingEntities(String projectId, String parent) { - var parentIri = IRI.create(parent); - return filterExistingEntitiesExecutor.execute(FilterExistingEntitiesRequest.create(ProjectId.valueOf(projectId), ImmutableSet.of(parentIri)), SecurityContextHelper.getExecutionContext()) + 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() @@ -188,6 +192,7 @@ public CompletableFuture> getExistingEntities(String projectId, Stri } public CompletableFuture createClassEntity(String projectId, CreateEntityDto createEntityDto) { + validateCreateEntityRequest(projectId, createEntityDto); return createClassEntityExecutor.execute( CreateClassesFromApiRequest.create( ChangeRequestId.generate(), @@ -215,4 +220,79 @@ public CompletableFuture getEntityDiscussionThreads(String entit return entityDiscussionExecutor.execute(GetEntityCommentsRequest.create(ProjectId.valueOf(projectId), entityIri), SecurityContextHelper.getExecutionContext()) .thenApply(GetEntityCommentsResponse::comments); } + + + 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/validators/CreateEntityValidatorService.java b/src/main/java/edu/stanford/protege/gateway/ontology/validators/CreateEntityValidatorService.java deleted file mode 100644 index 3b611c4..0000000 --- a/src/main/java/edu/stanford/protege/gateway/ontology/validators/CreateEntityValidatorService.java +++ /dev/null @@ -1,79 +0,0 @@ -package edu.stanford.protege.gateway.ontology.validators; - -import edu.stanford.protege.gateway.dto.CreateEntityDto; -import edu.stanford.protege.gateway.ontology.OntologyService; -import org.slf4j.*; -import org.springframework.stereotype.Service; - -import java.text.MessageFormat; -import java.util.Set; -import java.util.concurrent.ExecutionException; - -@Service -public class CreateEntityValidatorService { - - private final static Logger LOGGER = LoggerFactory.getLogger(CreateEntityValidatorService.class); - - private final OntologyService entityOntService; - - public CreateEntityValidatorService(OntologyService entityOntService) { - this.entityOntService = entityOntService; - } - - 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: $s. please remove any escape characters", title)); - } - } - - private void validateProjectId(String projectId) { - boolean projectExists; - try { - projectExists = entityOntService.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 = entityOntService.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 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/resources/application.yaml b/src/main/resources/application.yaml index 61b5678..9e59e29 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -18,7 +18,7 @@ spring: application: name: icatx-api-gateway rabbitmq: - host: localhost + host: rabbitmq port: 5672 username: guest password: guest From 6b9adf74f31fb0465fc3f3032452dee009fa154e Mon Sep 17 00:00:00 2001 From: soimugeowb Date: Mon, 30 Dec 2024 13:59:46 +0200 Subject: [PATCH 2/2] added github workflows --- .github/workflows/ci.yaml | 22 ++++++++++++++ .github/workflows/release.yaml | 55 ++++++++++++++++++++++++++++++++++ pom.xml | 15 ++++++++++ 3 files changed, 92 insertions(+) create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..477ff03 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,22 @@ +name: Verify + +on: + push: + branches-ignore: + - master + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'adopt' + - name: Build with Maven + run: mvn --batch-mode package diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..5ceeb7d --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,55 @@ +name: Release + +on: + push: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + if: ${{ github.actor != 'who-icatx-bot[bot]' }} + steps: + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{secrets.DOCKER_USERNAME}} + password: ${{secrets.DOCKER_PASSWORD}} + - uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ vars.ICATX_BOT_APP_ID }} + private-key: ${{ secrets.ICATX_BOT_APP_PRIVATE_KEY }} + - uses: actions/checkout@v4 + with: + token: ${{ steps.app-token.outputs.token }} + ref: ${{ github.head_ref }} + - name: Set up Maven Central Repository + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'adopt' + server-id: docker.io + server-username: DOCKER_USERNAME + server-password: DOCKER_PASSWORD + - name: Bump version + id: bump + uses: mickem/gh-action-bump-maven-version@v1 + - name: Build package + run: mvn --batch-mode clean package + - name: Build and push image + run: mvn --batch-mode package install + - name: Release + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.bump.outputs.tag }} + generate_release_notes: true + +env: + DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}} + DOCKER_TOKEN: ${{secrets.DOCKER_PASSWORD}} + +permissions: + contents: write \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5fea976..a0b8861 100644 --- a/pom.xml +++ b/pom.xml @@ -100,6 +100,21 @@ org.springframework.boot spring-boot-maven-plugin + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0 + + + test + + + **/*TestCase.java + **/*Test.java + **/*IT.java + + + org.codehaus.mojo exec-maven-plugin