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 1bd4c04..a0b8861 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;
- }
-}