Skip to content

Commit

Permalink
MODORDERS-344 poline update batch API
Browse files Browse the repository at this point in the history
  • Loading branch information
grabsefx committed Aug 28, 2023
1 parent 84b2a8b commit d33bb33
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 23 deletions.
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
52 changes: 52 additions & 0 deletions ramls/po-line-batch.raml
Original file line number Diff line number Diff line change
@@ -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: <b>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.</b>

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 <<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 -> {
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()
.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))
.mapEmpty()
);

}

private CompositeFuture updatePoLinesWithTitle(Conn conn, List<PoLine> poLines, Map<String, String> 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);
}

}
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
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ public void shouldFailUpdatePoLineWithTitleWhenUpdatingTitle() {
doReturn(succeededFuture(results))
.when(conn).get(anyString(), eq(Title.class), any(Criterion.class), anyBoolean());

Future<Void> f = poLinesService.updatePoLineWithTitle(poLineId, poLine, requestContext);
Future<Void> 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();
Expand Down

0 comments on commit d33bb33

Please sign in to comment.