From d33bb33ff0ad51b33dd97eb6946d1b68b8966bd1 Mon Sep 17 00:00:00 2001 From: siarhei_hrabko Date: Thu, 24 Aug 2023 16:59:36 +0300 Subject: [PATCH 1/6] MODORDERS-344 poline update batch API --- descriptors/ModuleDescriptor-template.json | 19 +++-- ramls/po-line-batch.raml | 52 ++++++++++++++ .../org/folio/config/ApplicationConfig.java | 5 ++ .../event/service/AuditOutboxService.java | 23 ++++--- .../org/folio/rest/impl/PoLineBatchAPI.java | 69 +++++++++++++++++++ .../java/org/folio/rest/impl/PoLinesAPI.java | 9 +-- .../services/lines/PoLinesBatchService.java | 59 ++++++++++++++++ .../folio/services/lines/PoLinesService.java | 10 ++- .../services/lines/PoLinesServiceTest.java | 2 +- 9 files changed, 225 insertions(+), 23 deletions(-) create mode 100644 ramls/po-line-batch.raml create mode 100644 src/main/java/org/folio/rest/impl/PoLineBatchAPI.java create mode 100644 src/main/java/org/folio/services/lines/PoLinesBatchService.java diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 524760e4..c270d14c 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -97,7 +97,7 @@ }, { "id": "orders-storage.po-lines", - "version": "12.1", + "version": "12.2", "handlers": [ { "methods": ["GET"], @@ -128,6 +128,11 @@ "methods": ["PATCH"], "pathPattern": "/orders-storage/po-lines/{id}", "permissionsRequired": ["orders-storage.po-lines.item.patch"] + }, + { + "methods": ["PUT"], + "pathPattern": "/orders-storage/po-lines-batch", + "permissionsRequired": ["orders-storage.po-lines-batch.collection.put"] } ] }, @@ -696,8 +701,13 @@ }, { "permissionName" : "orders-storage.po-lines.item.patch", - "displayName" : "po-line-item parch", - "description" : "Parch a po line" + "displayName" : "po-line-item patch", + "description" : "Patch a po line" + }, + { + "permissionName" : "orders-storage.po-lines-batch.collection.put", + "displayName" : "PO Lines batch update", + "description" : "Update a set of PO lines in a batch" }, { "permissionName" : "orders-storage.po-lines.all", @@ -709,7 +719,8 @@ "orders-storage.po-lines.item.get", "orders-storage.po-lines.item.put", "orders-storage.po-lines.item.delete", - "orders-storage.po-lines.item.patch" + "orders-storage.po-lines.item.patch", + "orders-storage.po-lines-batch.collection.put" ] }, { diff --git a/ramls/po-line-batch.raml b/ramls/po-line-batch.raml new file mode 100644 index 00000000..02c924bb --- /dev/null +++ b/ramls/po-line-batch.raml @@ -0,0 +1,52 @@ +#%RAML 1.0 +title: "mod-orders" +baseUri: http://github.com/folio-org/mod-orders-storage +version: v9.3 + +documentation: + - title: PO Line + content: This module implements the CRUD interface. This API is intended for internal use only. Please use the /orders/order-lines API provided by mod-orders instead. + +types: + po-line-collection: !include acq-models/mod-orders-storage/schemas/po_line_collection.json + UUID: + type: string + pattern: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$ + +resourceTypes: + collection: !include raml-util/rtypes/collection.raml + collection-item: !include raml-util/rtypes/item-collection.raml +traits: + + +/orders-storage/po-lines-batch: + displayName: Finance release encumbrance + description: Finance release encumbrance APIs + put: + description: "asd" + body: + application/json: + type: po-line-collection + example: + strict: false + value: !include acq-models/mod-orders-storage/examples/po_line_collection.sample + responses: + 204: + description: "Item successfully updated" + 404: + description: "Item with a given ID not found" + body: + text/plain: + example: "Bad request" + 400: + description: "Bad request, e.g. malformed request body or query parameter. Details of the error (e.g. name of the parameter or line/character number with malformed data) provided in the response." + body: + text/plain: + example: "unable to update <> -- malformed JSON at 13:4" + 500: + description: "Internal server error, e.g. due to misconfiguration" + body: + text/plain: + example: "internal server error, contact administrator" + + diff --git a/src/main/java/org/folio/config/ApplicationConfig.java b/src/main/java/org/folio/config/ApplicationConfig.java index afcedaa7..eb4678d2 100644 --- a/src/main/java/org/folio/config/ApplicationConfig.java +++ b/src/main/java/org/folio/config/ApplicationConfig.java @@ -19,6 +19,7 @@ import org.folio.rest.jaxrs.model.CreateInventoryType; import org.folio.rest.jaxrs.model.OrderLinePatchOperationType; import org.folio.services.lines.PoLineNumbersService; +import org.folio.services.lines.PoLinesBatchService; import org.folio.services.lines.PoLinesService; import org.folio.services.order.ExportHistoryService; import org.folio.orders.lines.update.OrderLinePatchOperationService; @@ -55,6 +56,10 @@ OrderDAO orderDAO() { PoLinesService poLinesService(PoLinesDAO poLinesDAO, AuditOutboxService auditOutboxService) { return new PoLinesService(poLinesDAO, auditOutboxService); } + @Bean + PoLinesBatchService poLinesBatchService(AuditOutboxService auditOutboxService, PoLinesService poLinesService) { + return new PoLinesBatchService(auditOutboxService, poLinesService); + } @Bean PieceService pieceService() { diff --git a/src/main/java/org/folio/event/service/AuditOutboxService.java b/src/main/java/org/folio/event/service/AuditOutboxService.java index 0896c04d..21c8f28a 100644 --- a/src/main/java/org/folio/event/service/AuditOutboxService.java +++ b/src/main/java/org/folio/event/service/AuditOutboxService.java @@ -98,20 +98,27 @@ public Future saveOrderOutboxLog(Conn conn, PurchaseOrder entity, Order } /** - * Saves order line outbox log. + * Saves order lines outbox logs. * * @param conn connection in transaction - * @param poLine the poLine + * @param poLines the poLine * @param action action for order line * @param okapiHeaders the okapi headers * @return future with saved outbox log in the same transaction */ - public Future saveOrderLineOutboxLog(Conn conn, PoLine poLine, OrderLineAuditEvent.Action action, Map okapiHeaders) { - log.trace("saveOrderLineOutboxLog, po line id={}", poLine.getId()); - String orderLine = Json.encode(poLine); - return saveOutboxLog(conn, action.value(), EntityType.ORDER_LINE, orderLine, okapiHeaders) - .onSuccess(reply -> log.info("Outbox log has been saved for order line id: {}", poLine.getId())) - .onFailure(e -> log.warn("Could not save outbox audit log for order line with id {}", poLine.getId(), e)); + public Future saveOrderLinesOutboxLogs(Conn conn, List poLines, OrderLineAuditEvent.Action action, Map okapiHeaders) { + var futures = poLines.stream().map(poLine -> { + log.trace("saveOrderLineOutboxLog, po line id={}", poLine.getId()); + String orderLine = Json.encode(poLine); + return saveOutboxLog(conn, action.value(), EntityType.ORDER_LINE, orderLine, okapiHeaders) + .onSuccess(reply -> log.info("Outbox log has been saved for order line id: {}", poLine.getId())) + .onFailure(e -> log.warn("Could not save outbox audit log for order line with id {}", poLine.getId(), e)); + }) + .toList(); + + return GenericCompositeFuture.join(futures) + .map(res -> true) + .otherwise(t -> false); } private List> getKafkaFutures(List eventLogs, Map okapiHeaders) { diff --git a/src/main/java/org/folio/rest/impl/PoLineBatchAPI.java b/src/main/java/org/folio/rest/impl/PoLineBatchAPI.java new file mode 100644 index 00000000..62715013 --- /dev/null +++ b/src/main/java/org/folio/rest/impl/PoLineBatchAPI.java @@ -0,0 +1,69 @@ +package org.folio.rest.impl; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.ws.rs.core.Response; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.dao.PostgresClientFactory; +import org.folio.event.service.AuditOutboxService; +import org.folio.rest.core.BaseApi; +import org.folio.rest.jaxrs.model.PoLine; +import org.folio.rest.jaxrs.model.PoLineCollection; +import org.folio.rest.jaxrs.resource.OrdersStoragePoLinesBatch; +import org.folio.rest.persist.HelperUtils; +import org.folio.rest.persist.PostgresClient; +import org.folio.services.lines.PoLinesBatchService; +import org.folio.spring.SpringContextUtil; +import org.springframework.beans.factory.annotation.Autowired; + +import io.vertx.core.AsyncResult; +import io.vertx.core.Context; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; + +public class PoLineBatchAPI extends BaseApi implements OrdersStoragePoLinesBatch { + private static final Logger log = LogManager.getLogger(); + private final PostgresClient pgClient; + @Autowired + PoLinesBatchService poLinesBatchService; + @Autowired + private PostgresClientFactory pgClientFactory; + @Autowired + private AuditOutboxService auditOutboxService; + + public PoLineBatchAPI(Vertx vertx, String tenantId) { + SpringContextUtil.autowireDependencies(this, Vertx.currentContext()); + pgClient = pgClientFactory.createInstance(tenantId); + } + + @Override + public void putOrdersStoragePoLinesBatch(PoLineCollection poLineCollection, Map okapiHeaders, + Handler> asyncResultHandler, Context vertxContext) { + poLinesBatchService.poLinesBatchUpdate(poLineCollection.getPoLines(), pgClient, okapiHeaders, vertxContext) + .onComplete(ar -> { + if (ar.failed()) { + log.error(getPoLineIdsForLogMessage(poLineCollection.getPoLines()), ar.cause()); + asyncResultHandler.handle(buildErrorResponse(ar.cause())); + } else { + log.info(getPoLineIdsForLogMessage(poLineCollection.getPoLines())); + auditOutboxService.processOutboxEventLogs(okapiHeaders); + asyncResultHandler.handle(buildNoContentResponse()); + } + }); + } + + @Override + protected String getEndpoint(Object entity) { + return HelperUtils.getEndpoint(OrdersStoragePoLinesBatch.class); + } + + private String getPoLineIdsForLogMessage(List polines) { + return "putOrdersStoragePoLinesBatch completed, PO line ids: " + polines.stream() + .map(PoLine::getId) + .collect(Collectors.joining(", ")); + } +} diff --git a/src/main/java/org/folio/rest/impl/PoLinesAPI.java b/src/main/java/org/folio/rest/impl/PoLinesAPI.java index 79125b58..00d323be 100644 --- a/src/main/java/org/folio/rest/impl/PoLinesAPI.java +++ b/src/main/java/org/folio/rest/impl/PoLinesAPI.java @@ -6,6 +6,7 @@ import org.folio.event.service.AuditOutboxService; import static org.folio.models.TableNames.PO_LINE_TABLE; +import java.util.List; import java.util.Map; import javax.ws.rs.core.Response; @@ -65,7 +66,7 @@ public void postOrdersStoragePoLines(PoLine poLine, Map okapiHea Handler> asyncResultHandler, Context vertxContext) { if (Boolean.TRUE.equals(poLine.getIsPackage())) { pgClient.withTrans(conn -> poLinesService.createPoLine(conn, poLine) - .compose(poLineId -> auditOutboxService.saveOrderLineOutboxLog(conn, poLine, OrderLineAuditEvent.Action.CREATE, okapiHeaders) + .compose(poLineId -> auditOutboxService.saveOrderLinesOutboxLogs(conn, List.of(poLine), OrderLineAuditEvent.Action.CREATE, okapiHeaders) .map(b -> poLineId))) .onComplete(ar -> { if (ar.failed()) { @@ -89,7 +90,7 @@ private void createPoLineWithTitle(PoLine poLine, Handler> log.trace("createPoLineWithTitle, poLineId={}, poLineNumber={}", poLine.getId(), poLine.getPoLineNumber()); pgClient.withTrans(conn -> poLinesService.createPoLine(conn, poLine) .compose(poLineId -> poLinesService.createTitle(conn, poLine)) - .compose(title -> auditOutboxService.saveOrderLineOutboxLog(conn, poLine, OrderLineAuditEvent.Action.CREATE, okapiHeaders))) + .compose(title -> auditOutboxService.saveOrderLinesOutboxLogs(conn, List.of(poLine), OrderLineAuditEvent.Action.CREATE, okapiHeaders))) .onComplete(ar -> { if (ar.failed()) { log.error("Order Line and Title creation failed, poLine={}", @@ -142,7 +143,7 @@ public void putOrdersStoragePoLinesById(String id, PoLine poLine, Map> asyncResultHandler, Context vertxContext) { if (Boolean.TRUE.equals(poLine.getIsPackage())) { pgClient.withTrans(conn -> poLinesService.updatePoLine(conn, poLine) - .compose(line -> auditOutboxService.saveOrderLineOutboxLog(conn, line, OrderLineAuditEvent.Action.EDIT, okapiHeaders))) + .compose(line -> auditOutboxService.saveOrderLinesOutboxLogs(conn, List.of(line), OrderLineAuditEvent.Action.EDIT, okapiHeaders))) .onComplete(ar -> { if (ar.failed()) { log.error("Update package order line failed, id={}, poLine={}", id, @@ -156,7 +157,7 @@ public void putOrdersStoragePoLinesById(String id, PoLine poLine, Map poLinesService.updatePoLineWithTitle(conn, id, poLine, new RequestContext(vertxContext, okapiHeaders))) .onComplete(ar -> { if (ar.failed()) { log.error("Update order line with title failed, id={}, poLine={}", id, diff --git a/src/main/java/org/folio/services/lines/PoLinesBatchService.java b/src/main/java/org/folio/services/lines/PoLinesBatchService.java new file mode 100644 index 00000000..eb4b5ecc --- /dev/null +++ b/src/main/java/org/folio/services/lines/PoLinesBatchService.java @@ -0,0 +1,59 @@ +package org.folio.services.lines; + +import static org.folio.models.TableNames.PO_LINE_TABLE; + +import java.util.List; +import java.util.Map; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.event.service.AuditOutboxService; +import org.folio.okapi.common.GenericCompositeFuture; +import org.folio.rest.core.models.RequestContext; +import org.folio.rest.jaxrs.model.OrderLineAuditEvent; +import org.folio.rest.jaxrs.model.PoLine; +import org.folio.rest.persist.Conn; +import org.folio.rest.persist.PostgresClient; + +import io.vertx.core.CompositeFuture; +import io.vertx.core.Context; +import io.vertx.core.Future; + +public class PoLinesBatchService { + private static final Logger log = LogManager.getLogger(); + private final AuditOutboxService auditOutboxService; + private final PoLinesService poLinesService; + + + public PoLinesBatchService(AuditOutboxService auditOutboxService, PoLinesService poLinesService) { + this.auditOutboxService = auditOutboxService; + this.poLinesService = poLinesService; + } + + public Future poLinesBatchUpdate(List poLines, PostgresClient pgClient, Map okapiHeaders, + Context vertxContext) { + + if (CollectionUtils.isEmpty(poLines)) { + log.warn("poLinesBatchUpdate:: po line list is empty"); + return Future.succeededFuture(); + } + + return pgClient.withTrans(conn -> + conn.updateBatch(PO_LINE_TABLE, poLines) + .compose(rowSet -> updatePoLinesWithTitle(conn, poLines, okapiHeaders, vertxContext)) + .compose(rowSet -> auditOutboxService.saveOrderLinesOutboxLogs(conn, poLines, OrderLineAuditEvent.Action.EDIT, okapiHeaders)) + .mapEmpty() + ); + + } + + private CompositeFuture updatePoLinesWithTitle(Conn conn, List poLines, Map okapiHeaders, Context vertxContext) { + var futures = poLines.stream() + .filter(poLine -> !poLine.getIsPackage()) + .map(poLine -> poLinesService.updatePoLineWithTitle(conn, poLine.getId(), poLine, new RequestContext(vertxContext, okapiHeaders))) + .toList(); + return GenericCompositeFuture.join(futures); + } + +} diff --git a/src/main/java/org/folio/services/lines/PoLinesService.java b/src/main/java/org/folio/services/lines/PoLinesService.java index 8bfcce90..4575c49d 100644 --- a/src/main/java/org/folio/services/lines/PoLinesService.java +++ b/src/main/java/org/folio/services/lines/PoLinesService.java @@ -101,19 +101,17 @@ public Future deleteById(String id, RequestContext requestContext) { return promise.future(); } - public Future updatePoLineWithTitle(String id, PoLine poLine, RequestContext requestContext) { + public Future updatePoLineWithTitle(Conn conn, String id, PoLine poLine, RequestContext requestContext) { Map okapiHeaders = requestContext.getHeaders(); - DBClient client = requestContext.toDBClient(); Promise promise = Promise.promise(); poLine.setId(id); - client.getPgClient().withTrans(conn -> updatePoLine(conn, poLine) + updatePoLine(conn, poLine) .compose(line -> updateTitle(conn, line)) - .compose(line -> auditOutboxService.saveOrderLineOutboxLog(conn, line, OrderLineAuditEvent.Action.EDIT, okapiHeaders))) + .compose(line -> auditOutboxService.saveOrderLinesOutboxLogs(conn, List.of(line), OrderLineAuditEvent.Action.EDIT, okapiHeaders)) .onComplete(ar -> { if (ar.succeeded()) { log.info("POLine and associated data were successfully updated, id={}", id); - auditOutboxService.processOutboxEventLogs(okapiHeaders); promise.complete(null); } else { log.error("updatePoLineWithTitle failed, id={}, poLine={}", id, @@ -467,7 +465,7 @@ private void populateTitleBasedOnPackagePoLine(Title title, PoLine packagePoLine } } - private Future updateTitle(Conn conn, PoLine poLine) { + public Future updateTitle(Conn conn, PoLine poLine) { Criterion criterion = getCriteriaByFieldNameAndValueNotJsonb(POLINE_ID_FIELD, poLine.getId()); return conn.get(TITLES_TABLE, Title.class, criterion, true) diff --git a/src/test/java/org/folio/services/lines/PoLinesServiceTest.java b/src/test/java/org/folio/services/lines/PoLinesServiceTest.java index aba60d3c..814f46bf 100644 --- a/src/test/java/org/folio/services/lines/PoLinesServiceTest.java +++ b/src/test/java/org/folio/services/lines/PoLinesServiceTest.java @@ -421,7 +421,7 @@ public void shouldFailUpdatePoLineWithTitleWhenUpdatingTitle() { doReturn(succeededFuture(results)) .when(conn).get(anyString(), eq(Title.class), any(Criterion.class), anyBoolean()); - Future f = poLinesService.updatePoLineWithTitle(poLineId, poLine, requestContext); + Future f = poLinesService.updatePoLineWithTitle(conn, poLineId, poLine, requestContext); assertThat(f.failed(), is(true)); io.vertx.ext.web.handler.HttpException thrown = (io.vertx.ext.web.handler.HttpException)f.cause(); From 2b4295bc51c2a0e4407edaf6b75791a2fed26408 Mon Sep 17 00:00:00 2001 From: siarhei_hrabko Date: Tue, 29 Aug 2023 10:47:56 +0300 Subject: [PATCH 2/6] MODORDERS-344 code coverage --- ramls/po-line-batch.raml | 19 ++---- .../folio/rest/impl/PoLineBatchAPITest.java | 62 +++++++++++++++++++ 2 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java diff --git a/ramls/po-line-batch.raml b/ramls/po-line-batch.raml index 02c924bb..ca8c0798 100644 --- a/ramls/po-line-batch.raml +++ b/ramls/po-line-batch.raml @@ -4,8 +4,8 @@ baseUri: http://github.com/folio-org/mod-orders-storage version: v9.3 documentation: - - title: PO Line - content: This module implements the CRUD interface. This API is intended for internal use only. Please use the /orders/order-lines API provided by mod-orders instead. + - title: PO Line batch + content: This module implements the PO lines batch processing interface. This API is intended for internal use only. types: po-line-collection: !include acq-models/mod-orders-storage/schemas/po_line_collection.json @@ -20,10 +20,10 @@ traits: /orders-storage/po-lines-batch: - displayName: Finance release encumbrance - description: Finance release encumbrance APIs + displayName: Process list of PO lines in a batch + description: Process list of PO lines in a batch APIs put: - description: "asd" + description: "Update the list of PO lines in a batch" body: application/json: type: po-line-collection @@ -32,12 +32,7 @@ traits: value: !include acq-models/mod-orders-storage/examples/po_line_collection.sample responses: 204: - description: "Item successfully updated" - 404: - description: "Item with a given ID not found" - body: - text/plain: - example: "Bad request" + description: "Collection successfully updated" 400: description: "Bad request, e.g. malformed request body or query parameter. Details of the error (e.g. name of the parameter or line/character number with malformed data) provided in the response." body: @@ -48,5 +43,3 @@ traits: body: text/plain: example: "internal server error, contact administrator" - - diff --git a/src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java b/src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java new file mode 100644 index 00000000..74683807 --- /dev/null +++ b/src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java @@ -0,0 +1,62 @@ +package org.folio.rest.impl; + +import static io.restassured.RestAssured.given; +import static org.folio.StorageTestSuite.storageUrl; +import static org.folio.rest.utils.TestEntities.PO_LINE; +import static org.folio.rest.utils.TestEntities.PURCHASE_ORDER; +import static org.folio.rest.utils.TestEntities.TITLES; + +import java.net.MalformedURLException; +import java.util.UUID; + +import org.apache.commons.lang3.tuple.Pair; +import org.folio.rest.jaxrs.model.PoLineCollection; +import org.folio.rest.utils.IsolatedTenant; +import org.folio.rest.utils.TestData; +import org.junit.jupiter.api.Test; + +import io.restassured.http.ContentType; +import io.restassured.http.Headers; +import io.vertx.core.json.Json; + +@IsolatedTenant +class PoLineBatchAPITest extends TestBase { + + private static final String PO_LINES_BATCH_ENDPOINT = "/orders-storage/po-lines-batch"; + @Test + void putOrdersStoragePoLinesBatchSuccess() throws MalformedURLException { + String userId = UUID.randomUUID().toString(); + Headers headers = getDikuTenantHeaders(userId); + + givenTestData(Pair.of(PURCHASE_ORDER, TestData.PurchaseOrder.DEFAULT), + Pair.of(PO_LINE, TestData.PoLine.DEFAULT), + Pair.of(TITLES, TestData.Title.DEFAULT)); + + String polinesAsString = Json.encode(new PoLineCollection().withTotalRecords(0)); + given() + .headers(headers) + .contentType(ContentType.JSON) + .body(polinesAsString) + .put(storageUrl(PO_LINES_BATCH_ENDPOINT)) + .then() + .statusCode(204); + } + + + @Test + void putOrdersStorageEmptyPoLinesBatch() throws MalformedURLException { + String userId = UUID.randomUUID().toString(); + Headers headers = getDikuTenantHeaders(userId); + + String polinesAsString = Json.encode(new PoLineCollection().withTotalRecords(0)); + + given() + .headers(headers) + .contentType(ContentType.JSON) + .body(polinesAsString) + .put(storageUrl(PO_LINES_BATCH_ENDPOINT)) + .then() + .statusCode(204); + } + +} From b04d419d6aa85f24a8c61a8b67ea9ca200853f26 Mon Sep 17 00:00:00 2001 From: siarhei_hrabko Date: Tue, 29 Aug 2023 11:04:46 +0300 Subject: [PATCH 3/6] MODORDERS-344 code coverage --- src/test/java/org/folio/StorageTestSuite.java | 6 ++++++ src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/folio/StorageTestSuite.java b/src/test/java/org/folio/StorageTestSuite.java index 5db67965..f318373c 100644 --- a/src/test/java/org/folio/StorageTestSuite.java +++ b/src/test/java/org/folio/StorageTestSuite.java @@ -30,6 +30,7 @@ import org.folio.rest.impl.EntitiesCrudTest; import org.folio.rest.impl.HelperUtilsTest; import org.folio.rest.impl.OrdersAPITest; +import org.folio.rest.impl.PoLineBatchAPITest; import org.folio.rest.impl.PoNumberTest; import org.folio.rest.impl.PurchaseOrderLineNumberTest; import org.folio.rest.impl.PurchaseOrderLinesApiTest; @@ -263,4 +264,9 @@ class TitleServiceTestNested extends TitleServiceTest {} class OrderLineUpdateInstanceHandlerTestNested extends OrderLineUpdateInstanceHandlerTest {} @Nested class PoLIneServiceVertxTestNested extends PoLIneServiceVertxTest {} + + @Nested + class PoLineBatchAPITestNested extends PoLineBatchAPITest {} + + } diff --git a/src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java b/src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java index 74683807..d339cf4e 100644 --- a/src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java +++ b/src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java @@ -20,7 +20,7 @@ import io.vertx.core.json.Json; @IsolatedTenant -class PoLineBatchAPITest extends TestBase { +public class PoLineBatchAPITest extends TestBase { private static final String PO_LINES_BATCH_ENDPOINT = "/orders-storage/po-lines-batch"; @Test From 95930e8bd958b708db2cf6234edc94d1a96dc79a Mon Sep 17 00:00:00 2001 From: siarhei_hrabko Date: Tue, 29 Aug 2023 14:13:18 +0300 Subject: [PATCH 4/6] MODORDERS-344 code coverage --- .../folio/rest/impl/PoLineBatchAPITest.java | 88 +++++++++++++++---- 1 file changed, 73 insertions(+), 15 deletions(-) diff --git a/src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java b/src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java index d339cf4e..1ebe1af6 100644 --- a/src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java +++ b/src/test/java/org/folio/rest/impl/PoLineBatchAPITest.java @@ -4,15 +4,17 @@ import static org.folio.StorageTestSuite.storageUrl; import static org.folio.rest.utils.TestEntities.PO_LINE; import static org.folio.rest.utils.TestEntities.PURCHASE_ORDER; -import static org.folio.rest.utils.TestEntities.TITLES; import java.net.MalformedURLException; +import java.util.List; import java.util.UUID; import org.apache.commons.lang3.tuple.Pair; +import org.folio.rest.jaxrs.model.PoLine; import org.folio.rest.jaxrs.model.PoLineCollection; import org.folio.rest.utils.IsolatedTenant; import org.folio.rest.utils.TestData; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.restassured.http.ContentType; @@ -26,30 +28,87 @@ public class PoLineBatchAPITest extends TestBase { @Test void putOrdersStoragePoLinesBatchSuccess() throws MalformedURLException { String userId = UUID.randomUUID().toString(); - Headers headers = getDikuTenantHeaders(userId); + Headers headers = getIsolatedTenantHeaders(userId); - givenTestData(Pair.of(PURCHASE_ORDER, TestData.PurchaseOrder.DEFAULT), - Pair.of(PO_LINE, TestData.PoLine.DEFAULT), - Pair.of(TITLES, TestData.Title.DEFAULT)); + givenTestData(Pair.of(PURCHASE_ORDER, TestData.PurchaseOrder.DEFAULT)); + + // prepare sample data + var samplePoline = Json.decodeValue(getFile(TestData.PoLine.DEFAULT), PoLine.class); + + var poLineId1 = UUID.randomUUID().toString(); + var poLine1asString = Json.encode(samplePoline + .withId(poLineId1) + .withIsPackage(true) + .withPoLineNumber("52590-10")); + + var poLineId2 = UUID.randomUUID().toString(); + var poLine2asString = Json.encode(samplePoline + .withId(poLineId2) + .withIsPackage(false) + .withPackagePoLineId(poLineId1) + .withPoLineNumber("52590-11")); + + var poLineId3 = UUID.randomUUID().toString(); + var poLine3asString = Json.encode(samplePoline + .withId(poLineId3) + .withIsPackage(false) + .withPackagePoLineId(poLineId1) + .withPoLineNumber("52590-12")); + + var packagePoLineCreated = postData(PO_LINE.getEndpoint(), poLine1asString, headers).as(PoLine.class); + var poLine2Created = postData(PO_LINE.getEndpoint(), poLine2asString, headers).as(PoLine.class); + var poLine3Created = postData(PO_LINE.getEndpoint(), poLine3asString, headers).as(PoLine.class); + // end sample data + + packagePoLineCreated.setTitleOrPackage("testPackageName"); + poLine2Created.setTitleOrPackage("newTestTitleName2"); + poLine3Created.setTitleOrPackage("newTestTitleName3"); + + String polinesAsString = Json.encode(new PoLineCollection() + .withPoLines(List.of(packagePoLineCreated, poLine2Created, poLine3Created)) + .withTotalRecords(3) + ); + + // update po lines in a batch + putBatchPoLines(headers, polinesAsString); + + var updatedLine1 = getDataById(PO_LINE.getEndpointWithId(), packagePoLineCreated.getId(), ISOLATED_TENANT_HEADER) + .then() + .extract() + .as(PoLine.class); + + var updatedLine2 = getDataById(PO_LINE.getEndpointWithId(), poLine2Created.getId(), ISOLATED_TENANT_HEADER) + .then() + .statusCode(200) + .extract() + .as(PoLine.class); + + var updatedLine3 = getDataById(PO_LINE.getEndpointWithId(), poLine3Created.getId(), ISOLATED_TENANT_HEADER) + .then() + .extract() + .as(PoLine.class); + + // check po lines updated + Assertions.assertEquals("testPackageName", updatedLine1.getTitleOrPackage()); + Assertions.assertEquals("newTestTitleName2", updatedLine2.getTitleOrPackage()); + Assertions.assertEquals("newTestTitleName3", updatedLine3.getTitleOrPackage()); - String polinesAsString = Json.encode(new PoLineCollection().withTotalRecords(0)); - given() - .headers(headers) - .contentType(ContentType.JSON) - .body(polinesAsString) - .put(storageUrl(PO_LINES_BATCH_ENDPOINT)) - .then() - .statusCode(204); } + @Test void putOrdersStorageEmptyPoLinesBatch() throws MalformedURLException { String userId = UUID.randomUUID().toString(); - Headers headers = getDikuTenantHeaders(userId); + Headers headers = getIsolatedTenantHeaders(userId); String polinesAsString = Json.encode(new PoLineCollection().withTotalRecords(0)); + putBatchPoLines(headers, polinesAsString); + } + + + private void putBatchPoLines(Headers headers, String polinesAsString) throws MalformedURLException { given() .headers(headers) .contentType(ContentType.JSON) @@ -58,5 +117,4 @@ void putOrdersStorageEmptyPoLinesBatch() throws MalformedURLException { .then() .statusCode(204); } - } From e483c566e7c0ee2c328a145646f7a2537d38651d Mon Sep 17 00:00:00 2001 From: siarhei_hrabko Date: Fri, 1 Sep 2023 15:48:02 +0300 Subject: [PATCH 5/6] MODORDERS-344 code coverage --- .../java/org/folio/rest/impl/PoLineBatchAPI.java | 6 +++--- .../folio/services/lines/PoLinesBatchService.java | 13 ++++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/folio/rest/impl/PoLineBatchAPI.java b/src/main/java/org/folio/rest/impl/PoLineBatchAPI.java index 62715013..92deaadc 100644 --- a/src/main/java/org/folio/rest/impl/PoLineBatchAPI.java +++ b/src/main/java/org/folio/rest/impl/PoLineBatchAPI.java @@ -46,10 +46,10 @@ public void putOrdersStoragePoLinesBatch(PoLineCollection poLineCollection, Map< poLinesBatchService.poLinesBatchUpdate(poLineCollection.getPoLines(), pgClient, okapiHeaders, vertxContext) .onComplete(ar -> { if (ar.failed()) { - log.error(getPoLineIdsForLogMessage(poLineCollection.getPoLines()), ar.cause()); + log.error("putOrdersStoragePoLinesBatch:: failed, PO line ids: {} ", getPoLineIdsForLogMessage(poLineCollection.getPoLines()), ar.cause()); asyncResultHandler.handle(buildErrorResponse(ar.cause())); } else { - log.info(getPoLineIdsForLogMessage(poLineCollection.getPoLines())); + log.info("putOrdersStoragePoLinesBatch:: completed, PO line ids: {} ", getPoLineIdsForLogMessage(poLineCollection.getPoLines())); auditOutboxService.processOutboxEventLogs(okapiHeaders); asyncResultHandler.handle(buildNoContentResponse()); } @@ -62,7 +62,7 @@ protected String getEndpoint(Object entity) { } private String getPoLineIdsForLogMessage(List polines) { - return "putOrdersStoragePoLinesBatch completed, PO line ids: " + polines.stream() + return polines.stream() .map(PoLine::getId) .collect(Collectors.joining(", ")); } diff --git a/src/main/java/org/folio/services/lines/PoLinesBatchService.java b/src/main/java/org/folio/services/lines/PoLinesBatchService.java index eb4b5ecc..544d4ca9 100644 --- a/src/main/java/org/folio/services/lines/PoLinesBatchService.java +++ b/src/main/java/org/folio/services/lines/PoLinesBatchService.java @@ -10,13 +10,11 @@ import org.apache.logging.log4j.Logger; import org.folio.event.service.AuditOutboxService; import org.folio.okapi.common.GenericCompositeFuture; -import org.folio.rest.core.models.RequestContext; import org.folio.rest.jaxrs.model.OrderLineAuditEvent; import org.folio.rest.jaxrs.model.PoLine; import org.folio.rest.persist.Conn; import org.folio.rest.persist.PostgresClient; -import io.vertx.core.CompositeFuture; import io.vertx.core.Context; import io.vertx.core.Future; @@ -25,10 +23,10 @@ public class PoLinesBatchService { private final AuditOutboxService auditOutboxService; private final PoLinesService poLinesService; - public PoLinesBatchService(AuditOutboxService auditOutboxService, PoLinesService poLinesService) { this.auditOutboxService = auditOutboxService; this.poLinesService = poLinesService; + } public Future poLinesBatchUpdate(List poLines, PostgresClient pgClient, Map okapiHeaders, @@ -41,19 +39,20 @@ public Future poLinesBatchUpdate(List poLines, PostgresClient pgCl return pgClient.withTrans(conn -> conn.updateBatch(PO_LINE_TABLE, poLines) - .compose(rowSet -> updatePoLinesWithTitle(conn, poLines, okapiHeaders, vertxContext)) + .compose(rowSet -> updatePoLinesWithTitle(conn, poLines)) .compose(rowSet -> auditOutboxService.saveOrderLinesOutboxLogs(conn, poLines, OrderLineAuditEvent.Action.EDIT, okapiHeaders)) .mapEmpty() ); } - private CompositeFuture updatePoLinesWithTitle(Conn conn, List poLines, Map okapiHeaders, Context vertxContext) { + private Future updatePoLinesWithTitle(Conn conn, List poLines) { var futures = poLines.stream() .filter(poLine -> !poLine.getIsPackage()) - .map(poLine -> poLinesService.updatePoLineWithTitle(conn, poLine.getId(), poLine, new RequestContext(vertxContext, okapiHeaders))) + .map(poLine -> poLinesService.updateTitle(conn, poLine)) .toList(); - return GenericCompositeFuture.join(futures); + return GenericCompositeFuture.join(futures) + .mapEmpty(); } } From f0088ca88d20a9f6f2137c240a3ea6ae03a2f37d Mon Sep 17 00:00:00 2001 From: siarhei_hrabko Date: Mon, 4 Sep 2023 16:16:03 +0300 Subject: [PATCH 6/6] MODORDERS-344 fixed comments --- .../event/service/AuditOutboxService.java | 18 ++++++++++-------- .../services/lines/PoLinesBatchService.java | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/folio/event/service/AuditOutboxService.java b/src/main/java/org/folio/event/service/AuditOutboxService.java index 21c8f28a..a661b224 100644 --- a/src/main/java/org/folio/event/service/AuditOutboxService.java +++ b/src/main/java/org/folio/event/service/AuditOutboxService.java @@ -1,7 +1,11 @@ package org.folio.event.service; -import io.vertx.core.Future; -import io.vertx.core.json.Json; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + import org.apache.commons.collections4.CollectionUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -19,11 +23,8 @@ import org.folio.rest.persist.PostgresClient; import org.folio.rest.tools.utils.TenantTool; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.stream.Collectors; +import io.vertx.core.Future; +import io.vertx.core.json.Json; public class AuditOutboxService { private static final Logger log = LogManager.getLogger(); @@ -107,7 +108,8 @@ public Future saveOrderOutboxLog(Conn conn, PurchaseOrder entity, Order * @return future with saved outbox log in the same transaction */ public Future saveOrderLinesOutboxLogs(Conn conn, List poLines, OrderLineAuditEvent.Action action, Map okapiHeaders) { - var futures = poLines.stream().map(poLine -> { + var futures = poLines.stream() + .map(poLine -> { log.trace("saveOrderLineOutboxLog, po line id={}", poLine.getId()); String orderLine = Json.encode(poLine); return saveOutboxLog(conn, action.value(), EntityType.ORDER_LINE, orderLine, okapiHeaders) diff --git a/src/main/java/org/folio/services/lines/PoLinesBatchService.java b/src/main/java/org/folio/services/lines/PoLinesBatchService.java index 544d4ca9..dfac1adf 100644 --- a/src/main/java/org/folio/services/lines/PoLinesBatchService.java +++ b/src/main/java/org/folio/services/lines/PoLinesBatchService.java @@ -39,14 +39,14 @@ public Future poLinesBatchUpdate(List poLines, PostgresClient pgCl return pgClient.withTrans(conn -> conn.updateBatch(PO_LINE_TABLE, poLines) - .compose(rowSet -> updatePoLinesWithTitle(conn, poLines)) + .compose(rowSet -> updateTitles(conn, poLines)) .compose(rowSet -> auditOutboxService.saveOrderLinesOutboxLogs(conn, poLines, OrderLineAuditEvent.Action.EDIT, okapiHeaders)) .mapEmpty() ); } - private Future updatePoLinesWithTitle(Conn conn, List poLines) { + private Future updateTitles(Conn conn, List poLines) { var futures = poLines.stream() .filter(poLine -> !poLine.getIsPackage()) .map(poLine -> poLinesService.updateTitle(conn, poLine))