diff --git a/api/pom.xml b/api/pom.xml index ea7132b..316cac8 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -1,171 +1,256 @@ - - 4.0.0 - com.ineat - colis-tracker - 1.0.0-SNAPSHOT + + 4.0.0 + com.ineat + tracking-tracker + 1.0.0-SNAPSHOT - - 3.8.1 - true - 11 - 11 - UTF-8 - UTF-8 - 1.11.3.Final - quarkus-universe-bom - io.quarkus - 1.11.3.Final - 3.0.0-M5 - 1.4.2.Final - + + 3.8.1 + true + 11 + 11 + UTF-8 + UTF-8 + 1.11.3.Final + quarkus-universe-bom + io.quarkus + 1.11.3.Final + 3.0.0-M5 + 1.4.2.Final + ${project.build.directory}/generated-sources/avro + src/main/resources/avro/ + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + - - - ${quarkus.platform.group-id} - ${quarkus.platform.artifact-id} - ${quarkus.platform.version} - pom - import - - - + + + io.quarkus + quarkus-smallrye-openapi + + + io.quarkus + quarkus-resteasy-reactive + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + + + io.quarkus + quarkus-arc + + + org.projectlombok + lombok + 1.18.16 + provided + + + org.mapstruct + mapstruct + ${org.mapstruct.version} + + + + + io.quarkus + quarkus-flyway + + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-hibernate-reactive-panache + + + io.quarkus + quarkus-reactive-pg-client + + + + + io.quarkus + quarkus-smallrye-reactive-messaging-kafka + + + io.quarkus + quarkus-avro + - - - io.quarkus - quarkus-hibernate-reactive-panache - - - io.quarkus - quarkus-resteasy-reactive-jackson - - - io.quarkus - quarkus-reactive-pg-client - - - io.quarkus - quarkus-resteasy-reactive - - - io.quarkus - quarkus-arc - - - io.quarkus - quarkus-junit5 - test - - - io.rest-assured - rest-assured - test - - - org.projectlombok - lombok - 1.18.16 - provided - - - org.mapstruct - mapstruct - ${org.mapstruct.version} - - - io.quarkus - quarkus-flyway - - - io.quarkus - quarkus-jdbc-postgresql - - - io.quarkus - quarkus-smallrye-openapi - - - - - - io.quarkus - quarkus-maven-plugin - ${quarkus-plugin.version} - true - - - - build - generate-code - generate-code-tests - - - - - - maven-compiler-plugin - ${compiler-plugin.version} - - 11 - 11 - - - org.mapstruct - mapstruct-processor - ${org.mapstruct.version} - - - - - - maven-surefire-plugin - ${surefire-plugin.version} - - - org.jboss.logmanager.LogManager - ${maven.home} - - - - - - - - native - - - native - - - + + + io.quarkus + quarkus-junit5 + test + + + io.smallrye.reactive + smallrye-reactive-messaging-in-memory + 2.8.0 + test + + + io.quarkus + quarkus-junit5-mockito + test + + + org.assertj + assertj-core + 3.11.1 + test + + + org.testcontainers + testcontainers + 1.15.2 + test + + + org.mockito + mockito-core + + + org.mockito + mockito-junit-jupiter + + + - - maven-failsafe-plugin - ${surefire-plugin.version} - - - - integration-test - verify - + + org.apache.avro + avro-maven-plugin + 1.10.0 + + + generate-sources + + schema + + + src/main/resources/avro + ${project.build.directory}/generated-sources + String + + **/*.avro + + + ${project.basedir}/src/main/resources/avro/TrackingEventPayload.avsc + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus-plugin.version} + true + + + + build + generate-code + generate-code-tests + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + 11 + 11 + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + org.projectlombok + lombok + 1.18.16 + + + + + + maven-surefire-plugin + ${surefire-plugin.version} - - ${project.build.directory}/${project.build.finalName}-runner - org.jboss.logmanager.LogManager - ${maven.home} - + + org.jboss.logmanager.LogManager + ${maven.home} + - - - + + - - - native - - - + + + + native + + + native + + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + String + ${project.avroSchemas.directory} + ${avro.generation.directory} + + **/*.avro + + + ${project.basedir}/src/main/resources/avro/TrackingEventPayload.avsc + + + + + + + + + native + + + diff --git a/api/src/main/docker/docker-compose.dev.yml b/api/src/main/docker/docker-compose.dev.yml index d41e388..e2ffd9d 100644 --- a/api/src/main/docker/docker-compose.dev.yml +++ b/api/src/main/docker/docker-compose.dev.yml @@ -1,6 +1,19 @@ version: '2' services: + kafka: + image: landoop/fast-data-dev:latest + ports: + - "9092:9092" + - "9081:8081" + - "9082:8082" + - "9083:8083" + - "2181:2181" + - "3030:3030" + - "9581-9585:9581-9585" + environment: + - ADV_HOST=127.0.0.1 + database: image: postgres environment: diff --git a/api/src/main/java/com/ineat/colistracker/application/ColisTrackerApplication.java b/api/src/main/java/com/ineat/colistracker/application/ColisTrackerApplication.java new file mode 100644 index 0000000..8156a52 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/application/ColisTrackerApplication.java @@ -0,0 +1,30 @@ +package com.ineat.colistracker.application; + + +import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; +import org.eclipse.microprofile.openapi.annotations.info.Contact; +import org.eclipse.microprofile.openapi.annotations.info.Info; +import org.eclipse.microprofile.openapi.annotations.info.License; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +import javax.ws.rs.core.Application; + +@OpenAPIDefinition( + tags = { + @Tag(name="trackings", description="Operations related to trackings"), + @Tag(name="couriers", description="Operations related to couriers") + }, + info = @Info( + title="Colis Tracker API", + version = "1.0.0-SNAPSHOT", + contact = @Contact( + name = "Example API Support", + url = "http://exampleurl.com/contact", + email = "techsupport@example.com"), + license = @License( + name = "Apache 2.0", + url = "https://www.apache.org/licenses/LICENSE-2.0.html")) +) +public class ColisTrackerApplication extends Application { + +} diff --git a/api/src/main/java/com/ineat/colistracker/domain/model/Colis.java b/api/src/main/java/com/ineat/colistracker/domain/model/Colis.java deleted file mode 100644 index dc734f4..0000000 --- a/api/src/main/java/com/ineat/colistracker/domain/model/Colis.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.ineat.colistracker.domain.model; - - -import java.time.LocalDateTime; - - -public class Colis { - public Long id; - public String name; - public String trackingId; - public LocalDateTime created; - public LocalDateTime updated; -} diff --git a/api/src/main/java/com/ineat/colistracker/domain/model/Courier.java b/api/src/main/java/com/ineat/colistracker/domain/model/Courier.java new file mode 100644 index 0000000..2ae570b --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/domain/model/Courier.java @@ -0,0 +1,27 @@ +package com.ineat.colistracker.domain.model; + +import lombok.Getter; +import org.eclipse.microprofile.config.ConfigProvider; + +public enum Courier { + LA_POSTE("La Poste", "colis_tracker.trackers.la_poste"), + DPD("DPD", "colis_tracker.trackers.dpd"), + MONDIAL_RELAY("Mondial Relay", "colis_tracker.trackers.mondial_relay"); + + @Getter + private final String name; + private final String configProperty; + + Courier(String name, String configProperty) { + this.name = name; + this.configProperty = configProperty; + } + + public boolean isActive() { + final String activeConfigPropertyKey = String.format("%s.active", configProperty); + + return ConfigProvider.getConfig() + .getOptionalValue(activeConfigPropertyKey, Boolean.class) + .orElse(false); + } +} diff --git a/api/src/main/java/com/ineat/colistracker/domain/model/Tracking.java b/api/src/main/java/com/ineat/colistracker/domain/model/Tracking.java new file mode 100644 index 0000000..e82b531 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/domain/model/Tracking.java @@ -0,0 +1,52 @@ +package com.ineat.colistracker.domain.model; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.NoArgsConstructor; +import lombok.With; + +import java.time.LocalDateTime; +import java.util.Set; + +@With +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Tracking { + /** + * Technical id used internally to identify the tracking + */ + public Long id; + + /** + * User defined name for the tracking + */ + public String name; + + /** + * Courier id + */ + public String trackingId; + + /** + * Datetime for when the tracking was created + */ + public LocalDateTime created; + + public LocalDateTime updated; + + public TrackingStatus status; + + public Courier courier; + + public Set history; + + public void activate() { + this.status = TrackingStatus.ACTIVE; + } + + public void archive() { + this.status = TrackingStatus.ARCHIVED; + } +} diff --git a/api/src/main/java/com/ineat/colistracker/domain/model/TrackingEvent.java b/api/src/main/java/com/ineat/colistracker/domain/model/TrackingEvent.java new file mode 100644 index 0000000..10009c1 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/domain/model/TrackingEvent.java @@ -0,0 +1,9 @@ +package com.ineat.colistracker.domain.model; + +import java.time.LocalDateTime; + +public class TrackingEvent { + public Long id; + public LocalDateTime time; + public String description; +} diff --git a/api/src/main/java/com/ineat/colistracker/domain/model/TrackingStatus.java b/api/src/main/java/com/ineat/colistracker/domain/model/TrackingStatus.java new file mode 100644 index 0000000..140553a --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/domain/model/TrackingStatus.java @@ -0,0 +1,16 @@ +package com.ineat.colistracker.domain.model; + +/** + * User defined status for the tracking. This + * defines if we watch for updates for the tracking or not + */ +public enum TrackingStatus { + /** + * Active. We'll watch for update for the tracking + */ + ACTIVE, + /** + * Archived. We won't watch for updates for the tracking + */ + ARCHIVED +} diff --git a/api/src/main/java/com/ineat/colistracker/domain/repository/ColisRepository.java b/api/src/main/java/com/ineat/colistracker/domain/repository/ColisRepository.java deleted file mode 100644 index 8c2b557..0000000 --- a/api/src/main/java/com/ineat/colistracker/domain/repository/ColisRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ineat.colistracker.domain.repository; - -import com.ineat.colistracker.domain.model.Colis; -import io.smallrye.mutiny.Multi; -import io.smallrye.mutiny.Uni; - -public interface ColisRepository { - Uni create(Colis colis); - - Multi all(); - - Uni delete(Long id); - - Uni update(Colis colis); -} diff --git a/api/src/main/java/com/ineat/colistracker/domain/repository/TrackingEventRepository.java b/api/src/main/java/com/ineat/colistracker/domain/repository/TrackingEventRepository.java new file mode 100644 index 0000000..d9ecee1 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/domain/repository/TrackingEventRepository.java @@ -0,0 +1,8 @@ +package com.ineat.colistracker.domain.repository; + +import com.ineat.colistracker.domain.model.TrackingEvent; +import io.smallrye.mutiny.Uni; + +public interface TrackingEventRepository { + Uni save(TrackingEvent trackingEvent); +} diff --git a/api/src/main/java/com/ineat/colistracker/domain/repository/TrackingRepository.java b/api/src/main/java/com/ineat/colistracker/domain/repository/TrackingRepository.java new file mode 100644 index 0000000..03db0ea --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/domain/repository/TrackingRepository.java @@ -0,0 +1,17 @@ +package com.ineat.colistracker.domain.repository; + +import com.ineat.colistracker.domain.model.Tracking; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +public interface TrackingRepository { + Uni create(Tracking tracking); + + Uni getById(Long id); + + Multi all(); + + Uni delete(Long id); + + Uni update(Tracking tracking); +} diff --git a/api/src/main/java/com/ineat/colistracker/domain/service/ColisService.java b/api/src/main/java/com/ineat/colistracker/domain/service/ColisService.java deleted file mode 100644 index 9462961..0000000 --- a/api/src/main/java/com/ineat/colistracker/domain/service/ColisService.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.ineat.colistracker.domain.service; - -import com.ineat.colistracker.domain.model.Colis; -import com.ineat.colistracker.domain.repository.ColisRepository; -import io.smallrye.mutiny.Multi; -import io.smallrye.mutiny.Uni; -import lombok.extern.java.Log; -import lombok.extern.slf4j.Slf4j; - -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; - -@ApplicationScoped -@Slf4j -public class ColisService { - - @Inject - ColisRepository colisRepository; - - public Uni create(Colis colis) { - return colisRepository - .create(colis) - .onItem().invoke(created -> log.info("Created colis with id {} and tracking id {}", created.id, created.trackingId)); - } - - public Multi findAll() { - return colisRepository.all(); - } - - public Uni delete(Long id) { - return colisRepository - .delete(id) - .onItem().invoke(aBoolean -> log.info("Colis with id {} deleted : {}", id, aBoolean)); - } - - public Uni update(Colis colis) { - return colisRepository - .update(colis) - .onItem().invoke(updated -> log.info("Colis with id {} successfully updated", updated.id)); - } -} diff --git a/api/src/main/java/com/ineat/colistracker/domain/usecase/courier/CourierFetcher.java b/api/src/main/java/com/ineat/colistracker/domain/usecase/courier/CourierFetcher.java new file mode 100644 index 0000000..fbed559 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/domain/usecase/courier/CourierFetcher.java @@ -0,0 +1,14 @@ +package com.ineat.colistracker.domain.usecase.courier; + +import com.ineat.colistracker.domain.model.Courier; +import io.smallrye.mutiny.Multi; + +import javax.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class CourierFetcher { + + public Multi all() { + return Multi.createFrom().items(Courier.values()); + } +} diff --git a/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingAppender.java b/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingAppender.java new file mode 100644 index 0000000..c00fef3 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingAppender.java @@ -0,0 +1,27 @@ +package com.ineat.colistracker.domain.usecase.tracking; + +import com.ineat.colistracker.domain.model.Tracking; +import com.ineat.colistracker.domain.repository.TrackingRepository; +import io.smallrye.mutiny.Uni; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import javax.enterprise.context.ApplicationScoped; +import javax.transaction.Transactional; + +@ApplicationScoped +@RequiredArgsConstructor +@Slf4j +public class TrackingAppender { + + private final TrackingRepository trackingRepository; + + @Transactional + public Uni execute(Tracking tracking) { + tracking.activate(); + + return trackingRepository + .create(tracking) + .onItem().invoke(created -> log.info("Created tracking with id {} and tracking id {}", created.id, created.trackingId)); + } +} diff --git a/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingFetcher.java b/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingFetcher.java new file mode 100644 index 0000000..e44c2b5 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingFetcher.java @@ -0,0 +1,27 @@ +package com.ineat.colistracker.domain.usecase.tracking; + +import com.ineat.colistracker.domain.model.Tracking; +import com.ineat.colistracker.domain.repository.TrackingRepository; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import javax.enterprise.context.ApplicationScoped; +import javax.transaction.Transactional; + +@ApplicationScoped +@RequiredArgsConstructor +@Slf4j +public class TrackingFetcher { + private final TrackingRepository trackingRepository; + + @Transactional + public Multi findAll() { + return trackingRepository.all(); + } + + public Uni getById(Long id) { + return trackingRepository.getById(id); + } +} diff --git a/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingPatcher.java b/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingPatcher.java new file mode 100644 index 0000000..2f2addc --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingPatcher.java @@ -0,0 +1,47 @@ +package com.ineat.colistracker.domain.usecase.tracking; + +import com.ineat.colistracker.domain.model.Tracking; +import com.ineat.colistracker.domain.repository.TrackingRepository; +import io.smallrye.mutiny.Uni; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import javax.enterprise.context.ApplicationScoped; +import javax.transaction.Transactional; + +@ApplicationScoped +@Slf4j +@RequiredArgsConstructor +public class TrackingPatcher { + + private final TrackingRepository trackingRepository; + + @Transactional + public Uni execute(Tracking tracking) { + if (tracking.id == null) { + return Uni.createFrom().failure(() -> new IllegalArgumentException("Tracking to update should not have null id")); + } + + return trackingRepository.getById(tracking.id) + .onFailure().invoke(error -> log.error("Could not find tracking with id {} to update", tracking.id, error)) + .onItem().transform(existing -> updateExistingTrackingWithFieldsToModify(tracking, existing)) + .onItem().call(trackingRepository::update) + .onItem().invoke(updated -> log.info("Colis with id {} successfully updated", updated.id)); + } + + private Tracking updateExistingTrackingWithFieldsToModify(Tracking toModify, Tracking existing) { + if (toModify.name != null) { + existing.name = toModify.name; + } + + if (toModify.trackingId != null) { + existing.trackingId = toModify.trackingId; + } + + if (toModify.status != null) { + existing.status = toModify.status; + } + + return existing; + } +} diff --git a/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingRemover.java b/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingRemover.java new file mode 100644 index 0000000..c855299 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingRemover.java @@ -0,0 +1,23 @@ +package com.ineat.colistracker.domain.usecase.tracking; + +import com.ineat.colistracker.domain.repository.TrackingRepository; +import io.smallrye.mutiny.Uni; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import javax.enterprise.context.ApplicationScoped; +import javax.transaction.Transactional; + +@ApplicationScoped +@RequiredArgsConstructor +@Slf4j +public class TrackingRemover { + private final TrackingRepository trackingRepository; + + @Transactional + public Uni execute(Long id) { + return trackingRepository + .delete(id) + .onItem().invoke(aBoolean -> log.info("Colis with id {} deleted : {}", id, aBoolean)); + } +} diff --git a/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingUpdateEventHandler.java b/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingUpdateEventHandler.java new file mode 100644 index 0000000..5005ded --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/domain/usecase/tracking/TrackingUpdateEventHandler.java @@ -0,0 +1,21 @@ +package com.ineat.colistracker.domain.usecase.tracking; + +import com.ineat.colistracker.domain.model.Tracking; +import com.ineat.colistracker.domain.model.TrackingEvent; +import com.ineat.colistracker.domain.repository.TrackingRepository; +import io.smallrye.mutiny.Uni; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import javax.enterprise.context.ApplicationScoped; + +@ApplicationScoped +@RequiredArgsConstructor +@Slf4j +public class TrackingUpdateEventHandler { + private final TrackingRepository trackingRepository; + + public Uni execute(Long trackingId, TrackingEvent trackingEvent) { + return trackingRepository.getById(trackingId); + } +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/CourierDTO.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/CourierDTO.java new file mode 100644 index 0000000..c53401f --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/CourierDTO.java @@ -0,0 +1,10 @@ +package com.ineat.colistracker.infrastructure.api.dto; + +import lombok.Builder; + +@Builder +public class CourierDTO { + public String id; + public String name; + public boolean active; +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/CreateOrUpdateColisDTO.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/CreateOrUpdateColisDTO.java deleted file mode 100644 index 3bc8610..0000000 --- a/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/CreateOrUpdateColisDTO.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.ineat.colistracker.infrastructure.api.dto; - -public class CreateOrUpdateColisDTO { - public String name; - public String trackingId; -} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/CreateTrackingDTO.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/CreateTrackingDTO.java new file mode 100644 index 0000000..5a95eee --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/CreateTrackingDTO.java @@ -0,0 +1,9 @@ +package com.ineat.colistracker.infrastructure.api.dto; + +import com.ineat.colistracker.domain.model.Courier; + +public class CreateTrackingDTO { + public String name; + public String trackingId; + public Courier courier; +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/ErrorDTO.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/ErrorDTO.java new file mode 100644 index 0000000..092b7ae --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/ErrorDTO.java @@ -0,0 +1,9 @@ +package com.ineat.colistracker.infrastructure.api.dto; + +import lombok.Builder; + +@Builder +public class ErrorDTO { + public String error; + public String description; +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/TrackingDTO.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/TrackingDTO.java new file mode 100644 index 0000000..ac85598 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/TrackingDTO.java @@ -0,0 +1,17 @@ +package com.ineat.colistracker.infrastructure.api.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.time.LocalDateTime; +import java.util.Set; + +public class TrackingDTO { + public Long id; + public String name; + public String trackingId; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + public LocalDateTime created; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + public LocalDateTime updated; + public Set trackingHistory; +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/TrackingEventDTO.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/TrackingEventDTO.java new file mode 100644 index 0000000..077ebe1 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/TrackingEventDTO.java @@ -0,0 +1,9 @@ +package com.ineat.colistracker.infrastructure.api.dto; + +import java.time.LocalDateTime; + +public class TrackingEventDTO { + public Long id; + public LocalDateTime time; + public String description; +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/ColisDTO.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/TrackingListDTO.java similarity index 91% rename from api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/ColisDTO.java rename to api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/TrackingListDTO.java index 69e5c86..f3506e9 100644 --- a/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/ColisDTO.java +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/TrackingListDTO.java @@ -4,12 +4,16 @@ import java.time.LocalDateTime; -public class ColisDTO { +public class TrackingListDTO { public Long id; + public String name; + public String trackingId; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") public LocalDateTime created; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") public LocalDateTime updated; } diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/UpdateTrackingDTO.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/UpdateTrackingDTO.java new file mode 100644 index 0000000..663ad1e --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/api/dto/UpdateTrackingDTO.java @@ -0,0 +1,11 @@ +package com.ineat.colistracker.infrastructure.api.dto; + +import com.ineat.colistracker.domain.model.Courier; +import com.ineat.colistracker.domain.model.TrackingStatus; + +public class UpdateTrackingDTO { + public String name; + public String trackingId; + public Courier courier; + public TrackingStatus status; +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/mapper/ColisMapper.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/mapper/ColisMapper.java deleted file mode 100644 index df712ac..0000000 --- a/api/src/main/java/com/ineat/colistracker/infrastructure/api/mapper/ColisMapper.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ineat.colistracker.infrastructure.api.mapper; - -import com.ineat.colistracker.domain.model.Colis; -import com.ineat.colistracker.infrastructure.api.dto.ColisDTO; -import com.ineat.colistracker.infrastructure.api.dto.CreateOrUpdateColisDTO; -import org.mapstruct.Mapper; - -@Mapper(componentModel = "cdi") -public interface ColisMapper { - Colis toDomain(ColisDTO dto); - - ColisDTO toDTO(Colis colis); - - Colis toDomain(CreateOrUpdateColisDTO dto); -} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/mapper/CourierMapper.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/mapper/CourierMapper.java new file mode 100644 index 0000000..35958b7 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/api/mapper/CourierMapper.java @@ -0,0 +1,16 @@ +package com.ineat.colistracker.infrastructure.api.mapper; + +import com.ineat.colistracker.domain.model.Courier; +import com.ineat.colistracker.infrastructure.api.dto.CourierDTO; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "cdi") +public interface CourierMapper { + default CourierDTO toDTO(Courier courier) { + return CourierDTO.builder() + .id(courier.name()) + .name(courier.getName()) + .active(courier.isActive()) + .build(); + } +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/mapper/TrackingMapper.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/mapper/TrackingMapper.java new file mode 100644 index 0000000..804bfbb --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/api/mapper/TrackingMapper.java @@ -0,0 +1,23 @@ +package com.ineat.colistracker.infrastructure.api.mapper; + +import com.ineat.colistracker.domain.model.Tracking; +import com.ineat.colistracker.domain.model.TrackingEvent; +import com.ineat.colistracker.infrastructure.api.dto.*; +import org.mapstruct.Mapper; + +import java.util.Set; + +@Mapper(componentModel = "cdi") +public interface TrackingMapper { + + TrackingDTO toDTO(Tracking tracking); + + TrackingListDTO toListDTO(Tracking tracking); + + Set toListDTOs(Set events); + + Tracking toDomain(CreateTrackingDTO dto); + + Tracking toDomain(UpdateTrackingDTO dto); + +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/resource/ColisResource.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/resource/ColisResource.java deleted file mode 100644 index 1b360bc..0000000 --- a/api/src/main/java/com/ineat/colistracker/infrastructure/api/resource/ColisResource.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.ineat.colistracker.infrastructure.api.resource; - -import com.ineat.colistracker.domain.model.Colis; -import com.ineat.colistracker.domain.service.ColisService; -import com.ineat.colistracker.infrastructure.api.dto.ColisDTO; -import com.ineat.colistracker.infrastructure.api.dto.CreateOrUpdateColisDTO; -import com.ineat.colistracker.infrastructure.api.mapper.ColisMapper; -import io.smallrye.mutiny.Multi; -import io.smallrye.mutiny.Uni; - -import javax.inject.Inject; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.net.URI; - -@Path("/api/colis") -@Consumes(MediaType.APPLICATION_JSON) -@Produces(MediaType.APPLICATION_JSON) -public class ColisResource { - - @Inject - ColisService colisService; - - @Inject - ColisMapper colisMapper; - - @POST - public Uni create(CreateOrUpdateColisDTO body) { - final Colis colis = colisMapper.toDomain(body); - - return colisService.create(colis) - .onItem().transform(created -> colisMapper.toDTO(created)) - .onItem().transform(created -> Response.status(Response.Status.CREATED).entity(created).build()); - } - - @GET - public Multi findAll() { - return colisService.findAll() - .onItem().transform(colis -> colisMapper.toDTO(colis)); - } - - @DELETE - @Path("/{id}") - public Uni delete(@PathParam("id") Long id) { - return colisService.delete(id) - .onItem().transform(res -> Response.ok().build()); - } - - @PUT - @Path("/{id}") - public Uni update(@PathParam("id") Long id, CreateOrUpdateColisDTO dto) { - final Colis colis = colisMapper.toDomain(dto); - colis.id = id; - - return colisService.update(colis) - .onItem().transform(updated -> colisMapper.toDTO(updated)); - } -} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/resource/CourierResource.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/resource/CourierResource.java new file mode 100644 index 0000000..6e91b27 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/api/resource/CourierResource.java @@ -0,0 +1,31 @@ +package com.ineat.colistracker.infrastructure.api.resource; + +import com.ineat.colistracker.domain.usecase.courier.CourierFetcher; +import com.ineat.colistracker.infrastructure.api.dto.CourierDTO; +import com.ineat.colistracker.infrastructure.api.mapper.CourierMapper; +import io.smallrye.mutiny.Multi; +import lombok.RequiredArgsConstructor; +import org.eclipse.microprofile.openapi.annotations.tags.Tags; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("/api/couriers") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Tags(refs = {"couriers"}) +@RequiredArgsConstructor +public class CourierResource { + + private final CourierFetcher courierFetcher; + private final CourierMapper courierMapper; + + @GET + public Multi all() { + return courierFetcher.all() + .onItem().transform(courierMapper::toDTO); + } +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/api/resource/TrackingResource.java b/api/src/main/java/com/ineat/colistracker/infrastructure/api/resource/TrackingResource.java new file mode 100644 index 0000000..bb0bcc1 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/api/resource/TrackingResource.java @@ -0,0 +1,92 @@ +package com.ineat.colistracker.infrastructure.api.resource; + +import com.ineat.colistracker.domain.model.Tracking; +import com.ineat.colistracker.domain.usecase.tracking.TrackingAppender; +import com.ineat.colistracker.domain.usecase.tracking.TrackingFetcher; +import com.ineat.colistracker.domain.usecase.tracking.TrackingPatcher; +import com.ineat.colistracker.domain.usecase.tracking.TrackingRemover; +import com.ineat.colistracker.infrastructure.api.dto.CreateTrackingDTO; +import com.ineat.colistracker.infrastructure.api.dto.ErrorDTO; +import com.ineat.colistracker.infrastructure.api.dto.TrackingDTO; +import com.ineat.colistracker.infrastructure.api.dto.UpdateTrackingDTO; +import com.ineat.colistracker.infrastructure.api.mapper.TrackingMapper; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import lombok.RequiredArgsConstructor; +import org.eclipse.microprofile.openapi.annotations.tags.Tags; +import org.eclipse.microprofile.reactive.messaging.Channel; +import org.jboss.resteasy.reactive.RestSseElementType; + +import javax.inject.Inject; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +@Path("/api/trackings") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Tags(refs = {"trackings"}) +@RequiredArgsConstructor +public class TrackingResource { + + private final TrackingPatcher trackingPatcher; + private final TrackingAppender trackingAppender; + private final TrackingFetcher trackingFetcher; + private final TrackingRemover trackingRemover; + private final TrackingMapper trackingMapper; + + @Inject + @Channel("tracking-updates") + Multi trackingUpdates; + + @POST + public Uni create(CreateTrackingDTO body) { + final Tracking tracking = trackingMapper.toDomain(body); + + return trackingAppender.execute(tracking) + .onItem().transform(trackingMapper::toDTO) + .onItem().transform(created -> Response.status(Response.Status.CREATED).entity(created).build()) + .onFailure().recoverWithItem(throwable -> { + final ErrorDTO errorDTO = ErrorDTO.builder().error(throwable.getCause().toString()).description(throwable.getMessage()).build(); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(errorDTO).build(); + }); + } + + @GET + public Multi findAll() { + return trackingFetcher.findAll() + .onItem().transform(trackingMapper::toDTO); + } + + @DELETE + @Path("/{id}") + public Uni delete(@PathParam("id") Long id) { + return trackingRemover.execute(id) + .onItem().transform(res -> Response.ok().build()); + } + + @GET + @Path("/{id}") + public Uni get(@PathParam("id") Long id) { + return trackingFetcher.getById(id) + .onItem().transform(trackingMapper::toDTO); + } + + @PATCH + @Path("/{id}") + public Uni update(@PathParam("id") Long id, UpdateTrackingDTO dto) { + final Tracking tracking = trackingMapper.toDomain(dto); + tracking.id = id; + + return trackingPatcher.execute(tracking) + .onItem().transform(trackingMapper::toDTO); + } + + @GET + @Path("/{id}/updates") + @Produces(MediaType.SERVER_SENT_EVENTS) + @RestSseElementType(MediaType.APPLICATION_JSON) + public Multi streamUpdates(@PathParam("id") Long id) { + return trackingUpdates.filter(tracking -> tracking.id.equals(id)); + } +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/database/entity/ColisEntity.java b/api/src/main/java/com/ineat/colistracker/infrastructure/database/entity/TrackingEntity.java similarity index 63% rename from api/src/main/java/com/ineat/colistracker/infrastructure/database/entity/ColisEntity.java rename to api/src/main/java/com/ineat/colistracker/infrastructure/database/entity/TrackingEntity.java index bdf26b1..98ae972 100644 --- a/api/src/main/java/com/ineat/colistracker/infrastructure/database/entity/ColisEntity.java +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/database/entity/TrackingEntity.java @@ -1,16 +1,14 @@ package com.ineat.colistracker.infrastructure.database.entity; -import io.quarkus.hibernate.reactive.panache.PanacheEntity; -import io.quarkus.hibernate.reactive.panache.PanacheEntityBase; import org.hibernate.annotations.CreationTimestamp; -import org.hibernate.annotations.UpdateTimestamp; import javax.persistence.*; import java.time.LocalDateTime; +import java.util.Set; @Entity -@Table(name = "colis") -public class ColisEntity { +@Table(name = "tracking") +public class TrackingEntity { @Id @Column(name = "id", columnDefinition = "SERIAL") @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -24,7 +22,14 @@ public class ColisEntity { @CreationTimestamp public LocalDateTime created; - @UpdateTimestamp public LocalDateTime updated; + @OneToMany( + mappedBy = "tracking", + cascade = CascadeType.ALL, + orphanRemoval = true, + fetch = FetchType.LAZY + ) + public Set trackingHistory; + } diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/database/entity/TrackingEventEntity.java b/api/src/main/java/com/ineat/colistracker/infrastructure/database/entity/TrackingEventEntity.java new file mode 100644 index 0000000..1629bab --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/database/entity/TrackingEventEntity.java @@ -0,0 +1,22 @@ +package com.ineat.colistracker.infrastructure.database.entity; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Entity +@Table(name = "tracking_event") +public class TrackingEventEntity { + @Id + @Column(name = "id", columnDefinition = "SERIAL") + @GeneratedValue(strategy = GenerationType.IDENTITY) + public Long id; + + @Column(name = "time", columnDefinition = "TIMESTAMP", nullable = false) + public LocalDateTime time; + + @Column(name = "description", columnDefinition = "TEXT") + public String description; + + @ManyToOne(fetch = FetchType.LAZY) + public TrackingEntity tracking; +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/database/mapper/ColisMapper.java b/api/src/main/java/com/ineat/colistracker/infrastructure/database/mapper/ColisMapper.java deleted file mode 100644 index acafe22..0000000 --- a/api/src/main/java/com/ineat/colistracker/infrastructure/database/mapper/ColisMapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.ineat.colistracker.infrastructure.database.mapper; - -import com.ineat.colistracker.domain.model.Colis; -import com.ineat.colistracker.infrastructure.database.entity.ColisEntity; -import org.mapstruct.Mapper; - -@Mapper(componentModel = "cdi") -public interface ColisMapper { - - Colis toDomain(ColisEntity entity); - - ColisEntity toEntity(Colis colis); - -} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/database/mapper/TrackingEventMapper.java b/api/src/main/java/com/ineat/colistracker/infrastructure/database/mapper/TrackingEventMapper.java new file mode 100644 index 0000000..94d21ea --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/database/mapper/TrackingEventMapper.java @@ -0,0 +1,14 @@ +package com.ineat.colistracker.infrastructure.database.mapper; + +import com.ineat.colistracker.domain.model.TrackingEvent; +import com.ineat.colistracker.infrastructure.database.entity.TrackingEventEntity; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "cdi") +public interface TrackingEventMapper { + + TrackingEventEntity toEntity(TrackingEvent trackingEvent); + + TrackingEvent toDomain(TrackingEventEntity trackingEventEntity); + +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/database/mapper/TrackingMapper.java b/api/src/main/java/com/ineat/colistracker/infrastructure/database/mapper/TrackingMapper.java new file mode 100644 index 0000000..cc0b3ff --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/database/mapper/TrackingMapper.java @@ -0,0 +1,14 @@ +package com.ineat.colistracker.infrastructure.database.mapper; + +import com.ineat.colistracker.domain.model.Tracking; +import com.ineat.colistracker.infrastructure.database.entity.TrackingEntity; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "cdi", uses = TrackingEventMapper.class) +public interface TrackingMapper { + + Tracking toDomain(TrackingEntity entity); + + TrackingEntity toEntity(Tracking tracking); + +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/database/repository/ColisPanacheRepository.java b/api/src/main/java/com/ineat/colistracker/infrastructure/database/repository/ColisPanacheRepository.java deleted file mode 100644 index d39d261..0000000 --- a/api/src/main/java/com/ineat/colistracker/infrastructure/database/repository/ColisPanacheRepository.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.ineat.colistracker.infrastructure.database.repository; - -import com.ineat.colistracker.domain.model.Colis; -import com.ineat.colistracker.infrastructure.database.entity.ColisEntity; -import com.ineat.colistracker.infrastructure.database.mapper.ColisMapper; -import io.quarkus.hibernate.reactive.panache.PanacheRepository; -import io.smallrye.mutiny.Multi; -import io.smallrye.mutiny.Uni; -import com.ineat.colistracker.domain.repository.ColisRepository; -import org.hibernate.reactive.mutiny.Mutiny; - -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; - -@ApplicationScoped -public class ColisPanacheRepository implements ColisRepository, PanacheRepository { - - @Inject - ColisMapper mapper; - - @Override - public Uni create(Colis colis) { - final ColisEntity entity = mapper.toEntity(colis); - - return persistAndFlush(entity) - .map(persisted -> mapper.toDomain(entity)); - } - - @Override - public Multi all() { - return findAll().stream() - .onItem().transform(entity -> mapper.toDomain(entity)); - } - - @Override - public Uni delete(Long id) { - return deleteById(id); - } - - @Override - public Uni update(Colis colis) { - return Uni.createFrom().nullItem(); - } -} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/database/repository/TrackingPanacheRepository.java b/api/src/main/java/com/ineat/colistracker/infrastructure/database/repository/TrackingPanacheRepository.java new file mode 100644 index 0000000..e291957 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/database/repository/TrackingPanacheRepository.java @@ -0,0 +1,51 @@ +package com.ineat.colistracker.infrastructure.database.repository; + +import com.ineat.colistracker.domain.model.Tracking; +import com.ineat.colistracker.infrastructure.database.entity.TrackingEntity; +import com.ineat.colistracker.infrastructure.database.mapper.TrackingMapper; +import io.quarkus.hibernate.reactive.panache.PanacheRepository; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import com.ineat.colistracker.domain.repository.TrackingRepository; +import lombok.RequiredArgsConstructor; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +@ApplicationScoped +@RequiredArgsConstructor +public class TrackingPanacheRepository implements TrackingRepository, PanacheRepository { + + private final TrackingMapper mapper; + + @Override + public Uni create(Tracking tracking) { + final TrackingEntity entity = mapper.toEntity(tracking); + + return persistAndFlush(entity) + .onItem().transform(persisted -> mapper.toDomain(entity)); + } + + @Override + public Uni getById(Long id) { + return findById(id) + .onItem().transform(mapper::toDomain); + } + + @Override + public Multi all() { + return findAll() + .stream() + .onItem().transform(mapper::toDomain); + } + + @Override + public Uni delete(Long id) { + return deleteById(id); + } + + @Override + public Uni update(Tracking tracking) { + return Uni.createFrom().nullItem(); + } +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/exchange/consumer/TrackingEventConsumer.java b/api/src/main/java/com/ineat/colistracker/infrastructure/exchange/consumer/TrackingEventConsumer.java new file mode 100644 index 0000000..2f52bf2 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/exchange/consumer/TrackingEventConsumer.java @@ -0,0 +1,28 @@ +package com.ineat.colistracker.infrastructure.exchange.consumer; + +import com.ineat.colistracker.domain.model.Tracking; +import com.ineat.colistracker.domain.model.TrackingEvent; +import com.ineat.colistracker.domain.usecase.tracking.TrackingUpdateEventHandler; +import com.ineat.colistracker.infrastructure.database.mapper.TrackingEventMapper; +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.kafka.Record; +import lombok.RequiredArgsConstructor; +import org.eclipse.microprofile.reactive.messaging.Incoming; +import org.eclipse.microprofile.reactive.messaging.Outgoing; + +import javax.enterprise.context.ApplicationScoped; + +@ApplicationScoped +@RequiredArgsConstructor +public class TrackingEventConsumer { + + private final TrackingEventMapper trackingEventMapper; + private final TrackingUpdateEventHandler trackingUpdateEventHandler; + + @Incoming("tracking-events") + @Outgoing("tracking-updates") + public Uni consume(Record trackingUpdateEvent) { + final Long trackingId = Long.parseLong(trackingUpdateEvent.key()); + return trackingUpdateEventHandler.execute(trackingId, null); + } +} diff --git a/api/src/main/java/com/ineat/colistracker/infrastructure/exchange/mapper/TrackingEventMapper.java b/api/src/main/java/com/ineat/colistracker/infrastructure/exchange/mapper/TrackingEventMapper.java new file mode 100644 index 0000000..58d4d18 --- /dev/null +++ b/api/src/main/java/com/ineat/colistracker/infrastructure/exchange/mapper/TrackingEventMapper.java @@ -0,0 +1,18 @@ +package com.ineat.colistracker.infrastructure.exchange.mapper; + +import com.ineat.colistracker.domain.model.TrackingEvent; +import com.ineat.colistracker.infrastructure.exchange.avro.TrackingUpdateEvent; + +import org.mapstruct.Mapper; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +@Mapper +public interface TrackingEventMapper { + TrackingEvent toDomain(TrackingUpdateEvent trackingUpdateEvent); + + default LocalDateTime toLocalTime(Long timestamp) { + return LocalDateTime.ofEpochSecond(timestamp, 0, ZoneOffset.UTC); + } +} diff --git a/api/src/main/resources/application.properties b/api/src/main/resources/application.properties index cbda1b9..5cd7731 100644 --- a/api/src/main/resources/application.properties +++ b/api/src/main/resources/application.properties @@ -1,7 +1,18 @@ quarkus.flyway.migrate-at-start = true + quarkus.datasource.db-kind=postgresql quarkus.datasource.username=colistracker quarkus.datasource.password=colistracker quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/colistracker quarkus.datasource.reactive.url=postgresql://localhost:5432/colistracker -quarkus.datasource.jdbc=false \ No newline at end of file +quarkus.datasource.jdbc=false + +kafka.bootstrap.servers=localhost:9092 + +mp.messaging.incoming.tracking-events.connector=smallrye-kafka +mp.messaging.incoming.tracking-events.key.deserializer=org.apache.kafka.common.serialization.StringDeserializer +mp.messaging.incoming.tracking-events.value.deserializer=io.confluent.kafka.serializers.KafkaAvroDeserializer +mp.messaging.incoming.tracking-events.schema.registry.url=http://localhost:9081 +mp.messaging.incoming.tracking-events.specific.avro.reader=true + +colis_tracker.trackers.la_poste.active=true \ No newline at end of file diff --git a/api/src/main/resources/avro/trackingUpdate.avsc b/api/src/main/resources/avro/trackingUpdate.avsc new file mode 100644 index 0000000..c3cb112 --- /dev/null +++ b/api/src/main/resources/avro/trackingUpdate.avsc @@ -0,0 +1,15 @@ +{ + "namespace": "com.ineat.colistracker.infrastructure.exchange.avro", + "type": "record", + "name": "TrackingUpdateEvent", + "fields": [ + { + "name": "description", + "type": "string" + }, + { + "name": "time", + "type": "long" + } + ] +} \ No newline at end of file diff --git a/api/src/main/resources/db/migration/V1.0.0__Colis.sql b/api/src/main/resources/db/migration/V1.0.0__Colis.sql index 9d37a8b..569f4dd 100644 --- a/api/src/main/resources/db/migration/V1.0.0__Colis.sql +++ b/api/src/main/resources/db/migration/V1.0.0__Colis.sql @@ -1,4 +1,4 @@ -CREATE TABLE colis +CREATE TABLE tracking ( id BIGSERIAL, name TEXT, diff --git a/api/src/test/java/com/ineat/colistracker/ExampleResourceTest.java b/api/src/test/java/com/ineat/colistracker/ExampleResourceTest.java deleted file mode 100644 index 839861a..0000000 --- a/api/src/test/java/com/ineat/colistracker/ExampleResourceTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.ineat.colistracker; - -import io.quarkus.test.junit.QuarkusTest; -import org.junit.jupiter.api.Test; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.CoreMatchers.is; - -@QuarkusTest -public class ExampleResourceTest { - - @Test - public void testHelloEndpoint() { - given() - .when().get("/hello") - .then() - .statusCode(200) - .body(is("Hello RESTEasy Reactive")); - } - -} \ No newline at end of file diff --git a/api/src/test/java/com/ineat/colistracker/IntegrationTestLifecycleManager.java b/api/src/test/java/com/ineat/colistracker/IntegrationTestLifecycleManager.java new file mode 100644 index 0000000..e5eb349 --- /dev/null +++ b/api/src/test/java/com/ineat/colistracker/IntegrationTestLifecycleManager.java @@ -0,0 +1,37 @@ +package com.ineat.colistracker; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; +import io.smallrye.reactive.messaging.connectors.InMemoryConnector; +import org.testcontainers.containers.GenericContainer; + +import java.util.HashMap; +import java.util.Map; + +public class IntegrationTestLifecycleManager implements QuarkusTestResourceLifecycleManager { + + public GenericContainer registry = new GenericContainer<>("apicurio/apicurio-registry-mem:1.2.2.Final") + .withExposedPorts(8080) + .withEnv("QUARKUS_PROFILE", "prod") + .withEnv("KAFKA_BOOTSTRAP_SERVERS", "localhost:19092") + .withEnv("APPLICATION_ID", "registry_id") + .withEnv("APPLICATION_SERVER", "localhost:9000"); + + + @Override + public Map start() { + final Map props1 = InMemoryConnector.switchIncomingChannelsToInMemory("tracking-events"); + + // Schema registry + registry.start(); + + // Postgresql + + return new HashMap<>(props1); + } + + @Override + public void stop() { + InMemoryConnector.clear(); + registry.stop(); + } +} \ No newline at end of file diff --git a/api/src/test/java/com/ineat/colistracker/NativeExampleResourceIT.java b/api/src/test/java/com/ineat/colistracker/NativeExampleResourceIT.java deleted file mode 100644 index 6cf93d3..0000000 --- a/api/src/test/java/com/ineat/colistracker/NativeExampleResourceIT.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.ineat.colistracker; - -import io.quarkus.test.junit.NativeImageTest; - -@NativeImageTest -public class NativeExampleResourceIT extends ExampleResourceTest { - - // Execute the same tests but in native mode. -} \ No newline at end of file diff --git a/api/src/test/java/com/ineat/colistracker/domain/usecase/TrackingAppenderTest.java b/api/src/test/java/com/ineat/colistracker/domain/usecase/TrackingAppenderTest.java new file mode 100644 index 0000000..5cf83c4 --- /dev/null +++ b/api/src/test/java/com/ineat/colistracker/domain/usecase/TrackingAppenderTest.java @@ -0,0 +1,58 @@ +package com.ineat.colistracker.domain.usecase; + +import com.ineat.colistracker.domain.model.Courier; +import com.ineat.colistracker.domain.model.Tracking; +import com.ineat.colistracker.domain.model.TrackingStatus; +import com.ineat.colistracker.domain.repository.TrackingRepository; +import com.ineat.colistracker.domain.usecase.tracking.TrackingAppender; +import io.smallrye.mutiny.Uni; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class TrackingAppenderTest { + + @Mock + TrackingRepository trackingRepository; + + TrackingAppender trackingAppender; + + @BeforeEach + void init() { + this.trackingAppender = new TrackingAppender(trackingRepository); + } + + @Test + @DisplayName("When creating tracking, should be activated by default") + public void test() { + // ARRANGE + final Tracking tracking = Tracking.builder() + .trackingId("TRCK") + .courier(Courier.DPD) + .name("Some tracking") + .build(); + + when(trackingRepository.create(any(Tracking.class))) + .then(i -> { + final Tracking arg0 = i.getArgument(0, Tracking.class); + return Uni.createFrom().item(arg0.withId(1L)); + }); + + // ACT + final Tracking created = trackingAppender.execute(tracking) + .await().indefinitely(); + + //ASSERT + assertThat(created) + .extracting(t -> t.status) + .isEqualTo(TrackingStatus.ACTIVE); + } +} \ No newline at end of file