diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index bd60e21c..3ca0bbe6 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -44,7 +44,7 @@ jobs: servers: '[{ "id": "github", "username": "${{ secrets.DCSA_USER }}", "password": "${{ secrets.DCSA_PACKAGES_PAT }}" }]' - name: maven build - run: mvn -B package --file pom.xml + run: mvn -B package -Dchangelist=-RELEASE # Build and push Docker image diff --git a/.github/workflows/docker_publish_bug_fix_test.yml b/.github/workflows/docker_publish_bug_fix_test.yml new file mode 100644 index 00000000..ec6a07a4 --- /dev/null +++ b/.github/workflows/docker_publish_bug_fix_test.yml @@ -0,0 +1,55 @@ +name: DO NOT APPROVE A PR WIT THIS CI FILE + +on: + push: + branches: + - DDT-1284 + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Set up Java JDK + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: 17 + cache: 'maven' + + - name: maven-settings-xml-action + uses: whelk-io/maven-settings-xml-action@v12 + with: + repositories: '[{ "id": "github", "name": "DCSA Backend repo", "url": "https://maven.pkg.github.com/dcsaorg/DCSA-Core", "releases": { "enabled": "true" }, "snapshots": { "enabled": "true" } }]' + servers: '[{ "id": "github", "username": "${{ secrets.DCSA_USER }}", "password": "${{ secrets.DCSA_PACKAGES_PAT }}" }]' + + - name: maven build + run: mvn -B package -Dchangelist=-RELEASE + + + # Build and push Docker image + # https://github.com/marketplace/actions/docker-build-push-action + - name: Build and push Docker image + uses: mr-smithers-excellent/docker-build-push@v5 + with: + image: dcsa-jit + tags: v1.2.0-beta1-rc2-latest + registry: ghcr.io + githubOrg: dcsaorg + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index a4cc5647..ec2d4083 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -39,19 +39,20 @@ jobs: - name: Fetch Backend Status run: | sleep 10 - status=$(curl -s http://localhost:9090/v1/actuator/health | jq -r '.status') + url=http://localhost:9090/jit/v1/actuator/health + status=$(curl -s "${url}" | jq -r '.status') retries=12 while [[ "$status" != "UP" ]] && [[ $retries -gt 0 ]]; do echo "Status is '$status' - waiting 5 secs ($retries retries left)" sleep 5 retries=$((retries - 1)) - status=$(curl -s http://localhost:9090/v1/actuator/health | jq -r '.status') + status=$(curl -s "${url}" | jq -r '.status') done echo "Final status is '$status'" if [[ "$status" != "UP" ]]; then - curl -v http://localhost:9090/v1/actuator/health || : + curl -v "${url}" || : docker ps || : docker logs dcsa-jit_dcsa-jit_1 || : exit 1 diff --git a/.github/workflows/publishRelease.yml b/.github/workflows/publishRelease.yml index 992f2e14..b176cb3d 100644 --- a/.github/workflows/publishRelease.yml +++ b/.github/workflows/publishRelease.yml @@ -42,7 +42,7 @@ jobs: IFS='-' read -ra PROJ_TAG <<<"${{ steps.get-version.outputs.version }}" - if [[ "${{ steps.get-changelist.outputs.info }}" == "-RELEASE" && "${PROJ_TAG[1]}" == "RELEASE" ]]; + if [[ "${{ steps.get-changelist.outputs.info }}" == "-RELEASE" && "${PROJ_TAG[-1]}" == "RELEASE" ]]; then echo "Version is RELEASE" else diff --git a/DCSA-Information-Model b/DCSA-Information-Model index 53d20a9e..f22422ca 160000 --- a/DCSA-Information-Model +++ b/DCSA-Information-Model @@ -1 +1 @@ -Subproject commit 53d20a9ed8646d3efa7ee02ff1e417e1477f5273 +Subproject commit f22422ca02b8a037207943805fc56d52527ed186 diff --git a/jit-application/src/main/resources/application.yml b/jit-application/src/main/resources/application.yml index bdd11b73..efafd88a 100644 --- a/jit-application/src/main/resources/application.yml +++ b/jit-application/src/main/resources/application.yml @@ -4,6 +4,9 @@ dcsa: version: 1.1.0 webui: baseUrl: "NOT_SPECIFIED" + sync: + pollFrequency: ${SYNC_POLL_FREQUENCY:60000} + maxMessagesPerPoll: ${SYNC_MAX_MESSAGES_PER_POLL:10} email: from: noreply@dcsa.org timezone: "NOT_SPECIFIED" @@ -22,8 +25,8 @@ camel: max-redeliveries: 3 redelivery-delay: 10000 route: - pending-email-notification: "jpa:org.dcsa.jit.persistence.entity.PendingEmailNotification?namedQuery=PendingEmailNotification.nextPendingEmailNotifications&delay=60000&maximumResults=10" - outbox-msg: "jpa:org.dcsa.jit.persistence.entity.OutboxMessage?namedQuery=poll-outbox-messages&delay=60000&transacted=true&maximumResults=10" + pending-email-notification: "jpa:org.dcsa.jit.persistence.entity.PendingEmailNotification?namedQuery=PendingEmailNotification.nextPendingEmailNotifications&delay=${dcsa.sync.pollFrequency}&maximumResults=${dcsa.sync.maxMessagesPerPoll}" + outbox-msg: "jpa:org.dcsa.jit.persistence.entity.OutboxMessage?namedQuery=poll-outbox-messages&delay=${dcsa.sync.pollFrequency}&transacted=true&maximumResults=${dcsa.sync.maxMessagesPerPoll}" emit-message: direct:emit-message oidc: direct:OIDC timestamp-notify: direct:timestamp-notification @@ -75,7 +78,7 @@ spring: server: port: 9090 - servlet.context-path: /v1 + servlet.context-path: /jit/v1 error: include-binding-errors: on_param include-message: always diff --git a/jit-application/src/test/java/org/dcsa/jit/controller/OperationsEventControllerTest.java b/jit-application/src/test/java/org/dcsa/jit/controller/OperationsEventControllerTest.java index 32b3e0e7..088641fc 100644 --- a/jit-application/src/test/java/org/dcsa/jit/controller/OperationsEventControllerTest.java +++ b/jit-application/src/test/java/org/dcsa/jit/controller/OperationsEventControllerTest.java @@ -46,7 +46,6 @@ class OperationsEventControllerTest { @MockBean ServiceRepository serviceRepository; @MockBean OperationsEventRepository operationsEventRepository; @MockBean UnLocationRepository unLocationRepository; - @MockBean UnmappedEventRepository unmappedEventRepository; @MockBean SMDGDelayReasonRepository smdgDelayReasonRepository; @MockBean PendingEmailNotificationRepository pendingEmailNotificationRepository; @MockBean OpsEventTimestampDefinitionRepository opsEventTimestampDefinitionRepository; diff --git a/jit-application/src/test/java/org/dcsa/jit/security/SecurityFlowIT.java b/jit-application/src/test/java/org/dcsa/jit/security/SecurityFlowIT.java index 4666644a..334af7de 100644 --- a/jit-application/src/test/java/org/dcsa/jit/security/SecurityFlowIT.java +++ b/jit-application/src/test/java/org/dcsa/jit/security/SecurityFlowIT.java @@ -52,7 +52,6 @@ class SecurityFlowIT { @MockBean TimestampDefinitionRepository timestampDefinitionRepository; @MockBean TransportCallRepository transportCallRepository; @MockBean UnLocationRepository unLocationRepository; - @MockBean UnmappedEventRepository unmappedEventRepository; @MockBean VesselRepository vesselRepository; @MockBean TimestampInfoRepository timestampInfoRepository; @MockBean SMDGDelayReasonRepository smdgDelayReasonRepository; diff --git a/jit-integration-tests/src/main/java/org/dcsa/jit/itests/config/RestAssuredConfigurator.java b/jit-integration-tests/src/main/java/org/dcsa/jit/itests/config/RestAssuredConfigurator.java index 5f8dfa13..519766f7 100644 --- a/jit-integration-tests/src/main/java/org/dcsa/jit/itests/config/RestAssuredConfigurator.java +++ b/jit-integration-tests/src/main/java/org/dcsa/jit/itests/config/RestAssuredConfigurator.java @@ -7,9 +7,8 @@ public class RestAssuredConfigurator { // API endpoints - public static final String EVENT_SUBSCRIPTIONS = "/v1/event-subscriptions"; - public static final String EVENTS = "/v1/events"; - public static final String TIMESTAMPS = "/v1/timestamps"; + public static final String EVENTS = "/jit/v1/events"; + public static final String TIMESTAMPS = "/jit/v1/timestamps"; public void initialize() { var properties = IntegrationTestsProperties.getInstance(); diff --git a/jit-integration-tests/src/main/java/org/dcsa/jit/itests/v1/HealthCheckIT.java b/jit-integration-tests/src/main/java/org/dcsa/jit/itests/v1/HealthCheckIT.java index f96ec1da..15b216f1 100644 --- a/jit-integration-tests/src/main/java/org/dcsa/jit/itests/v1/HealthCheckIT.java +++ b/jit-integration-tests/src/main/java/org/dcsa/jit/itests/v1/HealthCheckIT.java @@ -18,7 +18,7 @@ public static void initializeRestAssured() { public void testHealth() { given() .contentType("application/json") - .get("/v1/actuator/health") + .get("/jit/v1/actuator/health") .then() .assertThat() .statusCode(200) diff --git a/jit-integration-tests/src/main/java/org/dcsa/jit/itests/v1/OperationsEventIT.java b/jit-integration-tests/src/main/java/org/dcsa/jit/itests/v1/OperationsEventIT.java index 3ffcc710..3b0cdec3 100644 --- a/jit-integration-tests/src/main/java/org/dcsa/jit/itests/v1/OperationsEventIT.java +++ b/jit-integration-tests/src/main/java/org/dcsa/jit/itests/v1/OperationsEventIT.java @@ -31,7 +31,7 @@ public static void initializeRestAssured() { public void testOperationsEventWithoutQueryParameters() { given() .contentType(ContentType.JSON) - .get("/v1/events") + .get("/jit/v1/events") .then() .assertThat() .statusCode(HttpStatus.SC_OK) @@ -52,7 +52,7 @@ public void testOperationsEventWithLimit1() { given() .contentType(ContentType.JSON) .queryParam("limit", 1) - .get("/v1/events") + .get("/jit/v1/events") .then() .assertThat() .statusCode(HttpStatus.SC_OK) @@ -66,7 +66,7 @@ public void testOperationsEventWithLimit2() { given() .contentType(ContentType.JSON) .queryParam("limit", 2) - .get("/v1/events") + .get("/jit/v1/events") .then() .assertThat() .statusCode(HttpStatus.SC_OK) @@ -80,7 +80,7 @@ public void testOperationsEventWithTransportCallIdQueryParameter() { given() .contentType(ContentType.JSON) .queryParam("transportCallID", "TC-REF-08_03-A") - .get("/v1/events") + .get("/jit/v1/events") .then() .assertThat() .statusCode(HttpStatus.SC_OK) @@ -100,7 +100,7 @@ public void testWithUnLocationCodeQueryParameter() { given() .contentType(ContentType.JSON) .queryParam("UNLocationCode", "USNYC") - .get("/v1/events") + .get("/jit/v1/events") .then() .assertThat() .statusCode(HttpStatus.SC_OK) @@ -115,7 +115,7 @@ public void testWithVeselImoNumberQueryParameter() { given() .contentType(ContentType.JSON) .queryParam("vesselIMONumber", "9811000") - .get("/v1/events") + .get("/jit/v1/events") .then() .assertThat() .statusCode(HttpStatus.SC_OK) @@ -130,7 +130,7 @@ public void testWithOperationsEventTypeCodeQueryParameter1() { given() .contentType(ContentType.JSON) .queryParam("operationsEventTypeCode", "ARRI") - .get("/v1/events") + .get("/jit/v1/events") .then() .assertThat() .statusCode(HttpStatus.SC_OK) @@ -144,7 +144,7 @@ public void testWithOperationsEventTypeCodeQueryParameter2() { given() .contentType(ContentType.JSON) .queryParam("operationsEventTypeCode", "DEPA") - .get("/v1/events") + .get("/jit/v1/events") .then() .assertThat() .statusCode(HttpStatus.SC_OK) @@ -158,7 +158,7 @@ public void testWithCarrierVoyageNumberQueryParameter() { given() .contentType(ContentType.JSON) .queryParam("carrierVoyageNumber", "A_carrier_voyage_number") - .get("/v1/events") + .get("/jit/v1/events") .then() .assertThat() .statusCode(HttpStatus.SC_OK) @@ -172,7 +172,7 @@ public void testWithExportVoyageNumberQueryParameter() { given() .contentType(ContentType.JSON) .queryParam("carrierExportVoyageNumber", "TNT1E") - .get("/v1/events") + .get("/jit/v1/events") .then() .assertThat() .statusCode(HttpStatus.SC_OK) @@ -186,7 +186,7 @@ public void testWithCarrierServiceCodeQueryParameter1() { given() .contentType(ContentType.JSON) .queryParam("carrierServiceCode", "TNT1") - .get("/v1/events") + .get("/jit/v1/events") .then() .assertThat() .statusCode(HttpStatus.SC_OK) @@ -200,7 +200,7 @@ public void testWithCarrierServiceCodeQueryParameter2() { given() .contentType(ContentType.JSON) .queryParam("carrierServiceCode", "FE1") - .get("/v1/events") + .get("/jit/v1/events") .then() .assertThat() .statusCode(HttpStatus.SC_OK) diff --git a/jit-notifications/src/main/java/org/dcsa/jit/notifications/TimestampNotificationsHttpService.java b/jit-notifications/src/main/java/org/dcsa/jit/notifications/TimestampNotificationsHttpService.java index 8d1554e3..1654aa03 100644 --- a/jit-notifications/src/main/java/org/dcsa/jit/notifications/TimestampNotificationsHttpService.java +++ b/jit-notifications/src/main/java/org/dcsa/jit/notifications/TimestampNotificationsHttpService.java @@ -100,7 +100,6 @@ void timestampNotification() { .redeliveryDelay("{{camel.redelivery-delay}}") .useExponentialBackOff() .backOffMultiplier(2) - .log("Timestamp request failed!") .process(new DeadTimestampProcessor()) .to("jpa:org.dcsa.jit.persistence.entity.TimestampNotificationDead") .end() @@ -108,23 +107,21 @@ void timestampNotification() { .setHeader("Content-Type", simple("application/json")) .setHeader("Authorization", simple("${body}")) .setBody(simple("${exchangeProperty.outboxMessage.payload}")) - .toD("${exchangeProperty.outboxMessage.messageRoutingRule.apiUrl}"); + .toD("${exchangeProperty.outboxMessage.messageRoutingRule.apiUrl}") + .log("Successfully forwarded a timestamp to ${exchangeProperty.outboxMessage.messageRoutingRule.apiUrl}"); } // inner class as its only required within TimestampRoutingService static class DeadTimestampProcessor implements Processor { @Override - public void process(Exchange exchange) throws Exception { + public void process(Exchange exchange) { OutboxMessage outboxMessage = (OutboxMessage) exchange.getProperty("outboxMessage"); + Exception cause = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class); + log.info("Failed to delivery timestamp: {} -> {} '{}'", outboxMessage, cause.getClass().getName(), cause.getMessage()); exchange .getIn() - .setBody( - TimestampNotificationDead.builder() - .messageRoutingRule(outboxMessage.getMessageRoutingRule()) - .payload(outboxMessage.getPayload()) - .latestDeliveryAttemptedDatetime(OffsetDateTime.now()) - .build()); + .setBody(TimestampNotificationDead.from(outboxMessage)); } } } diff --git a/jit-persistence/src/main/java/org/dcsa/jit/persistence/entity/OutboxMessage.java b/jit-persistence/src/main/java/org/dcsa/jit/persistence/entity/OutboxMessage.java index e91d99e0..68293506 100644 --- a/jit-persistence/src/main/java/org/dcsa/jit/persistence/entity/OutboxMessage.java +++ b/jit-persistence/src/main/java/org/dcsa/jit/persistence/entity/OutboxMessage.java @@ -1,6 +1,7 @@ package org.dcsa.jit.persistence.entity; import lombok.*; +import org.springframework.data.domain.Persistable; import javax.persistence.*; import java.util.UUID; @@ -13,7 +14,7 @@ @Entity @Table(name = "outbox_message") @NamedQuery(name = "poll-outbox-messages", query = "SELECT om FROM OutboxMessage om") -public class OutboxMessage { +public class OutboxMessage implements Persistable { @Id @GeneratedValue @Column(name = "id", nullable = false) @@ -27,4 +28,21 @@ public class OutboxMessage { @Column(name = "payload", nullable = false, columnDefinition = "TEXT") private String payload; // String as payload since persistence module should not import transfer-obj + + @Transient + private boolean isNew; + + public boolean isNew() { + return id == null || isNew; + } + + public static OutboxMessage retry(TimestampNotificationDead dead) { + return OutboxMessage.builder() + .id(dead.getId()) + .messageRoutingRule(dead.getMessageRoutingRule()) + .payload(dead.getPayload()) + .isNew(true) + .build(); + } + } diff --git a/jit-persistence/src/main/java/org/dcsa/jit/persistence/entity/TimestampNotificationDead.java b/jit-persistence/src/main/java/org/dcsa/jit/persistence/entity/TimestampNotificationDead.java index 842b22f9..8c2553a2 100644 --- a/jit-persistence/src/main/java/org/dcsa/jit/persistence/entity/TimestampNotificationDead.java +++ b/jit-persistence/src/main/java/org/dcsa/jit/persistence/entity/TimestampNotificationDead.java @@ -1,6 +1,7 @@ package org.dcsa.jit.persistence.entity; import lombok.*; +import org.springframework.data.domain.Persistable; import javax.persistence.*; import java.time.OffsetDateTime; @@ -13,7 +14,7 @@ @Setter(AccessLevel.PRIVATE) @Entity @Table(name = "timestamp_notification_dead") -public class TimestampNotificationDead { +public class TimestampNotificationDead implements Persistable { @Id @GeneratedValue @@ -30,4 +31,22 @@ public class TimestampNotificationDead { @Column(name = "latest_delivery_attempted_datetime") private OffsetDateTime latestDeliveryAttemptedDatetime; + + @Transient + private boolean isNew; + + public boolean isNew() { + return id == null || isNew; + } + + public static TimestampNotificationDead from(OutboxMessage outboxMessage) { + return TimestampNotificationDead.builder() + // Preserve the ID to make tracking easier across fail-retry cycles. + .id(outboxMessage.getId()) + .messageRoutingRule(outboxMessage.getMessageRoutingRule()) + .payload(outboxMessage.getPayload()) + .latestDeliveryAttemptedDatetime(OffsetDateTime.now()) + .isNew(true) + .build(); + } } diff --git a/jit-persistence/src/main/java/org/dcsa/jit/persistence/entity/UnmappedEvent.java b/jit-persistence/src/main/java/org/dcsa/jit/persistence/entity/UnmappedEvent.java deleted file mode 100644 index 75d2e4c8..00000000 --- a/jit-persistence/src/main/java/org/dcsa/jit/persistence/entity/UnmappedEvent.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.dcsa.jit.persistence.entity; - -import lombok.*; -import org.springframework.data.domain.Persistable; - -import javax.persistence.*; -import java.time.OffsetDateTime; -import java.util.UUID; - -@Builder(toBuilder = true) -@NoArgsConstructor -@AllArgsConstructor -@Setter(AccessLevel.PRIVATE) -@Entity -@Table(name = "unmapped_event_queue") -public class UnmappedEvent implements Persistable { - @Id - @Column(name = "event_id", nullable = false) - private UUID eventID; - - private transient boolean newRecord; - - @Column(name = "enqueued_at_date_time", nullable = false) - private OffsetDateTime enqueuedAtDateTime; - - @Override - public UUID getId() { - return eventID; - } - - @Override - public boolean isNew() { - return eventID == null || newRecord; - } -} diff --git a/jit-persistence/src/main/java/org/dcsa/jit/persistence/repository/UnmappedEventRepository.java b/jit-persistence/src/main/java/org/dcsa/jit/persistence/repository/UnmappedEventRepository.java deleted file mode 100644 index b1f9630e..00000000 --- a/jit-persistence/src/main/java/org/dcsa/jit/persistence/repository/UnmappedEventRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.dcsa.jit.persistence.repository; - -import org.dcsa.jit.persistence.entity.UnmappedEvent; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.UUID; - -public interface UnmappedEventRepository extends JpaRepository {} diff --git a/jit-service/src/main/java/org/dcsa/jit/service/TimestampRoutingService.java b/jit-service/src/main/java/org/dcsa/jit/service/TimestampRoutingService.java index 28217823..c8a87ed1 100644 --- a/jit-service/src/main/java/org/dcsa/jit/service/TimestampRoutingService.java +++ b/jit-service/src/main/java/org/dcsa/jit/service/TimestampRoutingService.java @@ -40,8 +40,12 @@ public void routeMessage(TimestampTO timestamp) { List outboxMessages = messageRoutingRules.stream().map(rule -> toOutboxMessage(rule, payload)).toList(); outboxMessageRepository.saveAll(outboxMessages); + log.info("Timestamp for vessel IMO number {} scheduled for delivery to {} recipient(s)", + vesselIMONumber, + messageRoutingRules.size() + ); } else { - log.debug("No message routing rules found for vesselIMONumber '{}' and publisherRole '{}'", + log.info("No message routing rules found for vesselIMONumber '{}' and publisherRole '{}'", vesselIMONumber, publisherRole); } } diff --git a/jit-service/src/main/java/org/dcsa/jit/service/TimestampService.java b/jit-service/src/main/java/org/dcsa/jit/service/TimestampService.java index 6ed21cf9..53e98ab6 100644 --- a/jit-service/src/main/java/org/dcsa/jit/service/TimestampService.java +++ b/jit-service/src/main/java/org/dcsa/jit/service/TimestampService.java @@ -47,7 +47,6 @@ public class TimestampService { private final SMDGDelayReasonRepository smdgDelayReasonRepository; private final UnLocationRepository unLocationRepository; private final LocationRepository locationRepository; - private final UnmappedEventRepository unmappedEventRepository; private final PartyRepository partyRepository; private final AddressRepository addressRepository; private final CarrierRepository carrierRepository; @@ -288,13 +287,6 @@ public OperationsEvent create(OperationsEvent operationsEvent) { validateTimestamp(operationsEvent, timestampDefinition); - UnmappedEvent unmappedEvent = - UnmappedEvent.builder() - .eventID(operationsEvent.getEventID()) - .enqueuedAtDateTime(operationsEvent.getEventDateTime()) - .newRecord(true) - .build(); - unmappedEventRepository.save(unmappedEvent); return operationsEvent; } diff --git a/pom.xml b/pom.xml index 8ad42707..8f62952b 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.dcsa dcsa-bom - 1.3-SNAPSHOT + 1.3-RELEASE org.dcsa.jit @@ -17,15 +17,15 @@ - 1.1.0 + 1.2.0-beta2-rc2 - -SNAPSHOT + -RELEASE 0.1.0 - -SNAPSHOT + -RELEASE ${dcsa.shared-kernel.version}${dcsa.shared-kernel.tag}${dcsa.shared-kernel.artifacttype}