From 2ed3ffc1b80f45938744c61425cae0c34b21a267 Mon Sep 17 00:00:00 2001 From: Noah Bjerkli Aanonli Date: Fri, 4 Oct 2024 15:53:06 +0200 Subject: [PATCH] Unfinished item update from SynQ --- .../hostapi/config/SecurityConfig.kt | 1 - .../synqapi/config/SecurityConfig.kt | 1 - .../synqapi/config/SwaggerConfig.kt | 1 - .../synq/SynqBatchItemUpdatePayload.kt | 19 +++++++++- .../synqapi/synq/SynqController.kt | 18 ++++++++-- .../synq/SynqOrderStatusUpdatePayload.kt | 13 +++---- .../kotlin/no/nb/mlt/wls/domain/WLSService.kt | 11 ++++-- .../mlt/wls/domain/ports/inbound/MoveItem.kt | 11 ++++++ .../domain/ports/outbound/CallbackHandler.kt | 1 - .../callbacks/CallbackHandlerAdapter.kt | 1 - .../repositories/item/MongoItemRepository.kt | 35 +++++++++++-------- .../wls/infrastructure/synq/SynqAdapter.kt | 1 - .../no/nb/mlt/wls/domain/WLSServiceTest.kt | 8 ++--- 13 files changed, 83 insertions(+), 38 deletions(-) diff --git a/src/main/kotlin/no/nb/mlt/wls/application/hostapi/config/SecurityConfig.kt b/src/main/kotlin/no/nb/mlt/wls/application/hostapi/config/SecurityConfig.kt index 35351a53..d427c27d 100644 --- a/src/main/kotlin/no/nb/mlt/wls/application/hostapi/config/SecurityConfig.kt +++ b/src/main/kotlin/no/nb/mlt/wls/application/hostapi/config/SecurityConfig.kt @@ -46,5 +46,4 @@ class SecurityConfig { @Bean @Order(Ordered.LOWEST_PRECEDENCE) fun jwtDecoder(): JwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri) - } diff --git a/src/main/kotlin/no/nb/mlt/wls/application/synqapi/config/SecurityConfig.kt b/src/main/kotlin/no/nb/mlt/wls/application/synqapi/config/SecurityConfig.kt index 4195aaad..9ace32c3 100644 --- a/src/main/kotlin/no/nb/mlt/wls/application/synqapi/config/SecurityConfig.kt +++ b/src/main/kotlin/no/nb/mlt/wls/application/synqapi/config/SecurityConfig.kt @@ -45,5 +45,4 @@ class SecurityConfig { @Bean("synqJwtDecoder") @Order(Ordered.HIGHEST_PRECEDENCE) fun jwtDecoder(): JwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri) - } diff --git a/src/main/kotlin/no/nb/mlt/wls/application/synqapi/config/SwaggerConfig.kt b/src/main/kotlin/no/nb/mlt/wls/application/synqapi/config/SwaggerConfig.kt index 5f0ee673..e1d5d632 100644 --- a/src/main/kotlin/no/nb/mlt/wls/application/synqapi/config/SwaggerConfig.kt +++ b/src/main/kotlin/no/nb/mlt/wls/application/synqapi/config/SwaggerConfig.kt @@ -9,7 +9,6 @@ import io.swagger.v3.oas.models.info.Contact import io.swagger.v3.oas.models.info.Info import io.swagger.v3.oas.models.security.SecurityRequirement import org.springdoc.core.models.GroupedOpenApi -import org.springframework.beans.factory.annotation.Qualifier import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration diff --git a/src/main/kotlin/no/nb/mlt/wls/application/synqapi/synq/SynqBatchItemUpdatePayload.kt b/src/main/kotlin/no/nb/mlt/wls/application/synqapi/synq/SynqBatchItemUpdatePayload.kt index b0b5e5f7..186b574f 100644 --- a/src/main/kotlin/no/nb/mlt/wls/application/synqapi/synq/SynqBatchItemUpdatePayload.kt +++ b/src/main/kotlin/no/nb/mlt/wls/application/synqapi/synq/SynqBatchItemUpdatePayload.kt @@ -1,6 +1,8 @@ package no.nb.mlt.wls.application.synqapi.synq import io.swagger.v3.oas.annotations.media.Schema +import no.nb.mlt.wls.domain.model.HostName +import no.nb.mlt.wls.domain.ports.inbound.MoveItemPayload @Schema( description = "Payload for receiving Item updates in batch from SynQ storage system.", @@ -132,7 +134,7 @@ data class Product( description = "List of attribute values for the product.", example = "[{...}]" ) - val attributeValues: List, + val attributeValue: List, @Schema( description = "Position of the product in the TU, not used by us so it always have default values of '1,1,1'.", example = "{...}" @@ -187,3 +189,18 @@ data class Position( ) val zPosition: Int ) + +fun SynqBatchItemUpdatePayload.mapToItemPayloads(): List { + val list = mutableListOf() + for (product in loadUnit) { + list.add( + MoveItemPayload( + hostId = product.productId, + hostName = HostName.valueOf(product.hostName.uppercase()), + quantity = product.quantityOnHand, + location = location + ) + ) + } + return list +} diff --git a/src/main/kotlin/no/nb/mlt/wls/application/synqapi/synq/SynqController.kt b/src/main/kotlin/no/nb/mlt/wls/application/synqapi/synq/SynqController.kt index 75883f6d..fdec6e01 100644 --- a/src/main/kotlin/no/nb/mlt/wls/application/synqapi/synq/SynqController.kt +++ b/src/main/kotlin/no/nb/mlt/wls/application/synqapi/synq/SynqController.kt @@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses import io.swagger.v3.oas.annotations.tags.Tag import no.nb.mlt.wls.domain.model.Owner +import no.nb.mlt.wls.domain.ports.inbound.MoveItem import no.nb.mlt.wls.domain.ports.inbound.OrderStatusUpdate import org.springframework.http.ResponseEntity import org.springframework.security.core.annotation.AuthenticationPrincipal @@ -22,8 +23,20 @@ import org.springframework.web.bind.annotation.RestController @RequestMapping(path = [ "/synq/v1"]) @Tag(name = "SynQ Controller", description = "API for receiving product and order updates from SynQ in Hermes WLS") class SynqController( + private val moveItem: MoveItem, private val orderStatusUpdate: OrderStatusUpdate ) { + @PutMapping("/item-update") + suspend fun updateItem( + @AuthenticationPrincipal jwt: JwtAuthenticationToken, + @RequestBody synqBatchItemUpdatePayload: SynqBatchItemUpdatePayload + ): ResponseEntity { + for (payload in synqBatchItemUpdatePayload.mapToItemPayloads()) { + val item = moveItem.moveItem(payload) + println(item) + } + return ResponseEntity.ok().build() + } @Operation( summary = "Updates order status based on SynQ order status update", @@ -64,12 +77,11 @@ class SynqController( @PathVariable owner: Owner, @Parameter(description = "Order ID in the storage system") @PathVariable orderId: String - ): ResponseEntity { + ): ResponseEntity { orderStatusUpdate .updateOrderStatus(orderUpdatePayload.hostName, orderId, orderUpdatePayload.getConvertedStatus()) ?: return ResponseEntity.notFound().build() - return ResponseEntity.ok().build() + return ResponseEntity.ok().build() } - } 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 57a96ecd..e235f8a6 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 @@ -53,9 +53,10 @@ enum class SynqOrderStatus { STAGED } -fun SynqOrderStatusUpdatePayload.getConvertedStatus() = when(status) { - SynqOrderStatus.NEW -> Order.Status.NOT_STARTED - SynqOrderStatus.COMPLETED -> Order.Status.COMPLETED - SynqOrderStatus.CANCELLED -> Order.Status.DELETED - else -> Order.Status.IN_PROGRESS -} +fun SynqOrderStatusUpdatePayload.getConvertedStatus() = + when (status) { + SynqOrderStatus.NEW -> Order.Status.NOT_STARTED + SynqOrderStatus.COMPLETED -> Order.Status.COMPLETED + SynqOrderStatus.CANCELLED -> Order.Status.DELETED + else -> Order.Status.IN_PROGRESS + } 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 e6aa59dc..93c387a9 100644 --- a/src/main/kotlin/no/nb/mlt/wls/domain/WLSService.kt +++ b/src/main/kotlin/no/nb/mlt/wls/domain/WLSService.kt @@ -148,9 +148,14 @@ class WLSService( return itemRepository.getItem(hostName, hostId) } - override suspend fun updateOrderStatus(hostName: HostName, hostOrderId: String, status: Order.Status): Order? { - val order = orderRepository.getOrder(hostName, hostOrderId) - ?: throw OrderNotFoundException("No order with hostOrderId: $hostOrderId and hostName: $hostName exists") + override suspend fun updateOrderStatus( + hostName: HostName, + hostOrderId: String, + status: Order.Status + ): Order? { + val order = + orderRepository.getOrder(hostName, hostOrderId) + ?: throw OrderNotFoundException("No order with hostOrderId: $hostOrderId and hostName: $hostName exists") return orderRepository.updateOrder(order.copy(status = status)) } diff --git a/src/main/kotlin/no/nb/mlt/wls/domain/ports/inbound/MoveItem.kt b/src/main/kotlin/no/nb/mlt/wls/domain/ports/inbound/MoveItem.kt index 004ca10d..5549298e 100644 --- a/src/main/kotlin/no/nb/mlt/wls/domain/ports/inbound/MoveItem.kt +++ b/src/main/kotlin/no/nb/mlt/wls/domain/ports/inbound/MoveItem.kt @@ -18,4 +18,15 @@ interface MoveItem { quantity: Double, location: String ): Item + + suspend fun moveItem(moveItemPayload: MoveItemPayload): Item { + return moveItem(moveItemPayload.hostId, moveItemPayload.hostName, moveItemPayload.quantity, moveItemPayload.location) + } } + +data class MoveItemPayload( + val hostId: String, + val hostName: HostName, + val quantity: Double, + val location: String +) 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/CallbackHandler.kt index dbb8b744..58547447 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/CallbackHandler.kt @@ -4,7 +4,6 @@ import no.nb.mlt.wls.domain.model.Item import no.nb.mlt.wls.domain.model.Order interface CallbackHandler { - fun handleItemCallback(item: Item) fun handleOrderCallback(order: Order) diff --git a/src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/CallbackHandlerAdapter.kt b/src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/CallbackHandlerAdapter.kt index d5699539..b92391a8 100644 --- a/src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/CallbackHandlerAdapter.kt +++ b/src/main/kotlin/no/nb/mlt/wls/infrastructure/callbacks/CallbackHandlerAdapter.kt @@ -5,7 +5,6 @@ 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 org.springframework.stereotype.Component -import org.springframework.web.reactive.function.BodyInserters import org.springframework.web.reactive.function.client.WebClient @Component diff --git a/src/main/kotlin/no/nb/mlt/wls/infrastructure/repositories/item/MongoItemRepository.kt b/src/main/kotlin/no/nb/mlt/wls/infrastructure/repositories/item/MongoItemRepository.kt index 2028355e..3377aa98 100644 --- a/src/main/kotlin/no/nb/mlt/wls/infrastructure/repositories/item/MongoItemRepository.kt +++ b/src/main/kotlin/no/nb/mlt/wls/infrastructure/repositories/item/MongoItemRepository.kt @@ -65,20 +65,25 @@ class ItemRepositoryMongoAdapter( quantity: Double, location: String ): Item { - mongoRepo - .findAndUpdateItemByHostNameAndHostId(hostId, hostName, quantity, location) - .timeout(Duration.ofSeconds(8)) - .doOnError { - logger.error(it) { - if (it is TimeoutException) { - "Timed out while updating Item. Order ID: $hostId, Host: $hostName" - } else { - "Error while updating order" + val x = + mongoRepo + .findAndUpdateItemByHostNameAndHostId(hostName, hostId, quantity, location) + .timeout(Duration.ofSeconds(8)) + .doOnError { + logger.error(it) { + if (it is TimeoutException) { + "Timed out while updating Item. Host ID: $hostId, Host: $hostName" + } else { + "Error while updating order" + } } } - } - .onErrorMap { ItemMovingException(it.message ?: "Item could not be moved", it) } - .awaitSingleOrNull() ?: ItemNotFoundException("Item with host ID $hostId for $hostName does not exist in WLS database") + .onErrorMap { ItemMovingException(it.message ?: "Item could not be moved", it) } + .awaitSingle() + + if (x == 0L) { + throw ItemNotFoundException("Item was not found. Host ID: $hostId, Host: $hostName") + } return getItem(hostName, hostId)!! } @@ -94,12 +99,12 @@ interface ItemMongoRepository : ReactiveMongoRepository { @Query(count = true, value = "{ '\$or': ?0 }") fun countItemsMatchingIds(ids: List): Mono - @Query("{hostName: ?0, hostOrderId: ?1}") + @Query("{hostName: ?0,hostId: ?1}") @Update("{'\$set':{quantity: ?2,location: ?3}}") fun findAndUpdateItemByHostNameAndHostId( - hostOrderId: String, hostName: HostName, + hostId: String, quantity: Double, location: String - ): Mono + ): Mono } diff --git a/src/main/kotlin/no/nb/mlt/wls/infrastructure/synq/SynqAdapter.kt b/src/main/kotlin/no/nb/mlt/wls/infrastructure/synq/SynqAdapter.kt index 0a8d0d2d..ae5cb1e3 100644 --- a/src/main/kotlin/no/nb/mlt/wls/infrastructure/synq/SynqAdapter.kt +++ b/src/main/kotlin/no/nb/mlt/wls/infrastructure/synq/SynqAdapter.kt @@ -10,7 +10,6 @@ import no.nb.mlt.wls.domain.ports.outbound.StorageSystemException import no.nb.mlt.wls.domain.ports.outbound.StorageSystemFacade import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component -import org.springframework.web.reactive.function.BodyInserters import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.WebClientResponseException import org.springframework.web.server.ServerErrorException 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 17cf098d..ad8eba8e 100644 --- a/src/test/kotlin/no/nb/mlt/wls/domain/WLSServiceTest.kt +++ b/src/test/kotlin/no/nb/mlt/wls/domain/WLSServiceTest.kt @@ -132,7 +132,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) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) runTest { val movedItem = cut.moveItem(testItem.hostId, testItem.hostName, 1.0, "Somewhere nice") assertThat(movedItem).isEqualTo(expectedItem) @@ -147,7 +147,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) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) runTest { assertThrows { cut.moveItem(testItem.hostId, testItem.hostName, 1.0, "Somewhere nice") @@ -163,7 +163,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) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) runTest { assertThrows { cut.moveItem(testItem.hostId, testItem.hostName, -1.0, "Somewhere nice") @@ -179,7 +179,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) + val cut = WLSService(itemRepoMock, orderRepoMock, storageSystemRepoMock, callbackHandlerMock) runTest { assertThrows { cut.moveItem(testItem.hostId, testItem.hostName, 1.0, " ")