From cd0d12b168bf5ae919fc571ee2cc1010eff1a837 Mon Sep 17 00:00:00 2001 From: Daniel Aaron Salwerowicz Date: Tue, 8 Oct 2024 13:19:22 +0200 Subject: [PATCH] Fixed tests by adding a callback model, renamed more stuff --- .../hostapi/order/ApiOrderPayload.kt | 3 +- .../synq/SynqOrderStatusUpdatePayload.kt | 2 +- .../kotlin/no/nb/mlt/wls/domain/WLSService.kt | 4 +- ...allbackHandler.kt => InventoryNotifier.kt} | 6 +- .../nb/mlt/wls/infrastructure/BeansConfig.kt | 4 +- ...Adapter.kt => InventoryNotifierAdapter.kt} | 13 ++- .../callbacks/NotificationOrderPayload.kt | 97 +++++++++++++++++++ .../no/nb/mlt/wls/domain/WLSServiceTest.kt | 42 ++++---- 8 files changed, 133 insertions(+), 38 deletions(-) rename src/main/kotlin/no/nb/mlt/wls/domain/ports/outbound/{CallbackHandler.kt => InventoryNotifier.kt} (54%) rename src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/{CallbackHandlerAdapter.kt => InventoryNotifierAdapter.kt} (61%) create mode 100644 src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/NotificationOrderPayload.kt diff --git a/src/main/kotlin/no/nb/mlt/wls/application/hostapi/order/ApiOrderPayload.kt b/src/main/kotlin/no/nb/mlt/wls/application/hostapi/order/ApiOrderPayload.kt index 64e8a822..b4bd7424 100644 --- a/src/main/kotlin/no/nb/mlt/wls/application/hostapi/order/ApiOrderPayload.kt +++ b/src/main/kotlin/no/nb/mlt/wls/application/hostapi/order/ApiOrderPayload.kt @@ -11,7 +11,6 @@ import org.springframework.web.server.ServerWebInputException description = "Payload for creating and editing an order in Hermes WLS, and appropriate storage system(s).", example = """ { - "orderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "hostName": "AXIELL", "hostOrderId": "mlt-12345-order", "status": "NOT_STARTED", @@ -86,7 +85,7 @@ fun Order.toApiOrderPayload() = callbackUrl = callbackUrl ) -fun ApiOrderPayload.toOrder(): Order = +fun ApiOrderPayload.toOrder() = Order( hostName = hostName, hostOrderId = hostOrderId, diff --git a/src/main/kotlin/no/nb/mlt/wls/application/synqapi/synq/SynqOrderStatusUpdatePayload.kt b/src/main/kotlin/no/nb/mlt/wls/application/synqapi/synq/SynqOrderStatusUpdatePayload.kt index b17216cf..cda75105 100644 --- a/src/main/kotlin/no/nb/mlt/wls/application/synqapi/synq/SynqOrderStatusUpdatePayload.kt +++ b/src/main/kotlin/no/nb/mlt/wls/application/synqapi/synq/SynqOrderStatusUpdatePayload.kt @@ -27,7 +27,7 @@ data class SynqOrderStatusUpdatePayload( val status: SynqOrderStatus, @Schema( description = "Name of the host system which placed the order/owns the order products/items.", - example = "Axiell" + example = "AXIELL" ) val hostName: HostName, @Schema( diff --git a/src/main/kotlin/no/nb/mlt/wls/domain/WLSService.kt b/src/main/kotlin/no/nb/mlt/wls/domain/WLSService.kt index d826d37b..ba6e1c0a 100644 --- a/src/main/kotlin/no/nb/mlt/wls/domain/WLSService.kt +++ b/src/main/kotlin/no/nb/mlt/wls/domain/WLSService.kt @@ -21,8 +21,8 @@ import no.nb.mlt.wls.domain.ports.inbound.UpdateOrder import no.nb.mlt.wls.domain.ports.inbound.ValidationException import no.nb.mlt.wls.domain.ports.inbound.toItem import no.nb.mlt.wls.domain.ports.inbound.toOrder -import no.nb.mlt.wls.domain.ports.outbound.CallbackHandler import no.nb.mlt.wls.domain.ports.outbound.DuplicateResourceException +import no.nb.mlt.wls.domain.ports.outbound.InventoryNotifier import no.nb.mlt.wls.domain.ports.outbound.ItemId import no.nb.mlt.wls.domain.ports.outbound.ItemRepository import no.nb.mlt.wls.domain.ports.outbound.OrderRepository @@ -36,7 +36,7 @@ class WLSService( private val itemRepository: ItemRepository, private val orderRepository: OrderRepository, private val storageSystemFacade: StorageSystemFacade, - private val callbackHandler: CallbackHandler + private val inventoryNotifier: InventoryNotifier ) : AddNewItem, CreateOrder, DeleteOrder, UpdateOrder, GetOrder, GetItem, OrderStatusUpdate, MoveItem { override suspend fun addItem(itemMetadata: ItemMetadata): Item { getItem(itemMetadata.hostName, itemMetadata.hostId)?.let { diff --git a/src/main/kotlin/no/nb/mlt/wls/domain/ports/outbound/CallbackHandler.kt b/src/main/kotlin/no/nb/mlt/wls/domain/ports/outbound/InventoryNotifier.kt similarity index 54% rename from src/main/kotlin/no/nb/mlt/wls/domain/ports/outbound/CallbackHandler.kt rename to src/main/kotlin/no/nb/mlt/wls/domain/ports/outbound/InventoryNotifier.kt index 58547447..74c3df0a 100644 --- a/src/main/kotlin/no/nb/mlt/wls/domain/ports/outbound/CallbackHandler.kt +++ b/src/main/kotlin/no/nb/mlt/wls/domain/ports/outbound/InventoryNotifier.kt @@ -3,8 +3,8 @@ package no.nb.mlt.wls.domain.ports.outbound import no.nb.mlt.wls.domain.model.Item import no.nb.mlt.wls.domain.model.Order -interface CallbackHandler { - fun handleItemCallback(item: Item) +interface InventoryNotifier { + fun itemChanged(item: Item) - fun handleOrderCallback(order: Order) + fun orderChanged(order: Order) } diff --git a/src/main/kotlin/no/nb/mlt/wls/infrastructure/BeansConfig.kt b/src/main/kotlin/no/nb/mlt/wls/infrastructure/BeansConfig.kt index ba15f008..8774db75 100644 --- a/src/main/kotlin/no/nb/mlt/wls/infrastructure/BeansConfig.kt +++ b/src/main/kotlin/no/nb/mlt/wls/infrastructure/BeansConfig.kt @@ -1,7 +1,7 @@ package no.nb.mlt.wls.infrastructure import no.nb.mlt.wls.domain.WLSService -import no.nb.mlt.wls.infrastructure.callbacks.CallbackHandlerAdapter +import no.nb.mlt.wls.infrastructure.callbacks.InventoryNotifierAdapter import no.nb.mlt.wls.infrastructure.repositories.item.ItemRepositoryMongoAdapter import no.nb.mlt.wls.infrastructure.repositories.order.MongoOrderRepositoryAdapter import no.nb.mlt.wls.infrastructure.synq.SynqAdapter @@ -15,6 +15,6 @@ class BeansConfig { synqAdapter: SynqAdapter, itemMongoAdapter: ItemRepositoryMongoAdapter, orderMongoAdapter: MongoOrderRepositoryAdapter, - callbackHandler: CallbackHandlerAdapter + callbackHandler: InventoryNotifierAdapter ) = WLSService(itemMongoAdapter, orderMongoAdapter, synqAdapter, callbackHandler) } diff --git a/src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/CallbackHandlerAdapter.kt b/src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/InventoryNotifierAdapter.kt similarity index 61% rename from src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/CallbackHandlerAdapter.kt rename to src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/InventoryNotifierAdapter.kt index 5f5fadcc..ca94dd73 100644 --- a/src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/CallbackHandlerAdapter.kt +++ b/src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/InventoryNotifierAdapter.kt @@ -1,25 +1,24 @@ package no.nb.mlt.wls.infrastructure.callbacks -import no.nb.mlt.wls.application.hostapi.order.toApiOrderPayload import no.nb.mlt.wls.domain.model.Item import no.nb.mlt.wls.domain.model.Order -import no.nb.mlt.wls.domain.ports.outbound.CallbackHandler +import no.nb.mlt.wls.domain.ports.outbound.InventoryNotifier import org.springframework.stereotype.Component import org.springframework.web.reactive.function.client.WebClient @Component -class CallbackHandlerAdapter( +class InventoryNotifierAdapter( private val webClient: WebClient -) : CallbackHandler { - override fun handleItemCallback(item: Item) { +) : InventoryNotifier { + override fun itemChanged(item: Item) { TODO("Future task") } - override fun handleOrderCallback(order: Order) { + override fun orderChanged(order: Order) { webClient .post() .uri(order.callbackUrl) - .bodyValue(order.toApiOrderPayload()) + .bodyValue(order.toNotificationOrderPayload()) .retrieve() .bodyToMono(Void::class.java) .retry(5) diff --git a/src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/NotificationOrderPayload.kt b/src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/NotificationOrderPayload.kt new file mode 100644 index 00000000..13a98943 --- /dev/null +++ b/src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/NotificationOrderPayload.kt @@ -0,0 +1,97 @@ +package no.nb.mlt.wls.infrastructure.callbacks + +import io.swagger.v3.oas.annotations.media.Schema +import no.nb.mlt.wls.domain.model.HostName +import no.nb.mlt.wls.domain.model.Order +import no.nb.mlt.wls.domain.model.Owner + +@Schema( + description = "Payload for creating and editing an order in Hermes WLS, and appropriate storage system(s).", + example = """ + { + "hostName": "AXIELL", + "hostOrderId": "mlt-12345-order", + "status": "NOT_STARTED", + "orderLine": [ + { + "hostId": "mlt-12345", + "status": "NOT_STARTED" + } + ], + "orderType": "LOAN", + "owner": "NB", + "receiver": { + "name": "Doug Dimmadome", + "address": "Dimmsdale Dimmadome, 21st Ave. Texas" + }, + "callbackUrl": "https://example.com/send/callback/here" + } + """ +) +data class NotificationOrderPayload( + @Schema( + description = "Name of the host system which made the order.", + examples = ["AXIELL", "ALMA", "ASTA", "BIBLIOFIL"] + ) + val hostName: HostName, + @Schema( + description = "Order ID from the host system which made the order.", + example = "mlt-12345-order" + ) + val hostOrderId: String, + @Schema( + description = "Current status of the order as a whole.", + examples = ["NOT_STARTED", "IN_PROGRESS", "COMPLETED", "DELETED"] + ) + val status: Order.Status?, + @Schema( + description = "List of items in the order, also called order lines." + ) + val orderLine: List, + @Schema( + description = "Describes what type of order this is", + examples = ["LOAN", "DIGITIZATION"] + ) + val orderType: Order.Type, + @Schema( + description = "Who's the owner of the material in the order.", + examples = ["NB", "ARKIVVERKET"] + ) + val owner: Owner?, + @Schema( + description = "Who's the receiver of the material in the order." + ) + val receiver: Order.Receiver, + @Schema( + description = "Callback URL for the order used to update the order information in the host system.", + example = "https://example.com/send/callback/here" + ) + val callbackUrl: String +) + +fun Order.toNotificationOrderPayload() = + NotificationOrderPayload( + hostName = hostName, + hostOrderId = hostOrderId, + status = status, + orderLine = orderLine, + orderType = orderType, + owner = owner, + receiver = receiver, + callbackUrl = callbackUrl + ) + +fun NotificationOrderPayload.toOrder() = + Order( + hostName = hostName, + hostOrderId = hostOrderId, + status = status ?: Order.Status.NOT_STARTED, + orderLine = + orderLine.map { + Order.OrderItem(it.hostId, it.status) + }, + orderType = orderType, + owner = owner, + receiver = receiver, + callbackUrl = callbackUrl + ) diff --git a/src/test/kotlin/no/nb/mlt/wls/domain/WLSServiceTest.kt b/src/test/kotlin/no/nb/mlt/wls/domain/WLSServiceTest.kt index 6f4e9be3..3b56268f 100644 --- a/src/test/kotlin/no/nb/mlt/wls/domain/WLSServiceTest.kt +++ b/src/test/kotlin/no/nb/mlt/wls/domain/WLSServiceTest.kt @@ -17,7 +17,7 @@ import no.nb.mlt.wls.domain.ports.inbound.ItemMetadata import no.nb.mlt.wls.domain.ports.inbound.ItemNotFoundException import no.nb.mlt.wls.domain.ports.inbound.OrderNotFoundException import no.nb.mlt.wls.domain.ports.inbound.ValidationException -import no.nb.mlt.wls.domain.ports.outbound.CallbackHandler +import no.nb.mlt.wls.domain.ports.outbound.InventoryNotifier import no.nb.mlt.wls.domain.ports.outbound.ItemRepository import no.nb.mlt.wls.domain.ports.outbound.OrderRepository import no.nb.mlt.wls.domain.ports.outbound.StorageSystemException @@ -32,7 +32,7 @@ class WLSServiceTest { private val orderRepoMock = mockk() private val itemRepoMock = mockk() private val storageSystemRepoMock = mockk() - private val callbackHandlerMock = mockk() + private val inventoryNotifierMock = mockk() @BeforeEach fun beforeEach() { @@ -47,7 +47,7 @@ class WLSServiceTest { coEvery { itemRepoMock.createItem(any()) } answers { Mono.just(expectedItem) } coJustRun { storageSystemRepoMock.createItem(any()) } - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { val itemResult = cut.addItem( @@ -76,7 +76,7 @@ class WLSServiceTest { coEvery { itemRepoMock.createItem(any()) } answers { Mono.just(testItem.copy()) } coJustRun { storageSystemRepoMock.createItem(any()) } - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { val newItem = @@ -106,7 +106,7 @@ class WLSServiceTest { coEvery { itemRepoMock.getItem(HostName.AXIELL, "12345") } answers { expectedItem } - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { val itemResult = cut.getItem(HostName.AXIELL, "12345") assertThat(itemResult).isEqualTo(expectedItem) @@ -117,7 +117,7 @@ class WLSServiceTest { fun `getItem should return null if item does not exist`() { coEvery { itemRepoMock.getItem(HostName.AXIELL, "12345") } answers { null } - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { val itemResult = cut.getItem(HostName.AXIELL, "12345") assertThat(itemResult).isEqualTo(null) @@ -134,7 +134,7 @@ class WLSServiceTest { coEvery { itemRepoMock.getItem(any(), any()) } returns testItem coEvery { itemRepoMock.moveItem(any(), any(), any(), any()) } returns expectedItem - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { val movedItem = cut.moveItem(testItem.hostId, testItem.hostName, 1.0, "Somewhere nice") assertThat(movedItem).isEqualTo(expectedItem) @@ -149,7 +149,7 @@ class WLSServiceTest { fun `moveItem should fail when item does not exist`() { coEvery { itemRepoMock.moveItem(any(), any(), any(), any()) } throws ItemNotFoundException("Item not found") - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { assertThrows { cut.moveItem(testItem.hostId, testItem.hostName, 1.0, "Somewhere nice") @@ -165,7 +165,7 @@ class WLSServiceTest { fun `moveItem throws when count is invalid`() { coEvery { itemRepoMock.moveItem(any(), any(), -1.0, any()) } throws ValidationException("Location cannot be blank") - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { assertThrows { cut.moveItem(testItem.hostId, testItem.hostName, -1.0, "Somewhere nice") @@ -181,7 +181,7 @@ class WLSServiceTest { fun `moveItem throws when location is blank`() { coEvery { itemRepoMock.moveItem(any(), any(), any(), any()) } throws ValidationException("Item not found") - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { assertThrows { cut.moveItem(testItem.hostId, testItem.hostName, 1.0, " ") @@ -202,7 +202,7 @@ class WLSServiceTest { coEvery { orderRepoMock.createOrder(any()) } answers { expectedOrder } coJustRun { storageSystemRepoMock.createOrder(any()) } - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { val order = cut.createOrder(testOrder.toCreateOrderDTO()) @@ -219,7 +219,7 @@ class WLSServiceTest { orderRepoMock.getOrder(testOrder.hostName, testOrder.hostOrderId) } answers { testOrder.copy() } - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { val order = cut.createOrder( @@ -239,7 +239,7 @@ class WLSServiceTest { coEvery { orderRepoMock.getOrder(any(), any()) } answers { null } coEvery { itemRepoMock.doesEveryItemExist(any()) } answers { false } - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { assertThrows { cut.createOrder(testOrder.toCreateOrderDTO()) @@ -254,7 +254,7 @@ class WLSServiceTest { coJustRun { orderRepoMock.deleteOrder(any(), any()) } coJustRun { storageSystemRepoMock.deleteOrder(any(), any()) } - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { cut.deleteOrder(HostName.AXIELL, "12345") @@ -268,7 +268,7 @@ class WLSServiceTest { coEvery { storageSystemRepoMock.deleteOrder(any(), any()) } throws StorageSystemException("Order not found", null) coEvery { orderRepoMock.getOrder(any(), any()) } throws OrderNotFoundException("Order not found") - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { assertThrows { @@ -284,7 +284,7 @@ class WLSServiceTest { coJustRun { storageSystemRepoMock.deleteOrder(any(), any()) } coEvery { orderRepoMock.deleteOrder(any(), any()) } throws OrderNotFoundException("Order not found") - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { assertThrows { @@ -297,7 +297,7 @@ class WLSServiceTest { @Test fun `updateOrder with valid items should complete`() { - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) coEvery { itemRepoMock.doesEveryItemExist(any()) } answers { true } coEvery { orderRepoMock.getOrder(any(), any()) } answers { testOrder.copy() } coEvery { storageSystemRepoMock.updateOrder(any()) } answers { updatedOrder } @@ -324,7 +324,7 @@ class WLSServiceTest { @Test fun `updateOrder should fail when order does not exist`() { - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) coEvery { itemRepoMock.doesEveryItemExist(any()) } answers { true } coEvery { orderRepoMock.getOrder(any(), any()) } throws OrderNotFoundException("Order not found") @@ -349,7 +349,7 @@ class WLSServiceTest { @Test fun `updateOrder should fail when items do not exist`() { - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) coEvery { itemRepoMock.doesEveryItemExist(any()) } answers { false } runTest { @@ -376,7 +376,7 @@ class WLSServiceTest { coEvery { orderRepoMock.getOrder(HostName.AXIELL, "12345") } answers { expectedItem } - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { val order = cut.getOrder(HostName.AXIELL, "12345") assertThat(order).isEqualTo(expectedItem) @@ -387,7 +387,7 @@ class WLSServiceTest { fun `getOrder should return null when order does not exists in DB`() { coEvery { orderRepoMock.getOrder(any(), any()) } answers { null } - val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, inventoryNotifierMock) runTest { val order = cut.getOrder(HostName.AXIELL, "12345") assertThat(order).isNull()