diff --git a/Dockerfile b/Dockerfile index 2c152c5efd..9692903d52 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,9 +10,6 @@ COPY .mvn .mvn COPY settings.xml . COPY pom.xml . -COPY connector/pom.xml connector/pom.xml -COPY connector/edc-recursive-job connector/edc-recursive-job -COPY connector/irs-connector-parent connector/irs-connector-parent COPY integration-tests integration-tests COPY irs-api irs-api COPY irs-common irs-common diff --git a/api/irs-v1.0.yaml b/api/irs-v1.0.yaml index 81c20625d6..696f8e57b3 100644 --- a/api/irs-v1.0.yaml +++ b/api/irs-v1.0.yaml @@ -609,7 +609,7 @@ components: type: string format: date-time exception: - $ref: '#/components/schemas/JobException' + $ref: '#/components/schemas/JobErrorDetails' globalAssetId: $ref: '#/components/schemas/GlobalAssetIdentification' jobCompleted: @@ -651,7 +651,7 @@ components: - globalAssetId - jobId - jobState - JobException: + JobErrorDetails: type: object description: Exception state for this job. properties: diff --git a/ci/checkstyle-suppressions.xml b/ci/checkstyle-suppressions.xml index 33616c8977..dcbb0a35aa 100644 --- a/ci/checkstyle-suppressions.xml +++ b/ci/checkstyle-suppressions.xml @@ -1,7 +1,7 @@ + "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN" + "https://checkstyle.org/dtds/suppressions_1_2.dtd"> diff --git a/connector/README.md b/connector/README.md deleted file mode 100644 index 539adac767..0000000000 --- a/connector/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# Provider & Consumer Connector - -## Git token - -Create a [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) in GitHub, limited to **read:packages** scope. - -Configure the following GitHub *repository secrets*: - -- IRS_EDC_PKG_USERNAME (your_github_username) -- IRS_EDC_PKG_PASSWORD (your_github_pat_token) - -## Local build - -Copy `settings.xml` into your `~/.m2/` folder (or merge it with a file already there), and replace the environment variable references with the following: - -```xml -your_github_username -your_github_pat_token -``` - -## Docker build - -See `run-integration-test.sh` file. - -## Running tests - -Download certificate for the IRS Connector Consumer to communicate with its Key Vault to local filesystem. In the `cd/terraform-identities` directory run: - -```sh -terraform init -az keyvault secret download --file ../../dev/local/cert.pfx --vault-name "$(terraform output -raw vault_name)" --name "$(terraform output -raw irs_connector_consumer_cert_name)" --encoding base64 -``` - -Set environment variables for GitHub access: - -```bash -export IRS_EDC_PKG_USERNAME=your_github_username -export IRS_EDC_PKG_PASSWORD=your_github_pat_token -``` -Make sure you have configured the env properties. It can be done by creating .env file and copying content of the .env.example file into it. -Run integration tests: - -```bash -./run-integration-test.sh -``` - -### Debugging connectors from IDE - -Download certificate for the IRS Connector Consumer as explained in the previous section. - -Create empty file for the Provider Connector filesystem vault (FsVault): - -```bash -touch ../dev/local/dataspaceconnector-vault.properties -``` - -Import run configurations in the `dev/ide` folder into your IDE and use these to debug the consumer and provider connectors. As default the provider connector will try to use a local IRS running at `localhost:8080`, make sure to previously start the IRS api at that port as well. - -Send a request to the local consumer connector by issuing: -```bash -curl -f -X POST http://localhost:9191/api/v0.1/retrievePartsTree -H "Content-type:application/json" -d '{"byObjectIdRequest": {"oneIDManufacturer": "BMW MUC", "objectIDManufacturer": "YS3DD78N4X7055320", "view": "AS_BUILT", "aspect": "MATERIAL", "depth": 2}}' -``` - -## Prometheus endpoint - -- Download latest jar from https://github.com/prometheus/jmx_exporter/releases - -- Config is available at location cd/jmx_prometheus_config.yml - -- Attach jmx prometheus jar as a java agent to running process. - -```bash --javaagent:./jmx_prometheus_javaagent-.jar=:cd/jmx_prometheus_config.yml -``` - -- Metric endpoint will be available on http://localhost:/metrics diff --git a/connector/edc-recursive-job/README.md b/connector/edc-recursive-job/README.md deleted file mode 100644 index 039e06c300..0000000000 --- a/connector/edc-recursive-job/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# edc-recursive-job - -The logic to orchestate multiple transfers (to build a IRS parts tree out of recursive calls to IRS API endpoints in multiple dataspace partitions) is factored out into a generic framework to manage jobs (in this module), that allows plugging in a custom handler to provide the logic, in our case to parse the output of one IRS API call to determine the next endpoints to call. - -We are in discussions with the EDC team to determine if this module could be integrated upstream. diff --git a/connector/edc-recursive-job/pom.xml b/connector/edc-recursive-job/pom.xml deleted file mode 100644 index 788eb90974..0000000000 --- a/connector/edc-recursive-job/pom.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - 4.0.0 - - - net.catenax.irs - irs-connector-parent - ${revision} - ../irs-connector-parent - - - net.catenax.irs - edc-recursive-job - - Recursive Job Module - Module for recursive job management - - - - org.projectlombok - lombok - 1.18.24 - true - - - com.google.code.findbugs - annotations - 3.0.1u2 - provided - - - org.jetbrains - annotations - 23.0.0 - provided - - - org.slf4j - slf4j-api - 1.7.36 - provided - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - 2.13.2 - provided - - - diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobException.java b/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobException.java deleted file mode 100644 index 0cddd12159..0000000000 --- a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobException.java +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright (c) 2021 Copyright Holder (Catena-X Consortium) -// -// See the AUTHORS file(s) distributed with this work for additional -// information regarding authorship. -// -// See the LICENSE file(s) distributed with this work for -// additional information regarding license terms. -// -package net.catenax.irs.connector.job; - -/** - * Exception related to Job problems - */ -public class JobException extends RuntimeException { - - public JobException(final String message) { - super(message); - } - - public JobException(final Throwable throwable) { - super(throwable); - } -} diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobState.java b/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobState.java deleted file mode 100644 index 39f94b355d..0000000000 --- a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobState.java +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) 2021 Copyright Holder (Catena-X Consortium) -// -// See the AUTHORS file(s) distributed with this work for additional -// information regarding authorship. -// -// See the LICENSE file(s) distributed with this work for -// additional information regarding license terms. -// -package net.catenax.irs.connector.job; - -/** - * Represents the state of a {@link MultiTransferJob}. - */ -public enum JobState { - UNSAVED, - INITIAL, - IN_PROGRESS, - TRANSFERS_FINISHED, - COMPLETED, - CANCELED, - ERROR -} diff --git a/connector/edc-recursive-job/src/test/java/net/catenax/irs/connector/job/InMemoryJobStoreTest.java b/connector/edc-recursive-job/src/test/java/net/catenax/irs/connector/job/InMemoryJobStoreTest.java deleted file mode 100644 index bd58dddca5..0000000000 --- a/connector/edc-recursive-job/src/test/java/net/catenax/irs/connector/job/InMemoryJobStoreTest.java +++ /dev/null @@ -1,319 +0,0 @@ -package net.catenax.irs.connector.job; - -import com.github.javafaker.Faker; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.time.LocalDateTime; -import java.util.List; - -class InMemoryJobStoreTest { - - InMemoryJobStore sut = new InMemoryJobStore(); - Faker faker = new Faker(); - TestMother generate = new TestMother(); - MultiTransferJob job = generate.job(JobState.UNSAVED); - MultiTransferJob job2 = generate.job(JobState.UNSAVED); - MultiTransferJob originalJob = job.toBuilder().build(); - String otherJobId = faker.lorem().characters(); - TransferProcess process1 = generate.transfer(); - TransferProcess process2 = generate.transfer(); - String processId1 = process1.getId(); - String processId2 = process2.getId(); - String errorDetail = faker.lorem().sentence(); - - @Test - void find_WhenNotFound() { - assertThat(sut.find(otherJobId)).isEmpty(); - } - - @Test - void findByProcessId_WhenFound() { - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.create(job2); - sut.addTransferProcess(job2.getJobId(), processId2); - - refreshJob(); - assertThat(sut.findByProcessId(processId1)).contains(job); - } - - @Test - void findByProcessId_WhenNotFound() { - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - - assertThat(sut.findByProcessId(processId2)).isEmpty(); - } - - @Test - void create_and_find() { - sut.create(job); - assertThat(sut.find(job.getJobId())).isPresent() - .get() - .usingRecursiveComparison() - .isEqualTo(originalJob.toBuilder().state(JobState.INITIAL).build()); - assertThat(sut.find(otherJobId)).isEmpty(); - } - - @Test - void addTransferProcess() { - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - refreshJob(); - assertThat(job.getTransferProcessIds()).containsExactly(processId1); - assertThat(job.getState()).isEqualTo(JobState.IN_PROGRESS); - } - - @Test - void completeTransferProcess_WhenJobNotFound() { - sut.completeTransferProcess(otherJobId, process1); - } - - @Test - void completeTransferProcess_WhenTransferFound() { - // Arrange - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - - // Act - sut.completeTransferProcess(job.getJobId(), process1); - - // Assert - assertThat(job.getTransferProcessIds()).isEmpty(); - } - - @Test - void completeTransferProcess_WhenTransferNotFound() { - // Act - sut.completeTransferProcess(job.getJobId(), process1); - - // Assert - assertThat(job.getTransferProcessIds()).isEmpty(); - } - - @Test - void completeTransferProcess_WhenTransferAlreadyCompleted() { - // Arrange - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.completeTransferProcess(job.getJobId(), process1); - - // Act - assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> sut.completeTransferProcess(job.getJobId(), process1)); - - // Assert - refreshJob(); - assertThat(job.getTransferProcessIds()).isEmpty(); - } - - @Test - void completeTransferProcess_WhenNotLastTransfer_DoesNotTransitionJob() { - // Arrange - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.addTransferProcess(job.getJobId(), processId2); - - // Act - sut.completeTransferProcess(job.getJobId(), process1); - - // Assert - refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.IN_PROGRESS); - } - - @Test - void completeTransferProcess_WhenLastTransfer_TransitionsJob() { - // Arrange - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.addTransferProcess(job.getJobId(), processId2); - - // Act - sut.completeTransferProcess(job.getJobId(), process1); - sut.completeTransferProcess(job.getJobId(), process2); - - // Assert - refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.TRANSFERS_FINISHED); - } - - @Test - void completeJob_WhenJobNotFound() { - // Arrange - sut.create(job); - // Act - sut.completeJob(otherJobId); - refreshJob(); - // Assert - assertThat(job.getState()).isEqualTo(JobState.INITIAL); - } - - @Test - void completeJob_WhenJobInInitialState() { - // Arrange - sut.create(job); - sut.create(job2); - // Act - sut.completeJob(job.getJobId()); - // Assert - refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.COMPLETED); - assertTrue(job.getCompletionDate().isPresent()); - assertThat(job2.getState()).isEqualTo(JobState.UNSAVED); - } - - @Test - void completeJob_WhenJobInTransfersCompletedState() { - // Arrange - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.completeTransferProcess(job.getJobId(), process1); - // Act - sut.completeJob(job.getJobId()); - // Assert - refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.COMPLETED); - assertTrue(job.getCompletionDate().isPresent()); - } - - @Test - void completeJob_WhenJobInTransfersInProgressState() { - // Arrange - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - // Act - assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> sut.completeJob(job.getJobId())); - // Assert - refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.IN_PROGRESS); - } - - @Test - void markJobInError_WhenJobNotFound() { - // Arrange - sut.create(job); - // Act - sut.markJobInError(otherJobId, errorDetail); - // Assert - refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.INITIAL); - } - - @Test - void markJobInError_WhenJobInInitialState() { - // Arrange - sut.create(job); - sut.create(job2); - // Act - sut.markJobInError(job.getJobId(), errorDetail); - // Assert - refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.ERROR); - assertThat(job2.getState()).isEqualTo(JobState.UNSAVED); - assertThat(job.getErrorDetail()).isEqualTo(errorDetail); - assertTrue(job.getCompletionDate().isPresent()); - } - - @Test - void markJobInError_WhenJobInTransfersCompletedState() { - // Arrange - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.completeTransferProcess(job.getJobId(), process1); - // Act - sut.markJobInError(job.getJobId(), errorDetail); - // Assert - refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.ERROR); - assertTrue(job.getCompletionDate().isPresent()); - } - - @Test - void markJobInError_WhenJobInTransfersInProgressState() { - // Arrange - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - // Act - sut.markJobInError(job.getJobId(), errorDetail); - // Assert - refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.ERROR); - assertTrue(job.getCompletionDate().isPresent()); - } - - @Test - void shouldFindCompletedJobsOlderThanFiveHours() { - // Arrange - final LocalDateTime nowPlusFiveHours = LocalDateTime.now().plusHours(5); - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.completeTransferProcess(job.getJobId(), process1); - sut.completeJob(job.getJobId()); - // Act - final List completedJobs = sut.findByStateAndCompletionDateOlderThan(JobState.COMPLETED, nowPlusFiveHours); - // Assert - assertThat(completedJobs.size()).isEqualTo(1); - assertThat(completedJobs.get(0).getState()).isEqualTo(JobState.COMPLETED); - assertTrue(completedJobs.get(0).getCompletionDate().isPresent()); - } - - @Test - void shouldFindFailedJobsOlderThanFiveHours() { - // Arrange - final LocalDateTime nowPlusFiveHours = LocalDateTime.now().plusHours(5); - sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.markJobInError(job.getJobId(), errorDetail); - // Act - final List failedJobs = sut.findByStateAndCompletionDateOlderThan(JobState.ERROR, nowPlusFiveHours); - // Assert - assertThat(failedJobs.size()).isEqualTo(1); - assertThat(failedJobs.get(0).getState()).isEqualTo(JobState.ERROR); - assertTrue(failedJobs.get(0).getCompletionDate().isPresent()); - } - - @Test - void shouldDeleteJobById() { - // Arrange - sut.create(job); - // Act - sut.deleteJob(job.getJobId()); - // Assert - assertThat(sut.find(job.getJobId())).isEmpty(); - } - - @Test - void shouldFindJobsByCompletedJobState() { - // Arrange - sut.create(job); - sut.completeJob(job.getJobId()); - sut.create(job2); - // Act - final List foundJobs = sut.findByStates(List.of(JobState.COMPLETED)); - // Assert - assertThat(foundJobs.size()).isEqualTo(1); - assertThat(foundJobs.get(0).getJobId()).isEqualTo(job.getJobId()); - } - - @Test - void shouldFindJobsByErrorJobState() { - // Arrange - sut.create(job); - sut.markJobInError(job.getJobId(), "errorDetail"); - // Act - final List foundJobs = sut.findByStates(List.of(JobState.ERROR)); - // Assert - assertThat(foundJobs.size()).isEqualTo(1); - assertThat(foundJobs.get(0).getJobId()).isEqualTo(job.getJobId()); - } - - private void refreshJob() { - job = sut.find(job.getJobId()).get(); - } -} \ No newline at end of file diff --git a/connector/edc-recursive-job/src/test/java/net/catenax/irs/connector/job/TestMother.java b/connector/edc-recursive-job/src/test/java/net/catenax/irs/connector/job/TestMother.java deleted file mode 100644 index e064c72b75..0000000000 --- a/connector/edc-recursive-job/src/test/java/net/catenax/irs/connector/job/TestMother.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.catenax.irs.connector.job; - -import com.github.javafaker.Faker; - -import java.util.Map; -import java.util.UUID; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -/** - * Base object mother class to create objects for testing. - * - * @see - * https://martinfowler.com/bliki/ObjectMother.html - */ -class TestMother { - - Faker faker = new Faker(); - - MultiTransferJob job() { - return job(faker.options().option(JobState.class)); - } - - MultiTransferJob job(JobState jobState) { - return MultiTransferJob.builder() - .jobId(faker.lorem().characters()) - .jobData(Map.of( - faker.lorem().characters(), - faker.lorem().characters(), - faker.lorem().characters(), - faker.lorem().characters() - )) - .state(jobState) - .build(); - } - - DataRequest dataRequest() { - return new DataRequest() { - }; - } - - TransferInitiateResponse okResponse() { - return response(ResponseStatus.OK); - } - - TransferInitiateResponse response(ResponseStatus status) { - return TransferInitiateResponse.builder() - .transferId(UUID.randomUUID().toString()) - .status(status) - .build(); - } - - TransferProcess transfer() { - final String characters = faker.lorem().characters(); - return () -> characters; - } - - public Stream dataRequests(int count) { - return IntStream.range(0, count).mapToObj(i -> dataRequest()); - } -} \ No newline at end of file diff --git a/connector/irs-connector-parent/pom.xml b/connector/irs-connector-parent/pom.xml deleted file mode 100644 index 7643aa8897..0000000000 --- a/connector/irs-connector-parent/pom.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - 4.0.0 - - - net.catenax.irs - irs-parent - ${revision} - ../../irs-parent - - - net.catenax.irs - irs-connector-parent - pom - - IRS Connector parent - Parent Module for EDC Connectors - - - ../../ci/pmd-rules.xml - 11 - 3.0.0 - 0.0.1-SNAPSHOT.Agera.1553242392 - 2.12.4 - 3.0.0 - 7.0.1.Final - 1.18.22 - 1.8.0 - - - - - central - Maven Central - default - https://repo1.maven.org/maven2 - - false - - - - - - - org.junit.jupiter - junit-jupiter-engine - ${junit.version} - test - - - org.junit.jupiter - junit-jupiter-params - ${junit.version} - test - - - net.javacrumbs.json-unit - json-unit-assertj - ${json-unit-assertj.version} - test - - - org.mockito - mockito-junit-jupiter - ${mockito-junit-jupiter.version} - test - - - com.github.javafaker - javafaker - ${javafaker.version} - test - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.10.1 - - ${javaVersion} - ${javaVersion} - true - lines,vars,source - - - - - diff --git a/connector/pom.xml b/connector/pom.xml deleted file mode 100644 index 22eb69fca8..0000000000 --- a/connector/pom.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - 4.0.0 - - net.catenax.irs - item-relationship-service-connector-root - ${revision} - pom - - IRS Connector Root - Root module containing connector modules. - - - irs-connector-parent - edc-recursive-job - - diff --git a/connector/run-integration-test.sh b/connector/run-integration-test.sh deleted file mode 100644 index a203f8f7c9..0000000000 --- a/connector/run-integration-test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -euo pipefail -cd .. - -if [ ! -f dev/local/cert.pfx ]; then - echo "Missing file cert.pfx" - exit 1 -fi - -export DOCKER_BUILDKIT=1 -docker-compose --profile connector build --build-arg IRS_EDC_PKG_USERNAME=$IRS_EDC_PKG_USERNAME --build-arg IRS_EDC_PKG_PASSWORD=$IRS_EDC_PKG_PASSWORD -docker-compose --profile connector --profile irs up --exit-code-from=connector-integration-test --abort-on-container-exit diff --git a/irs-api/pom.xml b/irs-api/pom.xml index 85bd9a99c4..fae4c554f4 100644 --- a/irs-api/pom.xml +++ b/irs-api/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 @@ -83,12 +84,6 @@ ${json-unit-assertj.version} test - - net.catenax.irs - edc-recursive-job - ${project.version} - compile - org.awaitility awaitility diff --git a/irs-api/src/main/java/net/catenax/irs/aaswrapper/job/AASRecursiveJobHandler.java b/irs-api/src/main/java/net/catenax/irs/aaswrapper/job/AASRecursiveJobHandler.java index 268f8b1384..af607a5ba9 100644 --- a/irs-api/src/main/java/net/catenax/irs/aaswrapper/job/AASRecursiveJobHandler.java +++ b/irs-api/src/main/java/net/catenax/irs/aaswrapper/job/AASRecursiveJobHandler.java @@ -9,6 +9,9 @@ // package net.catenax.irs.aaswrapper.job; +import static net.catenax.irs.dtos.IrsCommonConstants.DEPTH_ID_KEY; +import static net.catenax.irs.dtos.IrsCommonConstants.ROOT_ITEM_ID_KEY; + import java.util.Map; import java.util.stream.Stream; @@ -21,15 +24,6 @@ */ @Slf4j public class AASRecursiveJobHandler implements RecursiveJobHandler { - /** - * Job Data key for root item ID - */ - public static final String ROOT_ITEM_ID_KEY = "root.item.id.key"; - - /** - * Expected depth of the tree - */ - public static final String DEPTH_ID_KEY = "depth.id.key"; private final TreeRecursiveLogic logic; @@ -39,7 +33,7 @@ public AASRecursiveJobHandler(final TreeRecursiveLogic logic) { @Override public Stream initiate(final MultiTransferJob job) { - log.info("Initiating request for job {}", job.getJobId()); + log.info("Initiating request for job {}", job.getJob().getJobId().toString()); final var partId = job.getJobData().get(ROOT_ITEM_ID_KEY); final var dataRequest = ItemDataRequest.rootNode(partId); return Stream.of(dataRequest); @@ -47,7 +41,7 @@ public Stream initiate(final MultiTransferJob job) { @Override public Stream recurse(final MultiTransferJob job, final AASTransferProcess transferProcess) { - log.info("Starting recursive request for job {}", job.getJobId()); + log.info("Starting recursive request for job {}", job.getJob().getJobId().toString()); final Integer expectedDepth = getExpectedTreeDepth(job.getJobData()); final Integer currentDepth = transferProcess.getDepth(); @@ -65,14 +59,14 @@ public Stream recurse(final MultiTransferJob job, final AASTran @Override public void complete(final MultiTransferJob job) { - log.info("Completed retrieval for Job {}", job.getJobId()); + log.info("Completed retrieval for Job {}", job.getJob().getJobId().toString()); final var completedTransfers = job.getCompletedTransfers(); - final var targetBlobName = job.getJobId(); - logic.assemblePartialItemGraphBlobs(completedTransfers, targetBlobName); + final var targetBlobName = job.getJob().getJobId(); + logic.assemblePartialItemGraphBlobs(completedTransfers, targetBlobName.toString()); } private Integer getExpectedTreeDepth(final Map jobData) { - return Integer.parseInt(jobData.get(AASRecursiveJobHandler.DEPTH_ID_KEY)); + return Integer.parseInt(jobData.get(DEPTH_ID_KEY)); } private boolean expectedDepthOfTreeIsNotReached(final Integer expectedDepth, final Integer currentDepth) { diff --git a/irs-api/src/main/java/net/catenax/irs/aaswrapper/submodel/domain/SubmodelFacade.java b/irs-api/src/main/java/net/catenax/irs/aaswrapper/submodel/domain/SubmodelFacade.java index 6ca65ffb9c..35b3d59b9e 100644 --- a/irs-api/src/main/java/net/catenax/irs/aaswrapper/submodel/domain/SubmodelFacade.java +++ b/irs-api/src/main/java/net/catenax/irs/aaswrapper/submodel/domain/SubmodelFacade.java @@ -44,8 +44,8 @@ public AssemblyPartRelationshipDTO getSubmodel(final String submodelEndpointAddr .build())); return AssemblyPartRelationshipDTO.builder() - .withCatenaXId(submodel.getCatenaXId()) - .withChildParts(childParts) + .catenaXId(submodel.getCatenaXId()) + .childParts(childParts) .build(); } diff --git a/irs-api/src/main/java/net/catenax/irs/configuration/IrsConfiguration.java b/irs-api/src/main/java/net/catenax/irs/configuration/IrsConfiguration.java index 569f850159..cfd404a867 100644 --- a/irs-api/src/main/java/net/catenax/irs/configuration/IrsConfiguration.java +++ b/irs-api/src/main/java/net/catenax/irs/configuration/IrsConfiguration.java @@ -9,12 +9,12 @@ // package net.catenax.irs.configuration; +import java.net.URL; + import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; -import java.net.URL; - /** * IRS configuration settings. Automatically populated by Spring from application.yml * and other configuration sources. diff --git a/irs-api/src/main/java/net/catenax/irs/configuration/OpenApiExamples.java b/irs-api/src/main/java/net/catenax/irs/configuration/OpenApiExamples.java index e06be51812..457b8c741f 100644 --- a/irs-api/src/main/java/net/catenax/irs/configuration/OpenApiExamples.java +++ b/irs-api/src/main/java/net/catenax/irs/configuration/OpenApiExamples.java @@ -24,7 +24,7 @@ import net.catenax.irs.component.Endpoint; import net.catenax.irs.component.GlobalAssetIdentification; import net.catenax.irs.component.Job; -import net.catenax.irs.component.JobException; +import net.catenax.irs.component.JobErrorDetails; import net.catenax.irs.component.JobHandle; import net.catenax.irs.component.Jobs; import net.catenax.irs.component.MeasurementUnit; @@ -61,10 +61,10 @@ public void createExamples(final Components components) { components.addExamples("job-handle", toExample(createJobHandle(JOB_HANDLE_ID_1))); components.addExamples("error-response", toExample(ErrorResponse.builder() .withErrors(List.of("TimeoutException", - "ParsingException")) + "ParsingException")) .withMessage("Some errors occured") .withStatusCode( - HttpStatus.INTERNAL_SERVER_ERROR) + HttpStatus.INTERNAL_SERVER_ERROR) .build())); components.addExamples("complete-job-result", createCompleteJobResult()); components.addExamples("job-result-without-uncompleted-result-tree", createJobResultWithoutTree()); @@ -134,8 +134,8 @@ private Summary createSummary() { .build(); } - private JobException createJobException() { - return new JobException("IrsTimeoutException", "Timeout while requesting Digital Registry", EXAMPLE_INSTANT); + private JobErrorDetails createJobException() { + return new JobErrorDetails("IrsTimeoutException", "Timeout while requesting Digital Registry", EXAMPLE_INSTANT); } private Example createJobResultWithoutTree() { @@ -199,7 +199,7 @@ private Quantity createQuantity() { .quantityNumber(1) .measurementUnit(MeasurementUnit.builder() .datatypeURI( - "urn:bamm:io.openmanufacturing:meta-model:1.0.0#piece") + "urn:bamm:io.openmanufacturing:meta-model:1.0.0#piece") .lexicalValue("piece") .build()) diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/BaseJobStore.java b/irs-api/src/main/java/net/catenax/irs/connector/job/BaseJobStore.java similarity index 87% rename from connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/BaseJobStore.java rename to irs-api/src/main/java/net/catenax/irs/connector/job/BaseJobStore.java index 41aa1c8563..2664039ee6 100644 --- a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/BaseJobStore.java +++ b/irs-api/src/main/java/net/catenax/irs/connector/job/BaseJobStore.java @@ -9,7 +9,7 @@ // package net.catenax.irs.connector.job; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.Collection; import java.util.List; import java.util.Optional; @@ -21,6 +21,7 @@ import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; +import net.catenax.irs.component.enums.JobState; import org.jetbrains.annotations.Nullable; /** @@ -55,19 +56,22 @@ public Optional find(final String jobId) { @Override public List findByStateAndCompletionDateOlderThan(final JobState jobState, - final LocalDateTime localDateTime) { + final Instant dateTime) { return readLock(() -> getAll().stream() .filter(hasState(jobState)) - .filter(isCompletionDateBefore(localDateTime)) + .filter(isCompletionDateBefore(dateTime)) .collect(Collectors.toList())); } private Predicate hasState(final JobState jobState) { - return job -> job.getState().equals(jobState); + return job -> job.getJob().getJobState().equals(jobState); } - private Predicate isCompletionDateBefore(final LocalDateTime localDateTime) { - return job -> job.getCompletionDate().isPresent() && job.getCompletionDate().get().isBefore(localDateTime); + private Predicate isCompletionDateBefore(final Instant localDateTime) { + return job -> { + final Instant completed = job.getJob().getJobCompleted(); + return completed != null && completed.isBefore(localDateTime); + }; } @Override @@ -79,7 +83,8 @@ public Optional findByProcessId(final String processId) { public void create(final MultiTransferJob job) { writeLock(() -> { final var newJob = job.toBuilder().transitionInitial().build(); - put(job.getJobId(), newJob); + log.info("Add the job into jobstore here {}", newJob); + put(job.getJob().getJobId().toString(), newJob); return null; }); } @@ -123,7 +128,7 @@ public List findByStates(final List jobStates) { } private Predicate hasState(final List jobStates) { - return job -> jobStates.contains(job.getState()); + return job -> jobStates.contains(job.getJob().getJobState()); } @Override @@ -145,7 +150,7 @@ private void modifyJob(final String jobId, final UnaryOperator log.warn("Job not found: {}", jobId); } else { final MultiTransferJob multiTransferJob = job.get(); - put(multiTransferJob.getJobId(), action.apply(multiTransferJob)); + put(multiTransferJob.getJob().getJobId().toString(), action.apply(multiTransferJob)); } return null; }); @@ -163,7 +168,7 @@ private T readLock(final Supplier work) { } } catch (InterruptedException e) { Thread.currentThread().interrupt(); - throw new JobException(e); + throw new JobException("Job Interrupted", e); } } @@ -179,7 +184,7 @@ private T writeLock(final Supplier work) { } } catch (InterruptedException e) { Thread.currentThread().interrupt(); - throw new JobException(e); + throw new JobException("Job Interrupted", e); } } } diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/DataRequest.java b/irs-api/src/main/java/net/catenax/irs/connector/job/DataRequest.java similarity index 100% rename from connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/DataRequest.java rename to irs-api/src/main/java/net/catenax/irs/connector/job/DataRequest.java diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/InMemoryJobStore.java b/irs-api/src/main/java/net/catenax/irs/connector/job/InMemoryJobStore.java similarity index 94% rename from connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/InMemoryJobStore.java rename to irs-api/src/main/java/net/catenax/irs/connector/job/InMemoryJobStore.java index 961e272dc8..4029d023e2 100644 --- a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/InMemoryJobStore.java +++ b/irs-api/src/main/java/net/catenax/irs/connector/job/InMemoryJobStore.java @@ -22,6 +22,10 @@ */ @Slf4j @RequiredArgsConstructor +@SuppressWarnings({ "PMD.TooManyMethods", + "PMD.PreserveStackTrace" +}) + public class InMemoryJobStore extends BaseJobStore { /** diff --git a/irs-api/src/main/java/net/catenax/irs/connector/job/JobException.java b/irs-api/src/main/java/net/catenax/irs/connector/job/JobException.java new file mode 100644 index 0000000000..1d4c53e26a --- /dev/null +++ b/irs-api/src/main/java/net/catenax/irs/connector/job/JobException.java @@ -0,0 +1,52 @@ +// +// Copyright (c) 2021 Copyright Holder (Catena-X Consortium) +// +// See the AUTHORS file(s) distributed with this work for additional +// information regarding authorship. +// +// See the LICENSE file(s) distributed with this work for +// additional information regarding license terms. +// +package net.catenax.irs.connector.job; + +import java.time.Instant; + +import lombok.Getter; +import net.catenax.irs.component.JobErrorDetails; + +/** + * Job Exception with embedded JobErrorDetails + */ +public class JobException extends RuntimeException { + private static final String DEFAULT_ERROR_MESSAGE = "Critical error occur!!"; + + @Getter + private final JobErrorDetails jobErrorDetails; + + public JobException() { + super(DEFAULT_ERROR_MESSAGE); + jobErrorDetails = JobErrorDetails.builder() + .exception(ResponseStatus.FATAL_ERROR.toString()) + .errorDetail(DEFAULT_ERROR_MESSAGE) + .exceptionDate(Instant.now()) + .build(); + } + + public JobException(final String message) { + super(message); + jobErrorDetails = JobErrorDetails.builder() + .exception(message) + .errorDetail(DEFAULT_ERROR_MESSAGE) + .exceptionDate(Instant.now()) + .build(); + } + + public JobException(final String message, final Throwable cause) { + super(message, cause); + jobErrorDetails = JobErrorDetails.builder() + .exception(cause.getMessage()) + .errorDetail(message) + .exceptionDate(Instant.now()) + .build(); + } +} diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobInitiateResponse.java b/irs-api/src/main/java/net/catenax/irs/connector/job/JobInitiateResponse.java similarity index 100% rename from connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobInitiateResponse.java rename to irs-api/src/main/java/net/catenax/irs/connector/job/JobInitiateResponse.java diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobOrchestrator.java b/irs-api/src/main/java/net/catenax/irs/connector/job/JobOrchestrator.java similarity index 56% rename from connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobOrchestrator.java rename to irs-api/src/main/java/net/catenax/irs/connector/job/JobOrchestrator.java index efac2d8739..bcb93d535d 100644 --- a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobOrchestrator.java +++ b/irs-api/src/main/java/net/catenax/irs/connector/job/JobOrchestrator.java @@ -9,28 +9,34 @@ // package net.catenax.irs.connector.job; -import static java.util.UUID.randomUUID; +import static net.catenax.irs.dtos.IrsCommonConstants.ROOT_ITEM_ID_KEY; -import java.time.LocalDateTime; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; -import lombok.Builder; -import lombok.Value; import lombok.extern.slf4j.Slf4j; +import net.catenax.irs.component.GlobalAssetIdentification; +import net.catenax.irs.component.Job; +import net.catenax.irs.component.enums.JobState; +import org.apache.commons.lang3.StringUtils; /** * Orchestrator service for recursive {@link MultiTransferJob}s that potentially * comprise multiple transfers. */ -@SuppressWarnings("PMD.AvoidCatchingGenericException") // Handle RuntimeException from callbacks +@SuppressWarnings({ "PMD.AvoidCatchingGenericException", "PMD.TooManyMethods" }) +// Handle RuntimeException from callbacks @Slf4j public class JobOrchestrator { private static final int TTL_CLEANUP_COMPLETED_JOBS_HOURS = 1; + private static final int TTL_CLEANUP_FAILED_JOBS_HOURS = 24; /** @@ -57,7 +63,8 @@ public class JobOrchestrator { * @param handler Recursive job handler. */ public JobOrchestrator(final TransferProcessManager processManager, final JobStore jobStore, - final RecursiveJobHandler handler) { + final RecursiveJobHandler handler) { + this.processManager = processManager; this.jobStore = jobStore; this.handler = handler; @@ -70,35 +77,40 @@ public JobOrchestrator(final TransferProcessManager processManager, final * @return response. */ public JobInitiateResponse startJob(final Map jobData) { - final var job = MultiTransferJob.builder() - .jobId(randomUUID().toString()) - .jobData(jobData) - .state(JobState.UNSAVED) - .build(); - - jobStore.create(job); + final Job job = createJob(jobData.get(ROOT_ITEM_ID_KEY)); + final var multiJob = MultiTransferJob.builder().job(job).jobData(jobData).build(); + jobStore.create(multiJob); final Stream requests; try { - requests = handler.initiate(job); + requests = handler.initiate(multiJob); } catch (RuntimeException e) { - markJobInError(job, e, "Handler method failed"); - return JobInitiateResponse.builder().jobId(job.getJobId()).status(ResponseStatus.FATAL_ERROR).build(); + markJobInError(multiJob, e, "Handler method failed"); + return JobInitiateResponse.builder() + .jobId(multiJob.getJob().getJobId().toString()) + .status(ResponseStatus.FATAL_ERROR) + .build(); } long transferCount; try { - transferCount = startTransfers(job, requests); + transferCount = startTransfers(multiJob, requests); } catch (JobException e) { - return JobInitiateResponse.builder().jobId(job.getJobId()).status(e.getStatus()).build(); + return JobInitiateResponse.builder() + .jobId(multiJob.getJob().getJobId().toString()) + .status(convertMessage(e.getJobErrorDetails().getException())) + .build(); } // If no transfers are requested, job is already complete if (transferCount == 0) { - completeJob(job); + completeJob(multiJob); } - return JobInitiateResponse.builder().jobId(job.getJobId()).status(ResponseStatus.OK).build(); + return JobInitiateResponse.builder() + .jobId(multiJob.getJob().getJobId().toString()) + .status(ResponseStatus.OK) + .build(); } /** @@ -114,8 +126,9 @@ public JobInitiateResponse startJob(final Map jobData) { } final var job = jobEntry.get(); - if (job.getState() != JobState.IN_PROGRESS) { - log.info("Ignoring transfer complete event for job {} in state {}", job.getJobId(), job.getState()); + if (job.getJob().getJobState() != JobState.RUNNING) { + log.info("Ignoring transfer complete event for job {} in state {} ", job.getJob().getJobId(), + job.getJob().getJobState()); return; } @@ -134,37 +147,36 @@ public JobInitiateResponse startJob(final Map jobData) { return; } - jobStore.completeTransferProcess(job.getJobId(), process); + jobStore.completeTransferProcess(job.getJob().getJobId().toString(), process); - callCompleteHandlerIfFinished(job.getJobId()); + callCompleteHandlerIfFinished(job.getJob().getJobId().toString()); } public List findAndCleanupCompletedJobs() { - final LocalDateTime currentDateMinusHours = LocalDateTime.now().minusHours(TTL_CLEANUP_COMPLETED_JOBS_HOURS); + final Instant currentDateMinusSeconds = Instant.now().minus(TTL_CLEANUP_COMPLETED_JOBS_HOURS, ChronoUnit.HOURS); final List completedJobs = jobStore.findByStateAndCompletionDateOlderThan(JobState.COMPLETED, - currentDateMinusHours); + currentDateMinusSeconds); return deleteJobs(completedJobs); } public List findAndCleanupFailedJobs() { - final LocalDateTime currentDateMinusHours = LocalDateTime.now().minusHours(TTL_CLEANUP_FAILED_JOBS_HOURS); + final Instant currentDateMinusSeconds = Instant.now().minus(TTL_CLEANUP_FAILED_JOBS_HOURS, ChronoUnit.HOURS); final List failedJobs = jobStore.findByStateAndCompletionDateOlderThan(JobState.ERROR, - currentDateMinusHours); - + currentDateMinusSeconds); return deleteJobs(failedJobs); } private List deleteJobs(final List jobs) { return jobs.stream() - .map(job -> jobStore.deleteJob(job.getJobId())) + .map(job -> jobStore.deleteJob(job.getJob().getJobId().toString())) .flatMap(Optional::stream) .collect(Collectors.toList()); } private void callCompleteHandlerIfFinished(final String jobId) { jobStore.find(jobId).ifPresent(job -> { - if (job.getState() != JobState.TRANSFERS_FINISHED) { + if (job.getJob().getJobState() != JobState.TRANSFERS_FINISHED) { return; } completeJob(job); @@ -178,39 +190,50 @@ private void completeJob(final MultiTransferJob job) { markJobInError(job, e, "Handler method failed"); return; } - jobStore.completeJob(job.getJobId()); + jobStore.completeJob(job.getJob().getJobId().toString()); } private void markJobInError(final MultiTransferJob job, final Throwable exception, final String message) { + log.error(message, exception); - jobStore.markJobInError(job.getJobId(), message); + jobStore.markJobInError(job.getJob().getJobId().toString(), message); } - private long startTransfers(final MultiTransferJob job, final Stream dataRequests) /* throws JobException */ { + private long startTransfers(final MultiTransferJob job, final Stream dataRequests) /* throws JobErrorDetails */ { return dataRequests.map(r -> startTransfer(job, r)).collect(Collectors.counting()); } private TransferInitiateResponse startTransfer(final MultiTransferJob job, - final T dataRequest) /* throws JobException */ { + final T dataRequest) /* throws JobErrorDetails */ { final var response = processManager.initiateRequest(dataRequest, - transferId -> jobStore.addTransferProcess(job.getJobId(), transferId), this::transferProcessCompleted); + transferId -> jobStore.addTransferProcess(job.getJob().getJobId().toString(), transferId), + this::transferProcessCompleted); if (response.getStatus() != ResponseStatus.OK) { - throw JobException.builder().status(response.getStatus()).build(); + throw new JobException(response.getStatus().toString()); } + jobStore.addTransferProcess(job.getJob().getJobId().toString(), response.getTransferId()); return response; } - /** - * Exception used to stop creating additional transfers if one transfer creation fails. - */ - @Value - @Builder - private static class JobException extends RuntimeException { - /** - * The status of the transfer in error. - */ - private final ResponseStatus status; + private Job createJob(final String globalAssetId) { + + if (StringUtils.isEmpty(globalAssetId)) { + throw new JobException("GlobalAsset Identifier cannot be null or empty string"); + } + + return Job.builder() + .jobId(UUID.randomUUID()) + .globalAssetId(GlobalAssetIdentification.builder().globalAssetId(globalAssetId).build()) + .createdOn(Instant.now()) + .lastModifiedOn(Instant.now()) + .jobState(JobState.UNSAVED) + .build(); } + + private ResponseStatus convertMessage(final String message) { + return ResponseStatus.valueOf(message); + } + } diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobStore.java b/irs-api/src/main/java/net/catenax/irs/connector/job/JobStore.java similarity index 87% rename from connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobStore.java rename to irs-api/src/main/java/net/catenax/irs/connector/job/JobStore.java index 4331960b77..811b57cdc6 100644 --- a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/JobStore.java +++ b/irs-api/src/main/java/net/catenax/irs/connector/job/JobStore.java @@ -9,12 +9,13 @@ // package net.catenax.irs.connector.job; -import org.jetbrains.annotations.Nullable; - -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; import java.util.Optional; +import net.catenax.irs.component.enums.JobState; +import org.jetbrains.annotations.Nullable; + /** * Manages storage of {@link MultiTransferJob} state. */ @@ -25,7 +26,7 @@ public interface JobStore { * * @param jobId the identifier of the job to retrieve. * @return the job if found, otherwise empty. - * @see MultiTransferJob#getJobId() + * @see MultiTransferJob#getJob() */ Optional find(String jobId); @@ -33,13 +34,14 @@ public interface JobStore { * Retrieve jobs by state with completion date older than requested date * * @param jobState the job state - * @param localDateTime requested date + * @param dateTime requested date * @return found jobs */ - List findByStateAndCompletionDateOlderThan(JobState jobState, LocalDateTime localDateTime); + List findByStateAndCompletionDateOlderThan(JobState jobState, Instant dateTime); /** * Retrieve jobs with requested states + * * @param jobStates requested job states * @return found jobs */ @@ -73,8 +75,8 @@ public interface JobStore { /** * Mark transfer process completed for the job. * - * @param jobId the job identifier. - * @param process transfer process to mark completed. + * @param jobId the job identifier. + * @param process transfer process to mark completed. */ void completeTransferProcess(String jobId, TransferProcess process); @@ -104,10 +106,11 @@ public interface JobStore { Optional deleteJob(String jobId); /** - * Set the job status to canceled. + * Cancel the job with identifier * - * @param jobId the job identifier. - * @return canceled MultiTransferJob + * @param jobId + * @return cancel job (if it existed) */ Optional cancelJob(String jobId); + } diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/MultiTransferJob.java b/irs-api/src/main/java/net/catenax/irs/connector/job/MultiTransferJob.java similarity index 70% rename from connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/MultiTransferJob.java rename to irs-api/src/main/java/net/catenax/irs/connector/job/MultiTransferJob.java index 30e8dd2c22..c56561d477 100644 --- a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/MultiTransferJob.java +++ b/irs-api/src/main/java/net/catenax/irs/connector/job/MultiTransferJob.java @@ -11,21 +11,24 @@ import static java.lang.String.format; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import lombok.Builder; import lombok.Getter; +import lombok.NonNull; import lombok.Singular; import lombok.ToString; +import net.catenax.irs.component.Job; +import net.catenax.irs.component.JobErrorDetails; +import net.catenax.irs.component.enums.JobState; import org.jetbrains.annotations.Nullable; /** @@ -37,42 +40,31 @@ public class MultiTransferJob { /** - * Job identifier. + * The attached job. */ + @NonNull @Getter - private final String jobId; + private Job job; + /** * Collection of transfer IDs that have not yet completed for the job. */ @Singular private final Set transferProcessIds; - /** - * Job state. - */ - @Getter - private JobState state; + /** * Arbitrary data attached to the job. */ @Getter @Singular("jobDatum") private Map jobData; - /** - * Error detail, potentially set if {@link #getState() state} is {@link JobState#ERROR}. - */ - @Getter - private String errorDetail; + /** * Collection of transfers that have completed for the job. */ @Getter @Singular private List completedTransfers; - /** - * Sets completion date for jobs with {@link JobState#COMPLETED} and {@link JobState#ERROR} state. - */ - @Getter - private Optional completionDate; public Collection getTransferProcessIds() { return Collections.unmodifiableSet(this.transferProcessIds); @@ -91,34 +83,39 @@ public static class MultiTransferJobBuilder { } /** - * Transition the job to the {@link JobState#IN_PROGRESS} state. + * Transition the job to the {@link JobState#RUNNING} state. */ /* package */ MultiTransferJobBuilder transitionInProgress() { - return transition(JobState.IN_PROGRESS, JobState.INITIAL, JobState.IN_PROGRESS); + return transition(JobState.RUNNING, JobState.INITIAL, JobState.RUNNING); } /** * Transition the job to the {@link JobState#TRANSFERS_FINISHED} state. */ /* package */ MultiTransferJobBuilder transitionTransfersFinished() { - return transition(JobState.TRANSFERS_FINISHED, JobState.IN_PROGRESS); + return transition(JobState.TRANSFERS_FINISHED, JobState.RUNNING); } /** * Transition the job to the {@link JobState#COMPLETED} state. */ /* package */ MultiTransferJobBuilder transitionComplete() { - return transition(JobState.COMPLETED, JobState.TRANSFERS_FINISHED, JobState.INITIAL) - .completionDate(Optional.of(LocalDateTime.now())); + return transition(JobState.COMPLETED, JobState.TRANSFERS_FINISHED, JobState.INITIAL).job( + job.toBuilder().jobCompleted(Instant.now()).build()); } /** * Transition the job to the {@link JobState#ERROR} state. */ /* package */ MultiTransferJobBuilder transitionError(final @Nullable String errorDetail) { - this.state = JobState.ERROR; - this.completionDate = Optional.of(LocalDateTime.now()); - this.errorDetail = errorDetail; + this.job = this.job.toBuilder() + .jobState(JobState.ERROR) + .jobCompleted(Instant.now()) + .exception(JobErrorDetails.builder() + .errorDetail(errorDetail) + .exceptionDate(Instant.now()) + .build()) + .build(); return this; } @@ -126,14 +123,16 @@ public static class MultiTransferJobBuilder { * Transition the job to the {@link JobState#CANCELED} state. */ /* package */ MultiTransferJobBuilder transitionCancel() { - return transition(JobState.CANCELED, JobState.UNSAVED, JobState.INITIAL, JobState.IN_PROGRESS); + return transition(JobState.CANCELED, JobState.UNSAVED, JobState.INITIAL, JobState.RUNNING); } private MultiTransferJobBuilder transition(final JobState end, final JobState... starts) { - if (Arrays.stream(starts).noneMatch(s -> s == state)) { - throw new IllegalStateException(format("Cannot transition from state %s to %s", state, end)); + if (Arrays.stream(starts).noneMatch(s -> s == job.getJobState())) { + throw new IllegalStateException( + format("Cannot transition from state %s to %s", job.getJobState(), end)); } - this.state = end; + + job = job.toBuilder().jobState(end).build(); return this; } } diff --git a/irs-api/src/main/java/net/catenax/irs/connector/job/PersistentJobStore.java b/irs-api/src/main/java/net/catenax/irs/connector/job/PersistentJobStore.java index 5cb65548e2..c09930f056 100644 --- a/irs-api/src/main/java/net/catenax/irs/connector/job/PersistentJobStore.java +++ b/irs-api/src/main/java/net/catenax/irs/connector/job/PersistentJobStore.java @@ -9,12 +9,6 @@ // package net.catenax.irs.connector.job; -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.Collections; -import java.util.Optional; -import java.util.stream.Collectors; - import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.catenax.irs.persistence.BlobPersistence; @@ -22,6 +16,12 @@ import net.catenax.irs.util.JsonUtil; import org.springframework.stereotype.Service; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.stream.Collectors; + /** * Stores Job data using persistent blob storage. */ @@ -64,7 +64,7 @@ protected Collection getAll() { protected void put(final String jobId, final MultiTransferJob job) { final byte[] blob = toBlob(job); try { - blobStore.putBlob(toBlobId(job.getJobId()), blob); + blobStore.putBlob(toBlobId(jobId), blob); } catch (BlobPersistenceException e) { log.error("Cannot create job in BlobStore", e); } @@ -77,7 +77,7 @@ protected Optional remove(final String jobId) { blobStore.delete(toBlobId(jobId)); return blob.map(this::toJob); } catch (BlobPersistenceException e) { - throw new JobException(e); + throw new JobException("Blob persistence error", e); } } @@ -93,4 +93,5 @@ private byte[] toBlob(final MultiTransferJob job) { private String toBlobId(final String jobId) { return JOB_PREFIX + jobId; } + } diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/RecursiveJobHandler.java b/irs-api/src/main/java/net/catenax/irs/connector/job/RecursiveJobHandler.java similarity index 100% rename from connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/RecursiveJobHandler.java rename to irs-api/src/main/java/net/catenax/irs/connector/job/RecursiveJobHandler.java diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/ResponseStatus.java b/irs-api/src/main/java/net/catenax/irs/connector/job/ResponseStatus.java similarity index 100% rename from connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/ResponseStatus.java rename to irs-api/src/main/java/net/catenax/irs/connector/job/ResponseStatus.java diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/TransferInitiateResponse.java b/irs-api/src/main/java/net/catenax/irs/connector/job/TransferInitiateResponse.java similarity index 100% rename from connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/TransferInitiateResponse.java rename to irs-api/src/main/java/net/catenax/irs/connector/job/TransferInitiateResponse.java diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/TransferProcess.java b/irs-api/src/main/java/net/catenax/irs/connector/job/TransferProcess.java similarity index 100% rename from connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/TransferProcess.java rename to irs-api/src/main/java/net/catenax/irs/connector/job/TransferProcess.java diff --git a/connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/TransferProcessManager.java b/irs-api/src/main/java/net/catenax/irs/connector/job/TransferProcessManager.java similarity index 100% rename from connector/edc-recursive-job/src/main/java/net/catenax/irs/connector/job/TransferProcessManager.java rename to irs-api/src/main/java/net/catenax/irs/connector/job/TransferProcessManager.java diff --git a/irs-api/src/main/java/net/catenax/irs/dto/AssemblyPartRelationshipDTO.java b/irs-api/src/main/java/net/catenax/irs/dto/AssemblyPartRelationshipDTO.java index 70f1f6ceb7..da782183e1 100644 --- a/irs-api/src/main/java/net/catenax/irs/dto/AssemblyPartRelationshipDTO.java +++ b/irs-api/src/main/java/net/catenax/irs/dto/AssemblyPartRelationshipDTO.java @@ -22,7 +22,7 @@ * AssemblyPartRelationshipDTO model used for internal application use */ @Data -@Builder(toBuilder = true, setterPrefix = "with") +@Builder(toBuilder = true) @JsonDeserialize(builder = AssemblyPartRelationshipDTO.AssemblyPartRelationshipDTOBuilder.class) public class AssemblyPartRelationshipDTO { /** @@ -38,7 +38,7 @@ public class AssemblyPartRelationshipDTO { /** * Builder class */ - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class AssemblyPartRelationshipDTOBuilder { } diff --git a/irs-api/src/main/java/net/catenax/irs/dto/ChildDataDTO.java b/irs-api/src/main/java/net/catenax/irs/dto/ChildDataDTO.java index 9ede426c74..06365a704b 100644 --- a/irs-api/src/main/java/net/catenax/irs/dto/ChildDataDTO.java +++ b/irs-api/src/main/java/net/catenax/irs/dto/ChildDataDTO.java @@ -36,7 +36,7 @@ public class ChildDataDTO { /** * Builder class */ - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class ChildDataDTOBuilder { } diff --git a/irs-api/src/main/java/net/catenax/irs/exceptions/AspectNotSupportedException.java b/irs-api/src/main/java/net/catenax/irs/exceptions/AspectNotSupportedException.java index fd79088022..7a256e51b9 100644 --- a/irs-api/src/main/java/net/catenax/irs/exceptions/AspectNotSupportedException.java +++ b/irs-api/src/main/java/net/catenax/irs/exceptions/AspectNotSupportedException.java @@ -9,7 +9,7 @@ // package net.catenax.irs.exceptions; -import net.catenax.irs.connector.annotations.ExcludeFromCodeCoverageGeneratedReport; +import net.catenax.irs.annotations.ExcludeFromCodeCoverageGeneratedReport; import net.catenax.irs.controllers.ApiErrorsConstants; /** diff --git a/irs-api/src/main/java/net/catenax/irs/exceptions/EntityNotFoundException.java b/irs-api/src/main/java/net/catenax/irs/exceptions/EntityNotFoundException.java index f3bc57eabb..76cb080bd5 100644 --- a/irs-api/src/main/java/net/catenax/irs/exceptions/EntityNotFoundException.java +++ b/irs-api/src/main/java/net/catenax/irs/exceptions/EntityNotFoundException.java @@ -9,7 +9,7 @@ // package net.catenax.irs.exceptions; -import net.catenax.irs.connector.annotations.ExcludeFromCodeCoverageGeneratedReport; +import net.catenax.irs.annotations.ExcludeFromCodeCoverageGeneratedReport; /** * General entity not found exception. diff --git a/irs-api/src/main/java/net/catenax/irs/exceptions/MaxDepthTooLargeException.java b/irs-api/src/main/java/net/catenax/irs/exceptions/MaxDepthTooLargeException.java index 1f039c1d1b..a449933e78 100644 --- a/irs-api/src/main/java/net/catenax/irs/exceptions/MaxDepthTooLargeException.java +++ b/irs-api/src/main/java/net/catenax/irs/exceptions/MaxDepthTooLargeException.java @@ -9,8 +9,8 @@ // package net.catenax.irs.exceptions; +import net.catenax.irs.annotations.ExcludeFromCodeCoverageGeneratedReport; import net.catenax.irs.configuration.IrsConfiguration; -import net.catenax.irs.connector.annotations.ExcludeFromCodeCoverageGeneratedReport; /** * Exception thrown by the service when the {@link net.catenax.irs.component.RegisterJob#getDepth()} @@ -20,6 +20,7 @@ public class MaxDepthTooLargeException extends RuntimeException { /** * Generate a new instance of a {@link MaxDepthTooLargeException} + * * @param message Exception message. */ public MaxDepthTooLargeException(final String message) { diff --git a/irs-api/src/main/java/net/catenax/irs/persistence/MinioBlobPersistence.java b/irs-api/src/main/java/net/catenax/irs/persistence/MinioBlobPersistence.java index da110d1aea..f1158969e5 100644 --- a/irs-api/src/main/java/net/catenax/irs/persistence/MinioBlobPersistence.java +++ b/irs-api/src/main/java/net/catenax/irs/persistence/MinioBlobPersistence.java @@ -10,17 +10,6 @@ package net.catenax.irs.persistence; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - import io.minio.BucketExistsArgs; import io.minio.GetObjectArgs; import io.minio.GetObjectResponse; @@ -47,11 +36,22 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + /** * BlobPersistence implementation using the min.io library */ @Slf4j -@SuppressWarnings("PMD.ExcessiveImports") +@SuppressWarnings({"PMD.ExcessiveImports", "PMD.PreserveStackTrace"}) public class MinioBlobPersistence implements BlobPersistence { private static final Integer EXPIRE_AFTER_DAYS = 7; @@ -59,7 +59,7 @@ public class MinioBlobPersistence implements BlobPersistence { private final String bucketName; public MinioBlobPersistence(final String endpoint, final String accessKey, final String secretKey, - final String bucketName) throws BlobPersistenceException { + final String bucketName) throws BlobPersistenceException { this(bucketName, createClient(endpoint, accessKey, secretKey)); } @@ -103,10 +103,10 @@ public void putBlob(final String targetBlobName, final byte[] blob) throws BlobP try { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(blob); minioClient.putObject(PutObjectArgs.builder() - .bucket(bucketName) - .object(targetBlobName) - .stream(byteArrayInputStream, byteArrayInputStream.available(), -1) - .build()); + .bucket(bucketName) + .object(targetBlobName) + .stream(byteArrayInputStream, byteArrayInputStream.available(), -1) + .build()); } catch (ServerException | InsufficientDataException | ErrorResponseException | IOException | NoSuchAlgorithmException | InvalidKeyException | InvalidResponseException | XmlParserException | InternalException e) { throw new BlobPersistenceException("Encountered error while trying to store blob", e); } @@ -130,6 +130,7 @@ public Optional getBlob(final String sourceBlobName) throws BlobPersiste } catch (IOException e) { throw createLoadFailedException(e); } + } private BlobPersistenceException createLoadFailedException(final Throwable cause) { @@ -142,10 +143,10 @@ public Collection findBlobByPrefix(final String prefix) { ListObjectsArgs.builder().prefix(prefix).bucket(bucketName).build()); return StreamSupport.stream(results.spliterator(), false) - .flatMap(this::getItem) - .map(Item::objectName) - .flatMap(this::getBlobIfPresent) - .collect(Collectors.toList()); + .flatMap(this::getItem) + .map(Item::objectName) + .flatMap(this::getBlobIfPresent) + .collect(Collectors.toList()); } @Override diff --git a/irs-api/src/main/java/net/catenax/irs/services/IrsItemGraphQueryService.java b/irs-api/src/main/java/net/catenax/irs/services/IrsItemGraphQueryService.java index d3ede6feaf..571d0225df 100644 --- a/irs-api/src/main/java/net/catenax/irs/services/IrsItemGraphQueryService.java +++ b/irs-api/src/main/java/net/catenax/irs/services/IrsItemGraphQueryService.java @@ -9,8 +9,10 @@ // package net.catenax.irs.services; +import static net.catenax.irs.dtos.IrsCommonConstants.DEPTH_ID_KEY; +import static net.catenax.irs.dtos.IrsCommonConstants.ROOT_ITEM_ID_KEY; + import java.nio.charset.StandardCharsets; -import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -23,10 +25,10 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.catenax.irs.aaswrapper.job.AASRecursiveJobHandler; import net.catenax.irs.aaswrapper.job.AASTransferProcess; import net.catenax.irs.aaswrapper.job.ItemContainer; import net.catenax.irs.aaswrapper.job.ItemDataRequest; +import net.catenax.irs.annotations.ExcludeFromCodeCoverageGeneratedReport; import net.catenax.irs.component.ChildItem; import net.catenax.irs.component.GlobalAssetIdentification; import net.catenax.irs.component.Job; @@ -36,7 +38,6 @@ import net.catenax.irs.component.Relationship; import net.catenax.irs.component.enums.BomLifecycle; import net.catenax.irs.component.enums.JobState; -import net.catenax.irs.connector.annotations.ExcludeFromCodeCoverageGeneratedReport; import net.catenax.irs.connector.job.JobInitiateResponse; import net.catenax.irs.connector.job.JobOrchestrator; import net.catenax.irs.connector.job.JobStore; @@ -69,7 +70,7 @@ public class IrsItemGraphQueryService implements IIrsItemGraphQueryService { @Override public JobHandle registerItemJob(final @NonNull RegisterJob request) { final String uuid = request.getGlobalAssetId().substring(IrsApiConstants.URN_PREFIX_SIZE); - final var params = Map.of(AASRecursiveJobHandler.ROOT_ITEM_ID_KEY, uuid, AASRecursiveJobHandler.DEPTH_ID_KEY, String.valueOf(request.getDepth())); + final var params = Map.of(ROOT_ITEM_ID_KEY, uuid, DEPTH_ID_KEY, String.valueOf(request.getDepth())); final JobInitiateResponse jobInitiateResponse = orchestrator.startJob(params); if (jobInitiateResponse.getStatus().equals(ResponseStatus.OK)) { @@ -88,10 +89,9 @@ public Jobs jobLifecycle(final @NonNull String jobId) { @Override public List getJobsByJobState(final @NonNull List jobStates) { - final List jobs = jobStore.findByStates( - jobStates.stream().map(this::convert).collect(Collectors.toList())); + final List jobs = jobStore.findByStates(jobStates); - return jobs.stream().map(MultiTransferJob::getJobId).map(UUID::fromString).collect(Collectors.toList()); + return jobs.stream().map(x -> x.getJob().getJobId()).collect(Collectors.toList()); } @Override @@ -102,7 +102,7 @@ public Job cancelJobById(final @NonNull UUID jobId) { if (canceled.isPresent()) { final MultiTransferJob job = canceled.get(); - return Job.builder().jobId(jobId).jobState(convert(job.getState())).build(); + return job.getJob(); } else { throw new EntityNotFoundException("No job exists with id " + jobId); } @@ -112,18 +112,13 @@ public Job cancelJobById(final @NonNull UUID jobId) { public Jobs getJobForJobId(final UUID jobId) { final Optional multiTransferJob = jobStore.find(jobId.toString()); if (multiTransferJob.isPresent()) { - final MultiTransferJob job = multiTransferJob.get(); - final Job.JobBuilder builder = Job.builder() - .jobId(UUID.fromString(job.getJobId())) - .jobState(convert(job.getState())); - job.getCompletionDate().ifPresent(date -> builder.jobCompleted(date.toInstant(ZoneOffset.UTC))); - final Job jobToReturn = builder.build(); + final MultiTransferJob multiJob = multiTransferJob.get(); final var relationships = new ArrayList(); try { - final Optional blob = blobStore.getBlob(job.getJobId()); + final Optional blob = blobStore.getBlob(multiJob.getJob().getJobId().toString()); final byte[] bytes = blob.orElseThrow( - () -> new EntityNotFoundException("Could not find stored data for job with id " + jobId)); + () -> new EntityNotFoundException("Could not find stored data for multiJob with id " + jobId)); final ItemContainer itemContainer = new JsonUtil().fromString(new String(bytes, StandardCharsets.UTF_8), ItemContainer.class); final List assemblyPartRelationships = itemContainer.getAssemblyPartRelationships(); @@ -131,7 +126,7 @@ public Jobs getJobForJobId(final UUID jobId) { } catch (BlobPersistenceException e) { log.error("Unable to read blob", e); } - return Jobs.builder().job(jobToReturn).relationships(relationships).build(); + return Jobs.builder().job(multiJob.getJob()).relationships(relationships).build(); } else { throw new EntityNotFoundException("No job exists with id " + jobId); } @@ -159,41 +154,4 @@ private Stream convert(final AssemblyPartRelationshipDTO dto) { .build()) .build()); } - - private JobState convert(final net.catenax.irs.connector.job.JobState state) { - switch (state) { - case COMPLETED: - return JobState.COMPLETED; - case IN_PROGRESS: - return JobState.RUNNING; - case ERROR: - return JobState.ERROR; - case INITIAL: - return JobState.INITIAL; - case TRANSFERS_FINISHED: - return JobState.TRANSFERS_FINISHED; - case CANCELED: - return JobState.CANCELED; - default: - throw new IllegalArgumentException("Cannot convert JobState of type " + state); - } - } - - private net.catenax.irs.connector.job.JobState convert(final JobState state) { - switch (state) { - case COMPLETED: - return net.catenax.irs.connector.job.JobState.COMPLETED; - case RUNNING: - return net.catenax.irs.connector.job.JobState.IN_PROGRESS; - case ERROR: - return net.catenax.irs.connector.job.JobState.ERROR; - case INITIAL: - return net.catenax.irs.connector.job.JobState.INITIAL; - case TRANSFERS_FINISHED: - return net.catenax.irs.connector.job.JobState.TRANSFERS_FINISHED; - default: - throw new IllegalArgumentException("Cannot convert JobState of type " + state); - } - } - } diff --git a/irs-api/src/test/java/net/catenax/irs/IrsApplicationTests.java b/irs-api/src/test/java/net/catenax/irs/IrsApplicationTests.java index a2289b3199..fc07dcf17e 100644 --- a/irs-api/src/test/java/net/catenax/irs/IrsApplicationTests.java +++ b/irs-api/src/test/java/net/catenax/irs/IrsApplicationTests.java @@ -1,8 +1,8 @@ package net.catenax.irs; import static java.nio.charset.StandardCharsets.UTF_8; -import static net.catenax.irs.aaswrapper.job.AASRecursiveJobHandler.DEPTH_ID_KEY; -import static net.catenax.irs.aaswrapper.job.AASRecursiveJobHandler.ROOT_ITEM_ID_KEY; +import static net.catenax.irs.dtos.IrsCommonConstants.DEPTH_ID_KEY; +import static net.catenax.irs.dtos.IrsCommonConstants.ROOT_ITEM_ID_KEY; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; @@ -12,11 +12,10 @@ import net.catenax.irs.aaswrapper.job.AASTransferProcess; import net.catenax.irs.aaswrapper.job.ItemDataRequest; +import net.catenax.irs.component.enums.JobState; import net.catenax.irs.connector.job.JobInitiateResponse; import net.catenax.irs.connector.job.JobOrchestrator; -import net.catenax.irs.connector.job.JobState; import net.catenax.irs.connector.job.JobStore; -import net.catenax.irs.connector.job.MultiTransferJob; import net.catenax.irs.connector.job.ResponseStatus; import net.catenax.irs.persistence.BlobPersistence; import org.awaitility.Awaitility; @@ -32,8 +31,7 @@ import org.springframework.test.context.ActiveProfiles; @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -@ActiveProfiles(profiles = { "local", - "test" +@ActiveProfiles(profiles = { "local", "test" }) class IrsApplicationTests { @@ -59,7 +57,7 @@ void contextLoads() { @Test void generatedOpenApiMatchesContract() throws Exception { final String generatedYaml = this.restTemplate.getForObject("http://localhost:" + port + "/api/api-docs.yaml", - String.class); + String.class); final String fixedYaml = Files.readString(new File("../api/irs-v1.0.yaml").toPath(), UTF_8); assertThat(generatedYaml).isEqualToNormalizingNewlines(fixedYaml); } @@ -75,7 +73,7 @@ void shouldStoreBlobResultWhenRunningJob() throws Exception { .atMost(10, TimeUnit.SECONDS) .pollInterval(100, TimeUnit.MILLISECONDS) .until(() -> jobStore.find(response.getJobId()) - .map(MultiTransferJob::getState) + .map(s -> s.getJob().getJobState()) .map(state -> state == JobState.COMPLETED) .orElse(false)); diff --git a/irs-api/src/test/java/net/catenax/irs/connector/job/InMemoryJobStoreTest.java b/irs-api/src/test/java/net/catenax/irs/connector/job/InMemoryJobStoreTest.java new file mode 100644 index 0000000000..76a3004452 --- /dev/null +++ b/irs-api/src/test/java/net/catenax/irs/connector/job/InMemoryJobStoreTest.java @@ -0,0 +1,447 @@ +package net.catenax.irs.connector.job; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URL; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import com.github.javafaker.Faker; +import net.catenax.irs.component.GlobalAssetIdentification; +import net.catenax.irs.component.Job; +import net.catenax.irs.component.JobErrorDetails; +import net.catenax.irs.component.enums.JobState; +import net.catenax.irs.util.TestMother; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpMethod; + +class InMemoryJobStoreTest { + final int TTL_IN_HOUR_SECONDS = 3600; + InMemoryJobStore sut = new InMemoryJobStore(); + Faker faker = new Faker(); + TestMother generate = new TestMother(); + MultiTransferJob job = generate.job(JobState.UNSAVED); + MultiTransferJob job2 = generate.job(JobState.UNSAVED); + MultiTransferJob originalJob = job.toBuilder().build(); + String otherJobId = faker.lorem().characters(); + TransferProcess process1 = generate.transfer(); + TransferProcess process2 = generate.transfer(); + String processId1 = process1.getId(); + String processId2 = process2.getId(); + String errorDetail = faker.lorem().sentence(); + + @Test + void find_WhenNotFound() { + assertThat(sut.find(otherJobId)).isEmpty(); + } + + @Test + void findByProcessId_WhenFound() { + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.create(job2); + sut.addTransferProcess(job2.getJob().getJobId().toString(), processId2); + + refreshJob(); + assertThat(sut.findByProcessId(processId1)).contains(job); + } + + @Test + void findByProcessId_WhenNotFound() { + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + + assertThat(sut.findByProcessId(processId2)).isEmpty(); + } + + @Test + void create_and_find() { + + sut.create(job); + assertThat(sut.find(job.getJob().getJobId().toString())).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(originalJob.toBuilder().transitionInitial().build()); + assertThat(sut.find(otherJobId)).isEmpty(); + } + + @Test + void addTransferProcess() { + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + refreshJob(); + assertThat(job.getTransferProcessIds()).containsExactly(processId1); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.RUNNING); + } + + @Test + void completeTransferProcess_WhenJobNotFound() { + sut.completeTransferProcess(otherJobId, process1); + } + + @Test + void completeTransferProcess_WhenTransferFound() { + // Arrange + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + + // Act + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); + + // Assert + assertThat(job.getTransferProcessIds()).isEmpty(); + } + + @Test + void completeTransferProcess_WhenTransferNotFound() { + // Act + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); + + // Assert + assertThat(job.getTransferProcessIds()).isEmpty(); + } + + @Test + void completeTransferProcess_WhenTransferAlreadyCompleted() { + // Arrange + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); + + // Act + assertThatExceptionOfType(IllegalStateException.class).isThrownBy( + () -> sut.completeTransferProcess(job.getJob().getJobId().toString(), process1)); + + // Assert + refreshJob(); + assertThat(job.getTransferProcessIds()).isEmpty(); + } + + @Test + void completeTransferProcess_WhenNotLastTransfer_DoesNotTransitionJob() { + // Arrange + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId2); + + // Act + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); + + // Assert + refreshJob(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.RUNNING); + } + + @Test + void completeTransferProcess_WhenLastTransfer_TransitionsJob() { + // Arrange + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId2); + + // Act + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process2); + + // Assert + refreshJob(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.TRANSFERS_FINISHED); + } + + @Test + void completeJob_WhenJobNotFound() { + // Arrange + sut.create(job); + // Act + sut.completeJob(otherJobId); + refreshJob(); + // Assert + assertThat(job.getJob().getJobState()).isEqualTo(JobState.INITIAL); + } + + @Test + void completeJob_WhenJobInInitialState() { + // Arrange + sut.create(job); + sut.create(job2); + // Act + sut.completeJob(job.getJob().getJobId().toString()); + // Assert + refreshJob(); + refreshJob2(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.COMPLETED); + assertTrue(Optional.ofNullable(job.getJob().getJobCompleted()).isPresent()); + assertThat(job2.getJob().getJobState()).isEqualTo(JobState.INITIAL); + } + + @Test + void completeJob_WhenJobInTransfersCompletedState() { + // Arrange + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); + // Act + sut.completeJob(job.getJob().getJobId().toString()); + // Assert + refreshJob(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.COMPLETED); + assertTrue(Optional.ofNullable(job.getJob().getJobCompleted()).isPresent()); + } + + @Test + void completeJob_WhenJobInTransfersInProgressState() { + // Arrange + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + // Act + assertThatExceptionOfType(IllegalStateException.class).isThrownBy( + () -> sut.completeJob(job.getJob().getJobId().toString())); + // Assert + refreshJob(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.RUNNING); + } + + @Test + void markJobInError_WhenJobNotFound() { + // Arrange + sut.create(job); + // Act + sut.markJobInError(otherJobId, errorDetail); + // Assert + refreshJob(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.INITIAL); + } + + @Test + void markJobInError_WhenJobInInitialState() { + // Arrange + sut.create(job); + sut.create(job2); + // Act + sut.markJobInError(job.getJob().getJobId().toString(), errorDetail); + // Assert + refreshJob(); + refreshJob2(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.ERROR); + assertThat(job2.getJob().getJobState()).isEqualTo(JobState.INITIAL); + assertThat(job.getJob().getException().getErrorDetail()).isEqualTo(errorDetail); + assertTrue(Optional.ofNullable(job.getJob().getJobCompleted()).isPresent()); + } + + @Test + void markJobInError_WhenJobInTransfersCompletedState() { + // Arrange + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); + // Act + sut.markJobInError(job.getJob().getJobId().toString(), errorDetail); + // Assert + refreshJob(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.ERROR); + assertTrue(Optional.ofNullable(job.getJob().getJobCompleted()).isPresent()); + } + + @Test + void markJobInError_WhenJobInTransfersInProgressState() { + // Arrange + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + // Act + sut.markJobInError(job.getJob().getJobId().toString(), errorDetail); + // Assert + refreshJob(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.ERROR); + assertTrue(Optional.ofNullable(job.getJob().getJobCompleted()).isPresent()); + } + + @Test + void shouldFindCompletedJobsOlderThanFiveHours() { + // Arrange + final Instant nowPlusFiveHours = Instant.now().plusSeconds(TTL_IN_HOUR_SECONDS * 5); + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); + sut.completeJob(job.getJob().getJobId().toString()); + // Act + final List completedJobs = sut.findByStateAndCompletionDateOlderThan(JobState.COMPLETED, + nowPlusFiveHours); + // Assert + assertThat(completedJobs.size()).isEqualTo(1); + assertThat(completedJobs.get(0).getJob().getJobState()).isEqualTo(JobState.COMPLETED); + assertTrue(Optional.ofNullable(completedJobs.get(0).getJob().getJobCompleted()).isPresent()); + } + + @Test + void shouldFindFailedJobsOlderThanFiveHours() { + // Arrange + final Instant nowPlusFiveHours = Instant.now().plusSeconds(TTL_IN_HOUR_SECONDS * 5); + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.markJobInError(job.getJob().getJobId().toString(), errorDetail); + // Act + final List failedJobs = sut.findByStateAndCompletionDateOlderThan(JobState.ERROR, + nowPlusFiveHours); + // Assert + assertThat(failedJobs.size()).isEqualTo(1); + assertThat(failedJobs.get(0).getJob().getJobState()).isEqualTo(JobState.ERROR); + assertTrue(Optional.ofNullable(failedJobs.get(0).getJob().getJobCompleted()).isPresent()); + } + + @Test + void shouldDeleteJobById() { + // Arrange + sut.create(job); + // Act + sut.deleteJob(job.getJob().getJobId().toString()); + // Assert + assertThat(sut.find(job.getJob().getJobId().toString())).isEmpty(); + } + + @Test + void jobStateIsInitial() { + sut.create(job); + final Optional multiTransferJob = sut.get(job.getJob().getJobId().toString()); + assertThat(multiTransferJob).isPresent(); + assertThat(multiTransferJob.get().getJob().getJobState()).isEqualTo(JobState.INITIAL); + } + + @Test + void jobStateIsInProgress() { + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + final Optional multiTransferJob = sut.get(job.getJob().getJobId().toString()); + assertThat(multiTransferJob).isPresent(); + assertThat(multiTransferJob.get().getJob().getJobState()).isEqualTo(JobState.RUNNING); + } + + @Test + void shouldFindJobsByCompletedJobState() { + // Arrange + sut.create(job); + sut.completeJob(job.getJob().getJobId().toString()); + sut.create(job2); + // Act + final List foundJobs = sut.findByStates(List.of(JobState.COMPLETED)); + // Assert + assertThat(foundJobs.size()).isEqualTo(1); + assertThat(foundJobs.get(0).getJob().getJobId().toString()).isEqualTo(job.getJob().getJobId().toString()); + } + + @Test + void shouldFindJobsByErrorJobState() { + // Arrange + sut.create(job); + sut.markJobInError(job.getJob().getJobId().toString(), "errorDetail"); + // Act + final List foundJobs = sut.findByStates(List.of(JobState.ERROR)); + // Assert + assertThat(foundJobs.size()).isEqualTo(1); + assertThat(foundJobs.get(0).getJob().getJobId().toString()).isEqualTo(job.getJob().getJobId().toString()); + } + + private void refreshJob() { + job = sut.find(job.getJob().getJobId().toString()).get(); + } + + private void refreshJob2() { + job2 = sut.find(job2.getJob().getJobId().toString()).get(); + } + + private Job createJob() { + GlobalAssetIdentification globalAssetId = GlobalAssetIdentification.builder() + .globalAssetId(UUID.randomUUID().toString()) + .build(); + + return Job.builder() + .globalAssetId(globalAssetId) + .jobId(UUID.randomUUID()) + .jobState(JobState.INITIAL) + .createdOn(Instant.now()) + .lastModifiedOn(Instant.now()) + .requestUrl(fakeURL()) + .action(HttpMethod.POST.toString()) + .build(); + } + + private URL fakeURL() { + try { + return new URL("http://localhost:8888/fake/url"); + } catch (Exception e) { + return null; + } + } + + @Test + void shouldStoreAndLoadJob() { + // arrange + final var jobId = UUID.randomUUID().toString(); + final MultiTransferJob job = createJob(jobId); + + // act + sut.create(job); + final Optional multiTransferJob = sut.find(jobId); + + // assert + assertThat(multiTransferJob).isPresent(); + + final MultiTransferJob storedJob = multiTransferJob.get(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(storedJob.getJob().getJobId().toString()).isEqualTo(job.getJob().getJobId().toString()); + softly.assertThat(storedJob.getJob().getJobState()).isEqualTo(JobState.INITIAL); + softly.assertThat(storedJob.getJob().getException().getErrorDetail()) + .isEqualTo(job.getJob().getException().getErrorDetail()); + softly.assertThat(storedJob.getJob().getJobCompleted()).isEqualTo(job.getJob().getJobCompleted()); + softly.assertThat(storedJob.getJobData()).isEqualTo(job.getJobData()); + softly.assertThat(storedJob.getCompletedTransfers()).isEqualTo(job.getCompletedTransfers()); + }); + + } + + private MultiTransferJob createJob(final String jobId) { + return MultiTransferJob.builder() + .job(Job.builder() + .jobId(UUID.fromString(jobId)) + .jobState(JobState.UNSAVED) + .jobCompleted(Instant.now()) + .exception(JobErrorDetails.builder() + .exception("SomeError") + .exceptionDate(Instant.now()) + .build()) + .build()) + .jobData(Map.of("dataKey", "dataValue")) + .build(); + } + + @Test + void shouldTransitionJobToComplete() { + // arrange + final var jobId = UUID.randomUUID().toString(); + final MultiTransferJob job = createJob(jobId); + + // act + sut.create(job); + sut.completeJob(jobId); + final Optional multiTransferJob = sut.find(jobId); + + // assert + assertThat(multiTransferJob).isPresent(); + + final MultiTransferJob storedJob = multiTransferJob.get(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(storedJob.getJob().getJobId().toString()).isEqualTo(job.getJob().getJobId().toString()); + softly.assertThat(storedJob.getJob().getJobState()).isEqualTo(JobState.COMPLETED); + softly.assertThat(storedJob.getJob().getException().getErrorDetail()) + .isEqualTo(job.getJob().getException().getErrorDetail()); + softly.assertThat(storedJob.getJobData()).isEqualTo(job.getJobData()); + softly.assertThat(storedJob.getCompletedTransfers()).isEqualTo(job.getCompletedTransfers()); + }); + } + +} \ No newline at end of file diff --git a/connector/edc-recursive-job/src/test/java/net/catenax/irs/connector/job/JobOrchestratorTest.java b/irs-api/src/test/java/net/catenax/irs/connector/job/JobOrchestratorTest.java similarity index 57% rename from connector/edc-recursive-job/src/test/java/net/catenax/irs/connector/job/JobOrchestratorTest.java rename to irs-api/src/test/java/net/catenax/irs/connector/job/JobOrchestratorTest.java index f8675959fb..2cc7c720dc 100644 --- a/connector/edc-recursive-job/src/test/java/net/catenax/irs/connector/job/JobOrchestratorTest.java +++ b/irs-api/src/test/java/net/catenax/irs/connector/job/JobOrchestratorTest.java @@ -1,20 +1,5 @@ package net.catenax.irs.connector.job; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.util.Optional; -import java.util.function.Consumer; -import java.util.regex.Pattern; -import java.util.stream.Stream; - import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE; import static org.mockito.ArgumentMatchers.any; @@ -27,6 +12,30 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import java.net.URL; +import java.time.Instant; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import com.github.javafaker.Faker; +import net.catenax.irs.component.GlobalAssetIdentification; +import net.catenax.irs.component.Job; +import net.catenax.irs.component.enums.JobState; +import net.catenax.irs.util.TestMother; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpMethod; + @ExtendWith(MockitoExtension.class) class JobOrchestratorTest { @@ -47,28 +56,31 @@ class JobOrchestratorTest { Pattern uuid = Pattern.compile("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"); TestMother generate = new TestMother(); - MultiTransferJob job = generate.job(JobState.IN_PROGRESS); + MultiTransferJob job = generate.job(JobState.RUNNING); DataRequest dataRequest = generate.dataRequest(); DataRequest dataRequest2 = generate.dataRequest(); TransferInitiateResponse okResponse = generate.okResponse(); TransferInitiateResponse okResponse2 = generate.okResponse(); TransferProcess transfer = generate.transfer(); + Faker faker = new Faker(); + GlobalAssetIdentification globalAssetId = GlobalAssetIdentification.builder() + .globalAssetId(faker.lorem().characters()) + .build(); @Test void startJob_storesJobWithDataAndState() { - assertThat(startJob()) - .usingRecursiveComparison() - .ignoringFields("jobId") - .isEqualTo(MultiTransferJob.builder() - .jobData(job.getJobData()) - .state(JobState.UNSAVED) - .build()); + MultiTransferJob job2 = startJob(); + assertThat(job2).usingRecursiveComparison() + .ignoringFields("job.job.jobId") + .isEqualTo(MultiTransferJob.builder() + .jobData(job.getJobData()) + .job(job2.getJob().toBuilder().jobState(JobState.UNSAVED).build()) + .build()); } @Test void startJob_storesJobWithUuidAsIdentifier() { - assertThat(startJob().getJobId()) - .matches(uuid); + assertThat(startJob().getJob().getJobId().toString()).matches(uuid.asPredicate()); } @Test @@ -86,11 +98,11 @@ void startJob_callsHandlerWithJob() { void startJob_WithTwoDataRequests_StartsTransfers() { // Arrange when(handler.initiate(any(MultiTransferJob.class))) - .thenReturn(Stream.of(dataRequest, dataRequest2)); + .thenReturn(Stream.of(dataRequest, dataRequest2)); when(processManager.initiateRequest(eq(dataRequest), any(), any())) - .thenReturn(okResponse); + .thenReturn(okResponse); when(processManager.initiateRequest(eq(dataRequest2), any(), any())) - .thenReturn(okResponse2); + .thenReturn(okResponse2); // Act var newJob = startJob(); @@ -103,40 +115,40 @@ void startJob_WithTwoDataRequests_StartsTransfers() { @Test void startJob_WithZeroDataRequest_CompletesJob() { // Arrange - when(handler.initiate(any(MultiTransferJob.class))) - .thenReturn(Stream.empty()); + when(handler.initiate(any(MultiTransferJob.class))).thenReturn(Stream.empty()); - // Act var response = sut.startJob(job.getJobData()); var newJob = getStartedJob(); // Assert verifyNoInteractions(processManager); - verify(jobStore).completeJob(newJob.getJobId()); + verify(jobStore).completeJob(newJob.getJob().getJobId().toString()); verifyNoMoreInteractions(jobStore); verify(handler).complete(newJob); - assertThat(response) - .isEqualTo( - JobInitiateResponse.builder().jobId(newJob.getJobId()).status(ResponseStatus.OK).build()); + assertThat(response).isEqualTo(JobInitiateResponse.builder() + .jobId(newJob.getJob().getJobId().toString()) + .status(ResponseStatus.OK) + .build()); } @Test void startJob_WithSuccessfulTransferStarts_ReturnsOk() { // Arrange when(handler.initiate(any(MultiTransferJob.class))) - .thenReturn(Stream.of(dataRequest)); + .thenReturn(Stream.of(dataRequest)); when(processManager.initiateRequest(eq(dataRequest), any(), any())) - .thenReturn(okResponse); + .thenReturn(okResponse); // Act var response = sut.startJob(job.getJobData()); // Assert var newJob = getStartedJob(); - assertThat(response) - .isEqualTo( - JobInitiateResponse.builder().jobId(newJob.getJobId()).status(ResponseStatus.OK).build()); + assertThat(response).isEqualTo(JobInitiateResponse.builder() + .jobId(newJob.getJob().getJobId().toString()) + .status(ResponseStatus.OK) + .build()); } @ParameterizedTest @@ -144,9 +156,9 @@ void startJob_WithSuccessfulTransferStarts_ReturnsOk() { void startJob_WhenTransferStartUnsuccessful_Abort(ResponseStatus status) { // Arrange when(handler.initiate(any())) - .thenReturn(Stream.of(dataRequest, dataRequest2)); + .thenReturn(Stream.of(dataRequest, dataRequest2)); when(processManager.initiateRequest(eq(dataRequest), any(), any())) - .thenReturn(generate.response(status)); + .thenReturn(generate.response(status)); // Act var response = sut.startJob(job.getJobData()); @@ -159,46 +171,46 @@ void startJob_WhenTransferStartUnsuccessful_Abort(ResponseStatus status) { verify(jobStore).create(jobCaptor.capture()); verifyNoMoreInteractions(jobStore); - assertThat(response) - .isEqualTo( - JobInitiateResponse.builder().jobId(jobCaptor.getValue().getJobId()).status(status).build()); + assertThat(response).isEqualTo(JobInitiateResponse.builder() + .jobId(jobCaptor.getValue().getJob().getJobId().toString()) + .status(status) + .build()); } - @Test void startJob_WhenHandlerInitiateThrows_StopJob() { // Arrange - when(handler.initiate(any(MultiTransferJob.class))) - .thenThrow(new RuntimeException()); + when(handler.initiate(any(MultiTransferJob.class))).thenThrow(new RuntimeException()); // Act var response = sut.startJob(job.getJobData()); // Assert verify(jobStore).create(jobCaptor.capture()); - verify(jobStore).markJobInError(jobCaptor.getValue().getJobId(), "Handler method failed"); + verify(jobStore).markJobInError(jobCaptor.getValue().getJob().getJobId().toString(), "Handler method failed"); verifyNoMoreInteractions(jobStore); verifyNoInteractions(processManager); - assertThat(response) - .isEqualTo( - JobInitiateResponse.builder().jobId(jobCaptor.getValue().getJobId()).status(ResponseStatus.FATAL_ERROR).build()); + assertThat(response).isEqualTo(JobInitiateResponse.builder() + .jobId(jobCaptor.getValue().getJob().getJobId().toString()) + .status(ResponseStatus.FATAL_ERROR) + .build()); } @Test void transferProcessCompleted_WhenCalledBackForCompletedTransfer_RunsNextTransfers() { // Arrange when(processManager.initiateRequest(eq(dataRequest), any(), any())) - .thenReturn(okResponse); + .thenReturn(okResponse); when(processManager.initiateRequest(eq(dataRequest2), any(), any())) - .thenReturn(okResponse2); - + .thenReturn(okResponse2); // Act callCompleteAndReturnNextTransfers(Stream.of(dataRequest, dataRequest2)); // Assert verify(processManager).initiateRequest(eq(dataRequest), any(), any()); - verify(jobStore).completeTransferProcess(job.getJobId(), transfer); + verify(jobStore).completeTransferProcess(job.getJob().getJobId().toString(), transfer); + } @Test @@ -207,8 +219,8 @@ void transferProcessCompleted_WhenCalledBackForCompletedTransfer_WithoutNextTran callCompleteAndReturnNextTransfers(Stream.empty()); // Assert - verify(jobStore).completeTransferProcess(job.getJobId(), transfer); - verify(jobStore).find(job.getJobId()); + verify(jobStore).completeTransferProcess(job.getJob().getJobId().toString(), transfer); + verify(jobStore).find(job.getJob().getJobId().toString()); verifyNoInteractions(processManager); verifyNoMoreInteractions(jobStore); } @@ -219,8 +231,8 @@ void transferProcessCompleted_WhenJobNotCompleted_DoesNotCallComplete() { callCompleteAndReturnNextTransfers(Stream.empty()); // Assert - verify(jobStore).completeTransferProcess(job.getJobId(), transfer); - verify(jobStore).find(job.getJobId()); + verify(jobStore).completeTransferProcess(job.getJob().getJobId().toString(), transfer); + verify(jobStore).find(job.getJob().getJobId().toString()); verifyNoMoreInteractions(jobStore); verifyNoMoreInteractions(handler); } @@ -228,34 +240,32 @@ void transferProcessCompleted_WhenJobNotCompleted_DoesNotCallComplete() { @Test void transferProcessCompleted_WhenJobCompleted_CallsComplete() { // Arrange - doAnswer(i -> byCompletingJob()) - .when(jobStore).completeTransferProcess(job.getJobId(), transfer); + doAnswer(i -> byCompletingJob()).when(jobStore) + .completeTransferProcess(job.getJob().getJobId().toString(), transfer); // Act callCompleteAndReturnNextTransfers(Stream.empty()); // Assert verify(handler).complete(job); - verify(jobStore).completeJob(job.getJobId()); + verify(jobStore).completeJob(job.getJob().getJobId().toString()); } - @Test void transferProcessCompleted_WhenHandlerCompleteThrows_StopJob() { // Arrange - doAnswer(i -> byCompletingJob()) - .when(jobStore).completeTransferProcess(job.getJobId(), transfer); + doAnswer(i -> byCompletingJob()).when(jobStore) + .completeTransferProcess(job.getJob().getJobId().toString(), transfer); doAnswer(i -> { - throw new RuntimeException(); - }) - .when(handler).complete(any()); + throw new JobException(); + }).when(handler).complete(any()); // Act callCompleteAndReturnNextTransfers(Stream.empty()); // Assert - verify(jobStore).markJobInError(job.getJobId(), "Handler method failed"); - verify(jobStore).find(job.getJobId()); + verify(jobStore).markJobInError(job.getJob().getJobId().toString(), "Handler method failed"); + verify(jobStore).find(job.getJob().getJobId().toString()); verifyNoMoreInteractions(jobStore); verifyNoInteractions(processManager); } @@ -263,8 +273,7 @@ void transferProcessCompleted_WhenHandlerCompleteThrows_StopJob() { @Test void transferProcessCompleted_WhenJobNotFound_Ignore() { // Arrange - when(jobStore.findByProcessId(transfer.getId())) - .thenReturn(Optional.empty()); + when(jobStore.findByProcessId(transfer.getId())).thenReturn(Optional.empty()); // Act callTransferProcessCompletedViaCallback(); @@ -275,14 +284,13 @@ void transferProcessCompleted_WhenJobNotFound_Ignore() { } @ParameterizedTest - @EnumSource(value = JobState.class, names = "IN_PROGRESS", mode = EXCLUDE) + @EnumSource(value = JobState.class, names = "RUNNING", mode = EXCLUDE) void transferProcessCompleted_WhenJobNotInProgress_Ignore(JobState state) { // Arrange - job = job.toBuilder().state(state).build(); + job = job.toBuilder().job(generate.fakeJob(state)).build(); // Act - when(jobStore.findByProcessId(transfer.getId())) - .thenReturn(Optional.of(job)); + when(jobStore.findByProcessId(transfer.getId())).thenReturn(Optional.of(job)); callTransferProcessCompletedViaCallback(); // Assert @@ -295,7 +303,7 @@ void transferProcessCompleted_WhenJobNotInProgress_Ignore(JobState state) { void transferProcessCompleted_WhenNextTransferStartUnsuccessful_Abort(ResponseStatus status) { // Arrange when(processManager.initiateRequest(eq(dataRequest), any(), any())) - .thenReturn(generate.response(status)); + .thenReturn(generate.response(status)); // Act callCompleteAndReturnNextTransfers(Stream.of(dataRequest, dataRequest2)); @@ -305,31 +313,28 @@ void transferProcessCompleted_WhenNextTransferStartUnsuccessful_Abort(ResponseSt verify(processManager, never()).initiateRequest(eq(dataRequest2), any(), any()); // temporarily created job should be deleted - verify(jobStore).markJobInError(job.getJobId(), "Failed to start a transfer"); + verify(jobStore).markJobInError(job.getJob().getJobId().toString(), "Failed to start a transfer"); verifyNoMoreInteractions(jobStore); } @Test void transferProcessCompleted_WhenHandlerRecurseThrows_StopJob() { // Arrange - when(jobStore.findByProcessId(transfer.getId())) - .thenReturn(Optional.of(job)); - when(handler.recurse(job, transfer)) - .thenThrow(new RuntimeException()); + when(jobStore.findByProcessId(transfer.getId())).thenReturn(Optional.of(job)); + when(handler.recurse(job, transfer)).thenThrow(new RuntimeException()); // Act callTransferProcessCompletedViaCallback(); // Assert - verify(jobStore).markJobInError(job.getJobId(), "Handler method failed"); + verify(jobStore).markJobInError(job.getJob().getJobId().toString(), "Handler method failed"); verifyNoMoreInteractions(jobStore); verifyNoInteractions(processManager); } private Object byCompletingJob() { job = job.toBuilder().transitionTransfersFinished().build(); - lenient().when(jobStore.find(job.getJobId())) - .thenReturn(Optional.of(job)); + lenient().when(jobStore.find(job.getJob().getJobId().toString())).thenReturn(Optional.of(job)); return null; } @@ -344,16 +349,37 @@ private MultiTransferJob getStartedJob() { } private void callCompleteAndReturnNextTransfers(Stream dataRequestStream) { - when(jobStore.findByProcessId(transfer.getId())) - .thenReturn(Optional.of(job)); - lenient().when(jobStore.find(job.getJobId())) - .thenReturn(Optional.of(job)); - when(handler.recurse(job, transfer)) - .thenReturn(dataRequestStream); + when(jobStore.findByProcessId(transfer.getId())).thenReturn(Optional.of(job)); + lenient().when(jobStore.find(job.getJob().getJobId().toString())).thenReturn(Optional.of(job)); + when(handler.recurse(job, transfer)).thenReturn(dataRequestStream); callTransferProcessCompletedViaCallback(); } private void callTransferProcessCompletedViaCallback() { sut.transferProcessCompleted(transfer); } + + private Job createJob() { + GlobalAssetIdentification globalAssetId = GlobalAssetIdentification.builder() + .globalAssetId(UUID.randomUUID().toString()) + .build(); + + return Job.builder() + .globalAssetId(globalAssetId) + .jobId(UUID.randomUUID()) + .jobState(JobState.UNSAVED) + .createdOn(Instant.now()) + .lastModifiedOn(Instant.now()) + .requestUrl(fakeURL()) + .action(HttpMethod.POST.toString()) + .build(); + } + + private URL fakeURL() { + try { + return new URL("http://localhost:8888/fake/url"); + } catch (Exception e) { + return null; + } + } } diff --git a/connector/edc-recursive-job/src/test/java/net/catenax/irs/connector/job/MultiTransferJobTest.java b/irs-api/src/test/java/net/catenax/irs/connector/job/MultiTransferJobTest.java similarity index 95% rename from connector/edc-recursive-job/src/test/java/net/catenax/irs/connector/job/MultiTransferJobTest.java rename to irs-api/src/test/java/net/catenax/irs/connector/job/MultiTransferJobTest.java index 4a92c32b73..20d9de1176 100644 --- a/connector/edc-recursive-job/src/test/java/net/catenax/irs/connector/job/MultiTransferJobTest.java +++ b/irs-api/src/test/java/net/catenax/irs/connector/job/MultiTransferJobTest.java @@ -1,10 +1,11 @@ package net.catenax.irs.connector.job; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + import com.github.javafaker.Faker; +import net.catenax.irs.util.TestMother; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - class MultiTransferJobTest { Faker faker = new Faker(); @@ -27,4 +28,6 @@ void getJobData_Immutable() { faker.lorem().word(), faker.lorem().word())); } + + } \ No newline at end of file diff --git a/irs-api/src/test/java/net/catenax/irs/connector/job/PersistentJobStoreTest.java b/irs-api/src/test/java/net/catenax/irs/connector/job/PersistentJobStoreTest.java index cf0c7df198..d0a9d6c638 100644 --- a/irs-api/src/test/java/net/catenax/irs/connector/job/PersistentJobStoreTest.java +++ b/irs-api/src/test/java/net/catenax/irs/connector/job/PersistentJobStoreTest.java @@ -3,18 +3,22 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; import com.github.javafaker.Faker; +import net.catenax.irs.component.Job; +import net.catenax.irs.component.JobErrorDetails; +import net.catenax.irs.component.enums.JobState; import net.catenax.irs.persistence.BlobPersistenceException; import net.catenax.irs.persistence.MinioBlobPersistence; import net.catenax.irs.testing.containers.MinioContainer; +import net.catenax.irs.util.JsonUtil; import net.catenax.irs.util.TestMother; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.AfterAll; @@ -28,6 +32,7 @@ class PersistentJobStoreTest { private static final String ACCESS_KEY = "accessKey"; private static final String SECRET_KEY = "secretKey"; + final int TTL_IN_HOUR_SECONDS = 3600; private static final MinioContainer minioContainer = new MinioContainer( new MinioContainer.CredentialsProvider(ACCESS_KEY, SECRET_KEY)).withReuse(true); @@ -72,9 +77,9 @@ void find_WhenNotFound() { @Test void findByProcessId_WhenFound() { sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); sut.create(job2); - sut.addTransferProcess(job2.getJobId(), processId2); + sut.addTransferProcess(job2.getJob().getJobId().toString(), processId2); refreshJob(); assertThat(sut.findByProcessId(processId1)).isPresent().get().usingRecursiveComparison().isEqualTo(job); @@ -83,7 +88,7 @@ void findByProcessId_WhenFound() { @Test void findByProcessId_WhenNotFound() { sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); assertThat(sut.findByProcessId(processId2)).isEmpty(); } @@ -91,23 +96,22 @@ void findByProcessId_WhenNotFound() { @Test void create_and_find() { sut.create(job); - assertThat(sut.find(job.getJobId())).isPresent() - .get() - .usingRecursiveComparison() - .isEqualTo(originalJob.toBuilder() - .completionDate(Optional.empty()) - .state(JobState.INITIAL) - .build()); + assertThat(sut.find(job.getJob().getJobId().toString())).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(originalJob.toBuilder() + .transitionInitial() + .build()); assertThat(sut.find(otherJobId)).isEmpty(); } @Test void addTransferProcess() { sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); refreshJob(); assertThat(job.getTransferProcessIds()).containsExactly(processId1); - assertThat(job.getState()).isEqualTo(JobState.IN_PROGRESS); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.RUNNING); } @Test @@ -115,14 +119,23 @@ void completeTransferProcess_WhenJobNotFound() { sut.completeTransferProcess(otherJobId, process1); } + @Test + void shouldSerializeAndDeserializeMultiTransferJob() { + final JsonUtil jsonUtil = new JsonUtil(); + final String firstSerialization = jsonUtil.asString(job); + final MultiTransferJob result = jsonUtil.fromString(firstSerialization, MultiTransferJob.class); + final String secondSerialization = jsonUtil.asString(result); + assertThat(firstSerialization).isEqualTo(secondSerialization); + } + @Test void completeTransferProcess_WhenTransferFound() { // Arrange sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); // Act - sut.completeTransferProcess(job.getJobId(), process1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); // Assert assertThat(job.getTransferProcessIds()).isEmpty(); @@ -131,7 +144,7 @@ void completeTransferProcess_WhenTransferFound() { @Test void completeTransferProcess_WhenTransferNotFound() { // Act - sut.completeTransferProcess(job.getJobId(), process1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); // Assert assertThat(job.getTransferProcessIds()).isEmpty(); @@ -141,12 +154,12 @@ void completeTransferProcess_WhenTransferNotFound() { void completeTransferProcess_WhenTransferAlreadyCompleted() { // Arrange sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.completeTransferProcess(job.getJobId(), process1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); // Act assertThatExceptionOfType(IllegalStateException.class).isThrownBy( - () -> sut.completeTransferProcess(job.getJobId(), process1)); + () -> sut.completeTransferProcess(job.getJob().getJobId().toString(), process1)); // Assert refreshJob(); @@ -157,31 +170,31 @@ void completeTransferProcess_WhenTransferAlreadyCompleted() { void completeTransferProcess_WhenNotLastTransfer_DoesNotTransitionJob() { // Arrange sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.addTransferProcess(job.getJobId(), processId2); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId2); // Act - sut.completeTransferProcess(job.getJobId(), process1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); // Assert refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.IN_PROGRESS); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.RUNNING); } @Test void completeTransferProcess_WhenLastTransfer_TransitionsJob() { // Arrange sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.addTransferProcess(job.getJobId(), processId2); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId2); // Act - sut.completeTransferProcess(job.getJobId(), process1); - sut.completeTransferProcess(job.getJobId(), process2); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process2); // Assert refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.TRANSFERS_FINISHED); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.TRANSFERS_FINISHED); } @Test @@ -192,7 +205,7 @@ void completeJob_WhenJobNotFound() { sut.completeJob(otherJobId); refreshJob(); // Assert - assertThat(job.getState()).isEqualTo(JobState.INITIAL); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.INITIAL); } @Test @@ -201,38 +214,40 @@ void completeJob_WhenJobInInitialState() { sut.create(job); sut.create(job2); // Act - sut.completeJob(job.getJobId()); + sut.completeJob(job.getJob().getJobId().toString()); // Assert refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.COMPLETED); - assertThat(job.getCompletionDate()).isPresent(); - assertThat(job2.getState()).isEqualTo(JobState.UNSAVED); + refreshJob2(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.COMPLETED); + assertThat(Optional.of(job.getJob().getJobCompleted())).isPresent(); + assertThat(job2.getJob().getJobState()).isEqualTo(JobState.INITIAL); } @Test void completeJob_WhenJobInTransfersCompletedState() { // Arrange sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.completeTransferProcess(job.getJobId(), process1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); // Act - sut.completeJob(job.getJobId()); + sut.completeJob(job.getJob().getJobId().toString()); // Assert refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.COMPLETED); - assertThat(job.getCompletionDate()).isPresent(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.COMPLETED); + assertThat(Optional.of(job.getJob().getJobCompleted())).isPresent(); } @Test void completeJob_WhenJobInTransfersInProgressState() { // Arrange sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); // Act - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> sut.completeJob(job.getJobId())); + assertThatExceptionOfType(IllegalStateException.class).isThrownBy( + () -> sut.completeJob(job.getJob().getJobId().toString())); // Assert refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.IN_PROGRESS); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.RUNNING); } @Test @@ -243,7 +258,7 @@ void markJobInError_WhenJobNotFound() { sut.markJobInError(otherJobId, errorDetail); // Assert refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.INITIAL); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.INITIAL); } @Test @@ -252,78 +267,83 @@ void markJobInError_WhenJobInInitialState() { sut.create(job); sut.create(job2); // Act - sut.markJobInError(job.getJobId(), errorDetail); + sut.markJobInError(job.getJob().getJobId().toString(), errorDetail); // Assert refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.ERROR); - assertThat(job2.getState()).isEqualTo(JobState.UNSAVED); - assertThat(job.getErrorDetail()).isEqualTo(errorDetail); - assertThat(job.getCompletionDate()).isPresent(); + refreshJob2(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.ERROR); + assertThat(job2.getJob().getJobState()).isEqualTo(JobState.INITIAL); + assertThat(job.getJob().getException().getErrorDetail()).isEqualTo(errorDetail); + assertThat(Optional.of(job.getJob().getJobCompleted())).isPresent(); } @Test void markJobInError_WhenJobInTransfersCompletedState() { // Arrange sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.completeTransferProcess(job.getJobId(), process1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); // Act - sut.markJobInError(job.getJobId(), errorDetail); + sut.markJobInError(job.getJob().getJobId().toString(), errorDetail); // Assert refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.ERROR); - assertThat(job.getCompletionDate()).isPresent(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.ERROR); + assertThat(Optional.of(job.getJob().getJobCompleted())).isPresent(); } @Test void markJobInError_WhenJobInTransfersInProgressState() { // Arrange sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); // Act - sut.markJobInError(job.getJobId(), errorDetail); + sut.markJobInError(job.getJob().getJobId().toString(), errorDetail); // Assert refreshJob(); - assertThat(job.getState()).isEqualTo(JobState.ERROR); - assertThat(job.getCompletionDate()).isPresent(); + assertThat(job.getJob().getJobState()).isEqualTo(JobState.ERROR); + assertThat(Optional.of(job.getJob().getJobCompleted())).isPresent(); } @Test void shouldFindCompletedJobsOlderThanFiveHours() { // Arrange - final LocalDateTime nowPlusFiveHours = LocalDateTime.now().plusHours(5); + final Instant nowPlusFiveHours = Instant.now().plusSeconds(TTL_IN_HOUR_SECONDS * 5); sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.completeTransferProcess(job.getJobId(), process1); - sut.completeJob(job.getJobId()); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.completeTransferProcess(job.getJob().getJobId().toString(), process1); + sut.completeJob(job.getJob().getJobId().toString()); // Act final List completedJobs = sut.findByStateAndCompletionDateOlderThan(JobState.COMPLETED, nowPlusFiveHours); // Assert assertThat(completedJobs).hasSize(1); - assertThat(completedJobs.get(0).getState()).isEqualTo(JobState.COMPLETED); - assertThat(completedJobs.get(0).getCompletionDate()).isPresent(); + assertThat(completedJobs.get(0).getJob().getJobState()).isEqualTo(JobState.COMPLETED); + assertThat(Optional.of(completedJobs.get(0).getJob().getJobCompleted())).isPresent(); } @Test void shouldFindFailedJobsOlderThanFiveHours() { // Arrange - final LocalDateTime nowPlusFiveHours = LocalDateTime.now().plusHours(5); + final Instant nowPlusFiveHours = Instant.now().plusSeconds(3600 * 5); sut.create(job); - sut.addTransferProcess(job.getJobId(), processId1); - sut.markJobInError(job.getJobId(), errorDetail); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + sut.markJobInError(job.getJob().getJobId().toString(), errorDetail); // Act final List failedJobs = sut.findByStateAndCompletionDateOlderThan(JobState.ERROR, nowPlusFiveHours); // Assert assertThat(failedJobs).isNotEmpty(); final Optional foundJob = failedJobs.stream() - .filter(failedJob -> failedJob.getJobId() - .equals(job.getJobId())) + .filter(failedJob -> failedJob.getJob() + .getJobId() + .toString() + .equals(job.getJob() + .getJobId() + .toString())) .findFirst(); assertThat(foundJob).isPresent(); - assertThat(foundJob.get().getState()).isEqualTo(JobState.ERROR); - assertThat(foundJob.get().getCompletionDate()).isPresent(); + assertThat(foundJob.get().getJob().getJobState()).isEqualTo(JobState.ERROR); + assertThat(Optional.of(foundJob.get().getJob().getJobCompleted())).isPresent(); } @Test @@ -331,9 +351,9 @@ void shouldDeleteJobById() { // Arrange sut.create(job); // Act - sut.deleteJob(job.getJobId()); + sut.deleteJob(job.getJob().getJobId().toString()); // Assert - assertThat(sut.find(job.getJobId())).isEmpty(); + assertThat(sut.find(job.getJob().getJobId().toString())).isEmpty(); } @Test @@ -345,7 +365,6 @@ void shouldThrowExceptionWhenDeleteJobByIllegalId() { assertThatExceptionOfType(JobException.class).isThrownBy(() -> sut.deleteJob(illegalId)); } - @Test void shouldThrowExceptionWhenFindJobByIllegalId() { // Arrange @@ -358,11 +377,10 @@ void shouldThrowExceptionWhenFindJobByIllegalId() { assertThat(job).isEmpty(); } - @Test void shouldStoreAndLoadJob() { // arrange - final var jobId = faker.lorem().characters(36); + final var jobId = UUID.randomUUID().toString(); final MultiTransferJob job = createJob(jobId); // act @@ -374,10 +392,11 @@ void shouldStoreAndLoadJob() { final MultiTransferJob storedJob = multiTransferJob.get(); SoftAssertions.assertSoftly(softly -> { - softly.assertThat(storedJob.getJobId()).isEqualTo(job.getJobId()); - softly.assertThat(storedJob.getState()).isEqualTo(JobState.INITIAL); - softly.assertThat(storedJob.getErrorDetail()).isEqualTo(job.getErrorDetail()); - softly.assertThat(storedJob.getCompletionDate()).isEqualTo(job.getCompletionDate()); + softly.assertThat(storedJob.getJob().getJobId().toString()).isEqualTo(job.getJob().getJobId().toString()); + softly.assertThat(storedJob.getJob().getJobState()).isEqualTo(JobState.INITIAL); + softly.assertThat(storedJob.getJob().getException().getErrorDetail()) + .isEqualTo(job.getJob().getException().getErrorDetail()); + softly.assertThat(storedJob.getJob().getJobCompleted()).isEqualTo(job.getJob().getJobCompleted()); softly.assertThat(storedJob.getJobData()).isEqualTo(job.getJobData()); softly.assertThat(storedJob.getCompletedTransfers()).isEqualTo(job.getCompletedTransfers()); }); @@ -386,18 +405,23 @@ void shouldStoreAndLoadJob() { private MultiTransferJob createJob(final String jobId) { return MultiTransferJob.builder() - .jobId(jobId) + .job(Job.builder() + .jobId(UUID.fromString(jobId)) + .jobState(JobState.UNSAVED) + .jobCompleted(Instant.now()) + .exception(JobErrorDetails.builder() + .exception("SomeError") + .exceptionDate(Instant.now()) + .build()) + .build()) .jobData(Map.of("dataKey", "dataValue")) - .state(JobState.UNSAVED) - .errorDetail("SomeError") - .completionDate(Optional.empty()) .build(); } @Test void shouldTransitionJobToComplete() { // arrange - final var jobId = faker.lorem().characters(36); + final var jobId = UUID.randomUUID().toString(); final MultiTransferJob job = createJob(jobId); // act @@ -405,34 +429,47 @@ void shouldTransitionJobToComplete() { sut.completeJob(jobId); final Optional multiTransferJob = sut.find(jobId); - // assert + // assertec assertThat(multiTransferJob).isPresent(); final MultiTransferJob storedJob = multiTransferJob.get(); SoftAssertions.assertSoftly(softly -> { - softly.assertThat(storedJob.getJobId()).isEqualTo(job.getJobId()); - softly.assertThat(storedJob.getState()).isEqualTo(JobState.COMPLETED); - softly.assertThat(storedJob.getErrorDetail()).isEqualTo(job.getErrorDetail()); - softly.assertThat(storedJob.getCompletionDate()).isPresent(); + softly.assertThat(storedJob.getJob().getJobId().toString()).isEqualTo(job.getJob().getJobId().toString()); + softly.assertThat(storedJob.getJob().getJobState()).isEqualTo(JobState.COMPLETED); + softly.assertThat(storedJob.getJob().getException().getErrorDetail()) + .isEqualTo(job.getJob().getException().getErrorDetail()); softly.assertThat(storedJob.getJobData()).isEqualTo(job.getJobData()); softly.assertThat(storedJob.getCompletedTransfers()).isEqualTo(job.getCompletedTransfers()); }); } private void refreshJob() { - job = sut.find(job.getJobId()).get(); + job = sut.find(job.getJob().getJobId().toString()).get(); + } + + private void refreshJob2() { + job2 = sut.find(job2.getJob().getJobId().toString()).get(); } @Test void shouldThrowExceptionWhenCreatingJob() throws BlobPersistenceException { // Arrange final var ex = new BlobPersistenceException("test", new RuntimeException()); - doThrow(ex).when(blobStoreSpy).putBlob(anyString(), any()); + doThrow(ex).when(blobStoreSpy).putBlob(any(), any()); // Act sut.create(job); // Assert - assertThat(sut.find(job.getJobId())).isEmpty(); + assertThat(sut.find(job.getJob().getJobId().toString())).isEmpty(); + } + + @Test + void jobStateIsInProgress() { + sut.create(job); + sut.addTransferProcess(job.getJob().getJobId().toString(), processId1); + final Optional multiTransferJob = sut.get(job.getJob().getJobId().toString()); + assertThat(multiTransferJob).isPresent(); + assertThat(multiTransferJob.get().getJob().getJobState()).isEqualTo(JobState.RUNNING); } } \ No newline at end of file diff --git a/irs-api/src/test/java/net/catenax/irs/services/IrsItemGraphQueryServiceTest.java b/irs-api/src/test/java/net/catenax/irs/services/IrsItemGraphQueryServiceTest.java index 11a33846be..a237bf07d0 100644 --- a/irs-api/src/test/java/net/catenax/irs/services/IrsItemGraphQueryServiceTest.java +++ b/irs-api/src/test/java/net/catenax/irs/services/IrsItemGraphQueryServiceTest.java @@ -4,7 +4,6 @@ import static net.catenax.irs.util.TestMother.registerJobWithoutDepth; import static org.awaitility.Awaitility.await; import static org.awaitility.Awaitility.given; -//import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -12,13 +11,16 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Duration; +import java.time.Instant; import java.util.UUID; import java.util.concurrent.TimeUnit; import net.catenax.irs.TestConfig; import net.catenax.irs.component.JobHandle; import net.catenax.irs.component.RegisterJob; -import net.catenax.irs.connector.job.JobState; +import net.catenax.irs.component.Job; +import net.catenax.irs.component.JobErrorDetails; +import net.catenax.irs.component.enums.JobState; import net.catenax.irs.connector.job.JobStore; import net.catenax.irs.connector.job.MultiTransferJob; import net.catenax.irs.exceptions.EntityNotFoundException; @@ -90,9 +92,16 @@ void getJobsByProcessingState() { void cancelJobById() { final String idAsString = String.valueOf(jobId); final MultiTransferJob multiTransferJob = MultiTransferJob.builder() - .jobId(idAsString) - .state(JobState.UNSAVED) - .errorDetail("Job should be canceled") + .job(Job.builder() + .jobId(UUID.fromString(idAsString)) + .jobState(JobState.UNSAVED) + .exception(JobErrorDetails.builder() + .errorDetail( + "Job should be canceled") + .exceptionDate( + Instant.now()) + .build()) + .build()) .build(); jobStore.create(multiTransferJob); @@ -100,7 +109,7 @@ void cancelJobById() { assertNotNull(service.cancelJobById(jobId)); assertFalse(jobStore.find(idAsString).isEmpty()); - final JobState state = jobStore.find(idAsString).get().getState(); + final JobState state = jobStore.find(idAsString).get().getJob().getJobState(); assertEquals(state, JobState.CANCELED); } diff --git a/irs-api/src/test/java/net/catenax/irs/util/TestMother.java b/irs-api/src/test/java/net/catenax/irs/util/TestMother.java index 84fc6fa234..699f49e12d 100644 --- a/irs-api/src/test/java/net/catenax/irs/util/TestMother.java +++ b/irs-api/src/test/java/net/catenax/irs/util/TestMother.java @@ -1,5 +1,9 @@ package net.catenax.irs.util; +import static net.catenax.irs.dtos.IrsCommonConstants.ROOT_ITEM_ID_KEY; + +import java.net.URL; +import java.time.Instant; import java.util.Map; import java.util.UUID; import java.util.stream.IntStream; @@ -8,11 +12,13 @@ import com.github.javafaker.Faker; import net.catenax.irs.component.RegisterJob; import net.catenax.irs.connector.job.DataRequest; -import net.catenax.irs.connector.job.JobState; import net.catenax.irs.connector.job.MultiTransferJob; import net.catenax.irs.connector.job.ResponseStatus; import net.catenax.irs.connector.job.TransferInitiateResponse; import net.catenax.irs.connector.job.TransferProcess; +import net.catenax.irs.component.GlobalAssetIdentification; +import net.catenax.irs.component.Job; +import net.catenax.irs.component.enums.JobState; /** * Base object mother class to create objects for testing. @@ -24,21 +30,29 @@ public class TestMother { Faker faker = new Faker(); + public Job fakeJob(JobState state) { + return Job.builder() + .jobId(UUID.randomUUID()) + .globalAssetId( + GlobalAssetIdentification.builder().globalAssetId(UUID.randomUUID().toString()).build()) + .jobState(state) + .createdOn(Instant.now()) + .owner(faker.lorem().characters()) + .lastModifiedOn(Instant.now()) + .requestUrl(fakeURL()) + .build(); + } + public MultiTransferJob job() { return job(faker.options().option(JobState.class)); } public MultiTransferJob job(JobState jobState) { return MultiTransferJob.builder() - .jobId(faker.lorem().characters(36)) - .jobData(Map.of( - faker.lorem().characters(), - faker.lorem().characters(), - faker.lorem().characters(), - faker.lorem().characters() - )) - .state(jobState) - .build(); + .job(fakeJob(jobState)) + .jobData(Map.of(ROOT_ITEM_ID_KEY, faker.lorem().characters(), + faker.lorem().characters(), faker.lorem().characters())) + .build(); } public DataRequest dataRequest() { @@ -66,6 +80,14 @@ public Stream dataRequests(int count) { return IntStream.range(0, count).mapToObj(i -> dataRequest()); } + private URL fakeURL() { + try { + return new URL("http://localhost:8888/fake/url"); + } catch (Exception e) { + return null; + } + } + public static RegisterJob registerJobWithoutDepth() { return registerJobWithDepth(null); } diff --git a/irs-common/pom.xml b/irs-common/pom.xml index 3cd209a782..4e0f82ce86 100644 --- a/irs-common/pom.xml +++ b/irs-common/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 diff --git a/irs-common/src/main/java/net/catenax/irs/dtos/IrsCommonConstants.java b/irs-common/src/main/java/net/catenax/irs/dtos/IrsCommonConstants.java new file mode 100644 index 0000000000..ef88cf351b --- /dev/null +++ b/irs-common/src/main/java/net/catenax/irs/dtos/IrsCommonConstants.java @@ -0,0 +1,29 @@ +// +// Copyright (c) 2021 Copyright Holder (Catena-X Consortium) +// +// See the AUTHORS file(s) distributed with this work for additional +// information regarding authorship. +// +// See the LICENSE file(s) distributed with this work for +// additional information regarding license terms. +// +package net.catenax.irs.dtos; + +import net.catenax.irs.annotations.ExcludeFromCodeCoverageGeneratedReport; + +/** + * Common constant used in IRS + */ +@ExcludeFromCodeCoverageGeneratedReport +public class IrsCommonConstants { + /** + * Job Data key for root item ID + */ + public static final String ROOT_ITEM_ID_KEY = "root.item.id.key"; + + /** + * Expected depth of the tree + */ + public static final String DEPTH_ID_KEY = "depth.id.key"; + +} diff --git a/irs-models/pom.xml b/irs-models/pom.xml index d7e4d1c7f4..f08db14161 100644 --- a/irs-models/pom.xml +++ b/irs-models/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 diff --git a/irs-models/src/main/java/net/catenax/irs/connector/annotations/ExcludeFromCodeCoverageGeneratedReport.java b/irs-models/src/main/java/net/catenax/irs/annotations/ExcludeFromCodeCoverageGeneratedReport.java similarity index 88% rename from irs-models/src/main/java/net/catenax/irs/connector/annotations/ExcludeFromCodeCoverageGeneratedReport.java rename to irs-models/src/main/java/net/catenax/irs/annotations/ExcludeFromCodeCoverageGeneratedReport.java index 9c4b99b0a7..b053e1d91a 100644 --- a/irs-models/src/main/java/net/catenax/irs/connector/annotations/ExcludeFromCodeCoverageGeneratedReport.java +++ b/irs-models/src/main/java/net/catenax/irs/annotations/ExcludeFromCodeCoverageGeneratedReport.java @@ -8,7 +8,7 @@ // additional information regarding license terms. // -package net.catenax.irs.connector.annotations; +package net.catenax.irs.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -22,6 +22,6 @@ * the string "Generated" in its name. */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD, ElementType.TYPE}) +@Target({ ElementType.METHOD, ElementType.TYPE }) public @interface ExcludeFromCodeCoverageGeneratedReport { } diff --git a/irs-models/src/main/java/net/catenax/irs/annotations/ValueOfEnum.java b/irs-models/src/main/java/net/catenax/irs/annotations/ValueOfEnum.java index 0d68763b1a..61b0c8f722 100644 --- a/irs-models/src/main/java/net/catenax/irs/annotations/ValueOfEnum.java +++ b/irs-models/src/main/java/net/catenax/irs/annotations/ValueOfEnum.java @@ -9,22 +9,22 @@ // package net.catenax.irs.annotations; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; -import net.catenax.irs.validators.ValueOfEnumValidator; - -import javax.validation.Constraint; -import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import javax.validation.Constraint; +import javax.validation.Payload; + +import net.catenax.irs.validators.ValueOfEnumValidator; /** * Custom annotation to validate input for enum. */ -@Target({FIELD}) +@Target({ FIELD }) @Retention(RUNTIME) @Documented @Constraint(validatedBy = ValueOfEnumValidator.class) @@ -32,7 +32,10 @@ @ExcludeFromCodeCoverageGeneratedReport public @interface ValueOfEnum { Class> enumClass(); + String message() default "must be any of enum {enumClass}"; - Class[] groups() default {}; - Class[] payload() default {}; + + Class[] groups() default { }; + + Class[] payload() default { }; } diff --git a/irs-models/src/main/java/net/catenax/irs/component/AsyncFetchedItems.java b/irs-models/src/main/java/net/catenax/irs/component/AsyncFetchedItems.java index 5be0520577..30527a36af 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/AsyncFetchedItems.java +++ b/irs-models/src/main/java/net/catenax/irs/component/AsyncFetchedItems.java @@ -42,7 +42,7 @@ public class AsyncFetchedItems { * User to build async fetched items */ @Schema(description = "User to build async fetched items") - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class AsyncFetchedItemsBuilder { } } diff --git a/irs-models/src/main/java/net/catenax/irs/component/ChildItem.java b/irs-models/src/main/java/net/catenax/irs/component/ChildItem.java index 2a022eebfb..5a8abf93ec 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/ChildItem.java +++ b/irs-models/src/main/java/net/catenax/irs/component/ChildItem.java @@ -49,7 +49,7 @@ public class ChildItem { * Builder for ChildItem class */ @Schema(description = "Builder to to build child items") - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class ChildItemBuilder { } } diff --git a/irs-models/src/main/java/net/catenax/irs/component/Description.java b/irs-models/src/main/java/net/catenax/irs/component/Description.java index e168cec1eb..a0607cb665 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/Description.java +++ b/irs-models/src/main/java/net/catenax/irs/component/Description.java @@ -38,7 +38,7 @@ public class Description { /** * Builder for Description class */ - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class DescriptionBuilder { } } diff --git a/irs-models/src/main/java/net/catenax/irs/component/FilteredSubmodel.java b/irs-models/src/main/java/net/catenax/irs/component/FilteredSubmodel.java index 66091f63cc..d2c7f708c0 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/FilteredSubmodel.java +++ b/irs-models/src/main/java/net/catenax/irs/component/FilteredSubmodel.java @@ -35,7 +35,7 @@ public class FilteredSubmodel { /** * Builder for FilteredSubmodel class */ - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class FilteredSubmodelBuilder { } } diff --git a/irs-models/src/main/java/net/catenax/irs/component/GenericDescription.java b/irs-models/src/main/java/net/catenax/irs/component/GenericDescription.java index 224f80d1ca..897c5cd2f0 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/GenericDescription.java +++ b/irs-models/src/main/java/net/catenax/irs/component/GenericDescription.java @@ -39,6 +39,4 @@ public class GenericDescription { @Schema(description = "Description") private List descriptions; - - } diff --git a/irs-models/src/main/java/net/catenax/irs/component/GlobalAssetIdentification.java b/irs-models/src/main/java/net/catenax/irs/component/GlobalAssetIdentification.java index fd0d3df707..cf1a8647bc 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/GlobalAssetIdentification.java +++ b/irs-models/src/main/java/net/catenax/irs/component/GlobalAssetIdentification.java @@ -24,31 +24,31 @@ * Global unique identifier for asset */ - @Schema(description = "Represents a CatenaX id in the format urn:uuid:.") @Value -@Builder +@Builder(toBuilder = true) @JsonSerialize(using = ToStringSerializer.class) -@JsonDeserialize(builder = GlobalAssetIdentification.GlobalAssetIdBuilder.class) +@JsonDeserialize(builder = GlobalAssetIdentification.GlobalAssetIdentificationBuilder.class) @ExcludeFromCodeCoverageGeneratedReport public class GlobalAssetIdentification { private static final int GLOBAL_ASSET_ID_LENGTH = 45; - @Valid - @Schema(description = "Global unique C-X identifier.", example = "urn:uuid:6c311d29-5753-46d4-b32c-19b918ea93b0", minLength = GLOBAL_ASSET_ID_LENGTH, maxLength = GLOBAL_ASSET_ID_LENGTH) + @Schema(description = "Global unique C-X identifier.", example = "urn:uuid:6c311d29-5753-46d4-b32c-19b918ea93b0", + minLength = GLOBAL_ASSET_ID_LENGTH, maxLength = GLOBAL_ASSET_ID_LENGTH) private String globalAssetId; + @Override + public String toString() { + return globalAssetId; + } + /** * Builder for GlobalAssetIdBuilder class */ - @JsonPOJOBuilder(withPrefix = "with") - public static class GlobalAssetIdBuilder { + @JsonPOJOBuilder(withPrefix = "") + public static class GlobalAssetIdentificationBuilder { } - @Override - public String toString() { - return globalAssetId; - } } diff --git a/irs-models/src/main/java/net/catenax/irs/component/IrsPartRelationshipsWithInfos.java b/irs-models/src/main/java/net/catenax/irs/component/IrsPartRelationshipsWithInfos.java index ed02b4f34a..fad47cbe78 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/IrsPartRelationshipsWithInfos.java +++ b/irs-models/src/main/java/net/catenax/irs/component/IrsPartRelationshipsWithInfos.java @@ -48,7 +48,7 @@ public class IrsPartRelationshipsWithInfos { /** * Builder class */ - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class IrsPartRelationshipsWithInfosBuilder { } diff --git a/irs-models/src/main/java/net/catenax/irs/component/Job.java b/irs-models/src/main/java/net/catenax/irs/component/Job.java index 3682b26766..8e72e91c4d 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/Job.java +++ b/irs-models/src/main/java/net/catenax/irs/component/Job.java @@ -20,6 +20,7 @@ import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import com.fasterxml.jackson.annotation.JsonUnwrapped; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import io.swagger.v3.oas.annotations.media.Schema; @@ -47,7 +48,7 @@ public class Job { @Size(min = INPUT_FIELD_MIN_LENGTH, max = JOB_ID_FIELD_MAX_LENGTH) @Schema(description = "Job ID for the requested item.", minLength = INPUT_FIELD_MIN_LENGTH, maxLength = JOB_ID_FIELD_MAX_LENGTH, implementation = UUID.class) - private final UUID jobId; + private UUID jobId; /** * globalAssetId @@ -56,14 +57,15 @@ public class Job { @Size(min = INPUT_FIELD_MIN_LENGTH, max = JOB_ID_FIELD_MAX_LENGTH) @Schema(description = "Part global unique Id", minLength = INPUT_FIELD_MIN_LENGTH, maxLength = JOB_ID_FIELD_MAX_LENGTH, implementation = GlobalAssetIdentification.class) - private final GlobalAssetIdentification globalAssetId; + @JsonUnwrapped + private GlobalAssetIdentification globalAssetId; @NotBlank @Schema() private JobState jobState; - @Schema(description = "Exception state for this job.", implementation = JobException.class) - private JobException exception; + @Schema(description = "Exception state for this job.", implementation = JobErrorDetails.class) + private JobErrorDetails exception; /** * Timestamp when the job was created @@ -117,7 +119,7 @@ public class Job { /** * Builder class */ - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class JobBuilder { } diff --git a/irs-models/src/main/java/net/catenax/irs/component/JobErrorDetails.java b/irs-models/src/main/java/net/catenax/irs/component/JobErrorDetails.java new file mode 100644 index 0000000000..02d44473ca --- /dev/null +++ b/irs-models/src/main/java/net/catenax/irs/component/JobErrorDetails.java @@ -0,0 +1,98 @@ +// +// Copyright (c) 2021 Copyright Holder (Catena-X Consortium) +// +// See the AUTHORS file(s) distributed with this work for additional +// information regarding authorship. +// +// See the LICENSE file(s) distributed with this work for +// additional information regarding license terms. +// +package net.catenax.irs.component; + +import java.time.Instant; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import net.catenax.irs.annotations.ExcludeFromCodeCoverageGeneratedReport; + +/** + * Exception container for job + */ +@Getter +@Setter +@Builder(toBuilder = true) +@JsonDeserialize(builder = JobErrorDetails.JobErrorDetailsBuilder.class) +@NoArgsConstructor +@AllArgsConstructor +@SuppressWarnings({ "PMD.ShortClassName", "PMD.MethodArgumentCouldBeFinal" }) +@ExcludeFromCodeCoverageGeneratedReport +public class JobErrorDetails { + + public static final int EXCEPTION_NAME_MAX_LENGTH = 100; + public static final int ERROR_DETAIL_MAX_LENGTH = 4000; + + @Schema(description = "Name of the exception occurred.", implementation = String.class, + maxLength = EXCEPTION_NAME_MAX_LENGTH) + private String exception; + + @Schema(description = "Detail information for the error occurred.", implementation = String.class, + maxLength = ERROR_DETAIL_MAX_LENGTH) + private String errorDetail; + + @Schema(description = "Datetime when error occurred.", implementation = Instant.class) + private Instant exceptionDate; + + @Override + public String toString() { + return "JobErrorDetails{" + "exception='" + exception + '\'' + + ", errorDetail='" + errorDetail + '\'' + + ", exceptionDate=" + exceptionDate + + '}'; + } + + /** + * Builder class + */ + @JsonPOJOBuilder(withPrefix = "") + public static final class JobErrorDetailsBuilder { + + private final JobErrorDetails errorDetails; + + private JobErrorDetailsBuilder() { + errorDetails = new JobErrorDetails(); + } + + @JsonCreator + public static JobErrorDetailsBuilder instance() { + return new JobErrorDetailsBuilder(); + } + + public JobErrorDetailsBuilder exception(String exception) { + errorDetails.exception = exception; + return this; + } + + public JobErrorDetailsBuilder errorDetail(String errorDetail) { + errorDetails.errorDetail = errorDetail; + return this; + } + + public JobErrorDetailsBuilder exceptionDate(Instant exceptionDate) { + errorDetails.exceptionDate = exceptionDate; + return this; + } + + public JobErrorDetails build() { + return this.errorDetails; + } + + } + +} diff --git a/irs-models/src/main/java/net/catenax/irs/component/JobException.java b/irs-models/src/main/java/net/catenax/irs/component/JobException.java deleted file mode 100644 index 91ba6bda19..0000000000 --- a/irs-models/src/main/java/net/catenax/irs/component/JobException.java +++ /dev/null @@ -1,52 +0,0 @@ -// -// Copyright (c) 2021 Copyright Holder (Catena-X Consortium) -// -// See the AUTHORS file(s) distributed with this work for additional -// information regarding authorship. -// -// See the LICENSE file(s) distributed with this work for -// additional information regarding license terms. -// -package net.catenax.irs.component; - -import java.time.Instant; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Value; -import net.catenax.irs.annotations.ExcludeFromCodeCoverageGeneratedReport; - -/** - * Exception container for job - */ -@Value -@Builder(toBuilder = true) -@JsonDeserialize(builder = JobException.JobExceptionBuilder.class) -@AllArgsConstructor -@SuppressWarnings("PMD.ShortClassName") -@ExcludeFromCodeCoverageGeneratedReport -public class JobException { - - public static final int EXCEPTION_NAME_MAX_LENGTH = 100; - public static final int ERROR_DETAIL_MAX_LENGTH = 4000; - - @Schema(description = "Name of the exception occurred.", implementation = String.class, maxLength = EXCEPTION_NAME_MAX_LENGTH) - private String exception; - - @Schema(description = "Detail information for the error occurred.", implementation = String.class, maxLength = ERROR_DETAIL_MAX_LENGTH) - private String errorDetail; - - @Schema(description = "Datetime when error occurred.", implementation = Instant.class) - private Instant exceptionDate; - - /** - * Builder class - */ - @JsonPOJOBuilder(withPrefix = "with") - public static class JobExceptionBuilder { - } - -} diff --git a/irs-models/src/main/java/net/catenax/irs/component/Jobs.java b/irs-models/src/main/java/net/catenax/irs/component/Jobs.java index 543d972dfc..551ba3e21c 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/Jobs.java +++ b/irs-models/src/main/java/net/catenax/irs/component/Jobs.java @@ -45,7 +45,7 @@ public class Jobs { /** * Builder class */ - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class JobsBuilder { } diff --git a/irs-models/src/main/java/net/catenax/irs/component/QueryParameter.java b/irs-models/src/main/java/net/catenax/irs/component/QueryParameter.java index 224073daff..033b4ab68b 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/QueryParameter.java +++ b/irs-models/src/main/java/net/catenax/irs/component/QueryParameter.java @@ -52,7 +52,7 @@ public class QueryParameter { * Builder for QueryParameter class */ @Schema(description = "Builder to to build query parameters") - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class QueryParameterBuilder { } } diff --git a/irs-models/src/main/java/net/catenax/irs/component/SemanticId.java b/irs-models/src/main/java/net/catenax/irs/component/SemanticId.java index 3d20ae0002..43d08d9c33 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/SemanticId.java +++ b/irs-models/src/main/java/net/catenax/irs/component/SemanticId.java @@ -38,7 +38,7 @@ public class SemanticId { * User to build SemanticId */ @Schema(description = "User to build async fetched items") - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class SemanticIdBuilder { } } diff --git a/irs-models/src/main/java/net/catenax/irs/component/Shell.java b/irs-models/src/main/java/net/catenax/irs/component/Shell.java index 00137f30b9..0629523320 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/Shell.java +++ b/irs-models/src/main/java/net/catenax/irs/component/Shell.java @@ -58,7 +58,7 @@ public class Shell { * User to build Shell */ @Schema(description = "User to build shell items") - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class ShellBuilder { } } diff --git a/irs-models/src/main/java/net/catenax/irs/component/SubmodelDescriptor.java b/irs-models/src/main/java/net/catenax/irs/component/SubmodelDescriptor.java index c00552b477..e37d8506f6 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/SubmodelDescriptor.java +++ b/irs-models/src/main/java/net/catenax/irs/component/SubmodelDescriptor.java @@ -51,7 +51,7 @@ public class SubmodelDescriptor { * User to build SubmodelDescriptor */ @Schema(description = "User to build async fetched items") - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class SubmodelDescriptorBuilder { } } diff --git a/irs-models/src/main/java/net/catenax/irs/component/Summary.java b/irs-models/src/main/java/net/catenax/irs/component/Summary.java index 19594d0506..a429a6a783 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/Summary.java +++ b/irs-models/src/main/java/net/catenax/irs/component/Summary.java @@ -35,7 +35,7 @@ public class Summary { /** * Builder class */ - @JsonPOJOBuilder(withPrefix = "with") + @JsonPOJOBuilder(withPrefix = "") public static class SummaryBuilder { } diff --git a/irs-models/src/main/java/net/catenax/irs/component/enums/Direction.java b/irs-models/src/main/java/net/catenax/irs/component/enums/Direction.java index 59b143233c..d38f2c4e3e 100644 --- a/irs-models/src/main/java/net/catenax/irs/component/enums/Direction.java +++ b/irs-models/src/main/java/net/catenax/irs/component/enums/Direction.java @@ -21,8 +21,8 @@ @Schema(description = "Direction in which the tree shall be traversed.") @ExcludeFromCodeCoverageGeneratedReport public enum Direction { - //@Schema(description = "The tree is traversed in upward direction.") UPWARD("upward"), - @Schema(description = "The tree is traversed in downward direction.") DOWNWARD(DirectionConstants.DOWNWARD); + //@Schema(description = "The tree is traversed in upward direction.") UPWARD("upward"), + @Schema(description = "The tree is traversed in downward direction.") DOWNWARD(DirectionConstants.DOWNWARD); private final String value; diff --git a/irs-models/src/main/java/net/catenax/irs/dtos/ErrorResponse.java b/irs-models/src/main/java/net/catenax/irs/dtos/ErrorResponse.java index d6322eeaba..b4758b8191 100644 --- a/irs-models/src/main/java/net/catenax/irs/dtos/ErrorResponse.java +++ b/irs-models/src/main/java/net/catenax/irs/dtos/ErrorResponse.java @@ -9,6 +9,8 @@ // package net.catenax.irs.dtos; +import java.util.List; + import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.swagger.v3.oas.annotations.media.Schema; @@ -16,8 +18,6 @@ import lombok.Value; import org.springframework.http.HttpStatus; -import java.util.List; - /*** API error response. */ @Schema(description = "Error response.") @Value diff --git a/irs-models/src/main/java/net/catenax/irs/dtos/ItemLifecycleStage.java b/irs-models/src/main/java/net/catenax/irs/dtos/ItemLifecycleStage.java index 5e69f6d9f7..55d76bc693 100644 --- a/irs-models/src/main/java/net/catenax/irs/dtos/ItemLifecycleStage.java +++ b/irs-models/src/main/java/net/catenax/irs/dtos/ItemLifecycleStage.java @@ -22,9 +22,7 @@ @Schema(description = "Stage defining whether changes apply to the AS_BUILT or AS_MAINTAINED BOM views.") @SuppressWarnings("PMD.CommentRequired") public enum ItemLifecycleStage { - @Schema(description = "The time the part is built.") - BUILD, + @Schema(description = "The time the part is built.") BUILD, - @Schema(description = "The time after the part is built.") - MAINTENANCE + @Schema(description = "The time after the part is built.") MAINTENANCE } diff --git a/irs-models/src/main/java/net/catenax/irs/dtos/ItemsTreeView.java b/irs-models/src/main/java/net/catenax/irs/dtos/ItemsTreeView.java index 2212e213ca..22189ac3da 100644 --- a/irs-models/src/main/java/net/catenax/irs/dtos/ItemsTreeView.java +++ b/irs-models/src/main/java/net/catenax/irs/dtos/ItemsTreeView.java @@ -20,9 +20,7 @@ @ExcludeFromCodeCoverageGeneratedReport @Schema(description = "View defining which data of the PartsTree is retrieved.") public enum ItemsTreeView { - @Schema(description = "The view of the PartsTree as the vehicle was assembled.") - AS_BUILT, + @Schema(description = "The view of the PartsTree as the vehicle was assembled.") AS_BUILT, - @Schema(description = "The view of the PartsTree that accounts for all updates during the vehicle lifecycle.") - AS_MAINTAINED + @Schema(description = "The view of the PartsTree that accounts for all updates during the vehicle lifecycle.") AS_MAINTAINED } diff --git a/irs-models/src/main/java/net/catenax/irs/validators/ValueOfEnumValidator.java b/irs-models/src/main/java/net/catenax/irs/validators/ValueOfEnumValidator.java index b96f33731a..d3ad26f2bb 100644 --- a/irs-models/src/main/java/net/catenax/irs/validators/ValueOfEnumValidator.java +++ b/irs-models/src/main/java/net/catenax/irs/validators/ValueOfEnumValidator.java @@ -9,21 +9,22 @@ // package net.catenax.irs.validators; -import net.catenax.irs.annotations.ExcludeFromCodeCoverageGeneratedReport; -import net.catenax.irs.annotations.ValueOfEnum; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import net.catenax.irs.annotations.ExcludeFromCodeCoverageGeneratedReport; +import net.catenax.irs.annotations.ValueOfEnum; + /** * Generic validator for Enum values. * This validator was added so that we can use String data type in place of Enum for API input request object. As spring BindException details are not that user-friendly when mapping an input which is not value of the Enum. */ @ExcludeFromCodeCoverageGeneratedReport -@SuppressWarnings({"PMD.CommentSize", "PMD.BeanMembersShouldSerialize"}) +@SuppressWarnings({ "PMD.CommentSize", "PMD.BeanMembersShouldSerialize" }) public class ValueOfEnumValidator implements ConstraintValidator { /** @@ -34,8 +35,8 @@ public class ValueOfEnumValidator implements ConstraintValidator - + 4.0.0 net.catenax.irs @@ -12,7 +13,6 @@ irs-testing - connector irs-api irs-common irs-models