Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MODORDERS-344 poline update batch API #347

Merged
merged 7 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
},
{
"id": "orders-storage.po-lines",
"version": "12.1",
"version": "12.2",
"handlers": [
{
"methods": ["GET"],
Expand Down Expand Up @@ -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"]
}
]
},
Expand Down Expand Up @@ -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",
Expand 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"
]
},
{
Expand Down
45 changes: 45 additions & 0 deletions ramls/po-line-batch.raml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#%RAML 1.0
title: "mod-orders"
baseUri: http://github.com/folio-org/mod-orders-storage
version: v9.3

documentation:
- title: PO Line batch
content: <b>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
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: Process list of PO lines in a batch
description: Process list of PO lines in a batch APIs
put:
description: "Update the list of PO lines in a batch"
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: "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:
text/plain:
example: "unable to update <<resourcePathName|!singularize>> -- malformed JSON at 13:4"
500:
description: "Internal server error, e.g. due to misconfiguration"
body:
text/plain:
example: "internal server error, contact administrator"
5 changes: 5 additions & 0 deletions src/main/java/org/folio/config/ApplicationConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down
23 changes: 15 additions & 8 deletions src/main/java/org/folio/event/service/AuditOutboxService.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,27 @@ public Future<Boolean> 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<Boolean> saveOrderLineOutboxLog(Conn conn, PoLine poLine, OrderLineAuditEvent.Action action, Map<String, String> 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<Boolean> saveOrderLinesOutboxLogs(Conn conn, List<PoLine> poLines, OrderLineAuditEvent.Action action, Map<String, String> okapiHeaders) {
var futures = poLines.stream().map(poLine -> {
SerhiiNosko marked this conversation as resolved.
Show resolved Hide resolved
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<Future<Boolean>> getKafkaFutures(List<OutboxEventLog> eventLogs, Map<String, String> okapiHeaders) {
Expand Down
69 changes: 69 additions & 0 deletions src/main/java/org/folio/rest/impl/PoLineBatchAPI.java
Original file line number Diff line number Diff line change
@@ -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<String, String> okapiHeaders,
Handler<AsyncResult<Response>> 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<PoLine> polines) {
return "putOrdersStoragePoLinesBatch completed, PO line ids: " + polines.stream()
SerhiiNosko marked this conversation as resolved.
Show resolved Hide resolved
.map(PoLine::getId)
.collect(Collectors.joining(", "));
}
}
9 changes: 5 additions & 4 deletions src/main/java/org/folio/rest/impl/PoLinesAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -65,7 +66,7 @@ public void postOrdersStoragePoLines(PoLine poLine, Map<String, String> okapiHea
Handler<AsyncResult<Response>> 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()) {
Expand All @@ -89,7 +90,7 @@ private void createPoLineWithTitle(PoLine poLine, Handler<AsyncResult<Response>>
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={}",
Expand Down Expand Up @@ -142,7 +143,7 @@ public void putOrdersStoragePoLinesById(String id, PoLine poLine, Map<String, St
Handler<AsyncResult<Response>> 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,
Expand All @@ -156,7 +157,7 @@ public void putOrdersStoragePoLinesById(String id, PoLine poLine, Map<String, St
});
} else {
try {
poLinesService.updatePoLineWithTitle(id, poLine, new RequestContext(vertxContext, okapiHeaders))
pgClient.withTrans(conn -> 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,
Expand Down
59 changes: 59 additions & 0 deletions src/main/java/org/folio/services/lines/PoLinesBatchService.java
Original file line number Diff line number Diff line change
@@ -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<Void> poLinesBatchUpdate(List<PoLine> poLines, PostgresClient pgClient, Map<String, String> 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))
SerhiiNosko marked this conversation as resolved.
Show resolved Hide resolved
.mapEmpty()
);

}

private CompositeFuture updatePoLinesWithTitle(Conn conn, List<PoLine> poLines, Map<String, String> okapiHeaders, Context vertxContext) {
var futures = poLines.stream()
.filter(poLine -> !poLine.getIsPackage())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can pass getIsPackage as lambda and invert it using Predicate.not

.map(poLine -> poLinesService.updatePoLineWithTitle(conn, poLine.getId(), poLine, new RequestContext(vertxContext, okapiHeaders)))
SerhiiNosko marked this conversation as resolved.
Show resolved Hide resolved
.toList();
return GenericCompositeFuture.join(futures);
}

}
10 changes: 4 additions & 6 deletions src/main/java/org/folio/services/lines/PoLinesService.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,19 +101,17 @@ public Future<Void> deleteById(String id, RequestContext requestContext) {
return promise.future();
}

public Future<Void> updatePoLineWithTitle(String id, PoLine poLine, RequestContext requestContext) {
public Future<Void> updatePoLineWithTitle(Conn conn, String id, PoLine poLine, RequestContext requestContext) {
Map<String, String> okapiHeaders = requestContext.getHeaders();
DBClient client = requestContext.toDBClient();
Promise<Void> 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,
Expand Down Expand Up @@ -467,7 +465,7 @@ private void populateTitleBasedOnPackagePoLine(Title title, PoLine packagePoLine
}
}

private Future<PoLine> updateTitle(Conn conn, PoLine poLine) {
public Future<PoLine> updateTitle(Conn conn, PoLine poLine) {
Criterion criterion = getCriteriaByFieldNameAndValueNotJsonb(POLINE_ID_FIELD, poLine.getId());

return conn.get(TITLES_TABLE, Title.class, criterion, true)
Expand Down
6 changes: 6 additions & 0 deletions src/test/java/org/folio/StorageTestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -263,4 +264,9 @@ class TitleServiceTestNested extends TitleServiceTest {}
class OrderLineUpdateInstanceHandlerTestNested extends OrderLineUpdateInstanceHandlerTest {}
@Nested
class PoLIneServiceVertxTestNested extends PoLIneServiceVertxTest {}

@Nested
class PoLineBatchAPITestNested extends PoLineBatchAPITest {}


}
Loading