From 8bde8558f9e9898e2ccea21817405cf442ca63af Mon Sep 17 00:00:00 2001 From: Boburbek Kadirkhodjaev Date: Wed, 6 Nov 2024 19:05:46 +0500 Subject: [PATCH] [MODAUD-196]. Implement consumer & endpoint for invoice line records (#176) * [MODAUD-196]. Implement consumer & endpoint for invoice line records * [MODAUD-196]. Fix sonar issues * [MODAUD-196]. Fix more sonar issues * [MODAUD-196]. Update pom versions and dao classes * [MODAUD-196]. Set additionalProperties=true (open to modifications) * [MODAUD-195]. Rename restricted arg name * [MODAUD-196]. Update all acq services tests, apply minor fixes * [MODAUD-196]. Fix sonar issues --- descriptors/ModuleDescriptor-template.json | 23 +++- .../dao/acquisition/InvoiceLineEventsDao.java | 32 +++++ .../impl/InvoiceEventsDaoImpl.java | 40 +++--- .../impl/InvoiceLineEventsDaoImpl.java | 115 ++++++++++++++++++ .../rest/impl/AuditDataAcquisitionImpl.java | 23 +++- .../java/org/folio/rest/impl/InitAPIs.java | 13 +- .../InvoiceLineAuditEventsService.java | 31 +++++ .../impl/InvoiceAuditEventsServiceImpl.java | 4 +- .../InvoiceLineAuditEventsServiceImpl.java | 42 +++++++ .../impl/OrderAuditEventsServiceImpl.java | 1 - .../impl/OrderLineAuditEventsServiceImpl.java | 1 - .../impl/PieceAuditEventsServiceImpl.java | 1 - .../org/folio/util/AcquisitionEventType.java | 3 +- .../org/folio/util/AuditEventDBConstants.java | 2 + .../InvoiceLineEventConsumersVerticle.java | 26 ++++ .../consumers/InvoiceLineEventsHandler.java | 62 ++++++++++ ...ate_acquisition_invoice_line_log_table.sql | 12 ++ .../templates/db_scripts/schema.json | 5 + .../src/test/java/org/folio/TestSuite.java | 15 +++ .../org/folio/dao/InvoiceEventsDaoTest.java | 34 ++---- .../folio/dao/InvoiceLineEventsDaoTest.java | 80 ++++++++++++ .../org/folio/dao/OrderEventsDaoTest.java | 25 ++-- .../org/folio/dao/OrderLineEventsDaoTest.java | 30 +++-- .../org/folio/dao/PieceEventsDaoTest.java | 25 ++-- .../impl/AuditDataAcquisitionAPITest.java | 44 +++++++ .../InvoiceLineEventsHandlerMockTest.java | 88 ++++++++++++++ .../impl/OrderLineEventsHandlerMockTest.java | 6 +- .../InvoiceAuditEventsServiceTest.java | 64 +++++++--- .../InvoiceLineAuditEventsServiceTest.java | 92 ++++++++++++++ .../services/OrderAuditEventsServiceTest.java | 65 +++++++--- .../OrderLineAuditEventsServiceTest.java | 80 ++++++++---- .../services/PieceAuditEventsServiceTest.java | 70 +++++++++-- .../java/org/folio/utils/EntityUtils.java | 21 ++++ ramls/acquisition-events.raml | 33 +++++ ramls/invoice_line_audit_event.json | 44 +++++++ .../invoice_line_audit_event_collection.json | 25 ++++ 36 files changed, 1107 insertions(+), 170 deletions(-) create mode 100644 mod-audit-server/src/main/java/org/folio/dao/acquisition/InvoiceLineEventsDao.java create mode 100644 mod-audit-server/src/main/java/org/folio/dao/acquisition/impl/InvoiceLineEventsDaoImpl.java create mode 100644 mod-audit-server/src/main/java/org/folio/services/acquisition/InvoiceLineAuditEventsService.java create mode 100644 mod-audit-server/src/main/java/org/folio/services/acquisition/impl/InvoiceLineAuditEventsServiceImpl.java create mode 100644 mod-audit-server/src/main/java/org/folio/verticle/acquisition/InvoiceLineEventConsumersVerticle.java create mode 100644 mod-audit-server/src/main/java/org/folio/verticle/acquisition/consumers/InvoiceLineEventsHandler.java create mode 100644 mod-audit-server/src/main/resources/templates/db_scripts/acquisition/create_acquisition_invoice_line_log_table.sql create mode 100644 mod-audit-server/src/test/java/org/folio/dao/InvoiceLineEventsDaoTest.java create mode 100644 mod-audit-server/src/test/java/org/folio/rest/impl/InvoiceLineEventsHandlerMockTest.java create mode 100644 mod-audit-server/src/test/java/org/folio/services/InvoiceLineAuditEventsServiceTest.java create mode 100644 ramls/invoice_line_audit_event.json create mode 100644 ramls/invoice_line_audit_event_collection.json diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index c0a907c9..bf8b01cd 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -166,6 +166,21 @@ } ] }, + { + "id": "acquisition-invoice-line-events", + "version": "1.0", + "handlers": [ + { + "methods": [ + "GET" + ], + "pathPattern": "/audit-data/acquisition/invoice-line/{id}", + "permissionsRequired": [ + "acquisition.invoice-line.events.get" + ] + } + ] + }, { "id": "circulation-logs", "version": "1.2", @@ -284,6 +299,11 @@ "displayName": "Acquisition invoice events - get invoice change events", "description": "Get invoice change events" }, + { + "permissionName": "acquisition.invoice-line.events.get", + "displayName": "Acquisition invoice-line events - get invoice-line change events", + "description": "Get invoice-line change events" + }, { "permissionName": "audit.all", "displayName": "Audit - all permissions", @@ -299,7 +319,8 @@ "acquisition.order-line.events.get", "acquisition.piece.events.get", "acquisition.piece.events.history.get", - "acquisition.invoice.events.get" + "acquisition.invoice.events.get", + "acquisition.invoice-line.events.get" ] } ], diff --git a/mod-audit-server/src/main/java/org/folio/dao/acquisition/InvoiceLineEventsDao.java b/mod-audit-server/src/main/java/org/folio/dao/acquisition/InvoiceLineEventsDao.java new file mode 100644 index 00000000..066c8954 --- /dev/null +++ b/mod-audit-server/src/main/java/org/folio/dao/acquisition/InvoiceLineEventsDao.java @@ -0,0 +1,32 @@ +package org.folio.dao.acquisition; + +import io.vertx.core.Future; +import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.RowSet; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEventCollection; + +public interface InvoiceLineEventsDao { + + /** + * Saves invoiceLineAuditEvent entity to DB + * + * @param invoiceLineAuditEvent InvoiceLineAuditEvent entity to save + * @param tenantId tenant id + * @return future with created row + */ + Future> save(InvoiceLineAuditEvent invoiceLineAuditEvent, String tenantId); + + /** + * Searches for invoice_line audit events by id + * + * @param invoiceLineId invoice_line id + * @param sortBy sort by + * @param sortOrder sort order + * @param limit limit + * @param offset offset + * @param tenantId tenant id + * @return future with InvoiceLineAuditEventCollection + */ + Future getAuditEventsByInvoiceLineId(String invoiceLineId, String sortBy, String sortOrder, int limit, int offset, String tenantId); +} diff --git a/mod-audit-server/src/main/java/org/folio/dao/acquisition/impl/InvoiceEventsDaoImpl.java b/mod-audit-server/src/main/java/org/folio/dao/acquisition/impl/InvoiceEventsDaoImpl.java index 55a32c75..8e31a4a4 100644 --- a/mod-audit-server/src/main/java/org/folio/dao/acquisition/impl/InvoiceEventsDaoImpl.java +++ b/mod-audit-server/src/main/java/org/folio/dao/acquisition/impl/InvoiceEventsDaoImpl.java @@ -1,5 +1,23 @@ package org.folio.dao.acquisition.impl; +import static java.lang.String.format; +import static org.folio.util.AuditEventDBConstants.ACTION_DATE_FIELD; +import static org.folio.util.AuditEventDBConstants.ACTION_FIELD; +import static org.folio.util.AuditEventDBConstants.EVENT_DATE_FIELD; +import static org.folio.util.AuditEventDBConstants.ID_FIELD; +import static org.folio.util.AuditEventDBConstants.INVOICE_ID_FIELD; +import static org.folio.util.AuditEventDBConstants.MODIFIED_CONTENT_FIELD; +import static org.folio.util.AuditEventDBConstants.ORDER_BY_PATTERN; +import static org.folio.util.AuditEventDBConstants.TOTAL_RECORDS_FIELD; +import static org.folio.util.AuditEventDBConstants.USER_ID_FIELD; +import static org.folio.util.DbUtils.formatDBTableName; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.util.Date; +import java.util.UUID; + import io.vertx.core.Future; import io.vertx.core.json.JsonObject; import io.vertx.sqlclient.Row; @@ -13,24 +31,6 @@ import org.folio.util.PostgresClientFactory; import org.springframework.stereotype.Repository; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.util.Date; -import java.util.UUID; - -import static java.lang.String.format; -import static org.folio.util.AuditEventDBConstants.ACTION_DATE_FIELD; -import static org.folio.util.AuditEventDBConstants.ACTION_FIELD; -import static org.folio.util.AuditEventDBConstants.EVENT_DATE_FIELD; -import static org.folio.util.AuditEventDBConstants.ID_FIELD; -import static org.folio.util.AuditEventDBConstants.INVOICE_ID_FIELD; -import static org.folio.util.AuditEventDBConstants.MODIFIED_CONTENT_FIELD; -import static org.folio.util.AuditEventDBConstants.ORDER_BY_PATTERN; -import static org.folio.util.AuditEventDBConstants.TOTAL_RECORDS_FIELD; -import static org.folio.util.AuditEventDBConstants.USER_ID_FIELD; -import static org.folio.util.DbUtils.formatDBTableName; - @Repository public class InvoiceEventsDaoImpl implements InvoiceEventsDao { @@ -62,10 +62,10 @@ public Future> save(InvoiceAuditEvent invoiceAuditEvent, String tena } @Override - public Future getAuditEventsByInvoiceId(String invoiceId, String sortBy, String sortInvoice, int limit, int offset, String tenantId) { + public Future getAuditEventsByInvoiceId(String invoiceId, String sortBy, String sortOrder, int limit, int offset, String tenantId) { LOGGER.debug("getAuditEventsByInvoiceId:: Retrieving AuditEvent with invoice id : {}", invoiceId); String logTable = formatDBTableName(tenantId, TABLE_NAME); - String query = format(GET_BY_INVOICE_ID_SQL, logTable, logTable, format(ORDER_BY_PATTERN, sortBy, sortInvoice)); + String query = format(GET_BY_INVOICE_ID_SQL, logTable, logTable, format(ORDER_BY_PATTERN, sortBy, sortOrder)); return pgClientFactory.createInstance(tenantId).execute(query, Tuple.of(UUID.fromString(invoiceId), limit, offset)) .map(rowSet -> rowSet.rowCount() == 0 ? new InvoiceAuditEventCollection().withTotalItems(0) : mapRowToListOfInvoiceEvent(rowSet)); diff --git a/mod-audit-server/src/main/java/org/folio/dao/acquisition/impl/InvoiceLineEventsDaoImpl.java b/mod-audit-server/src/main/java/org/folio/dao/acquisition/impl/InvoiceLineEventsDaoImpl.java new file mode 100644 index 00000000..242c635b --- /dev/null +++ b/mod-audit-server/src/main/java/org/folio/dao/acquisition/impl/InvoiceLineEventsDaoImpl.java @@ -0,0 +1,115 @@ +package org.folio.dao.acquisition.impl; + +import static java.lang.String.format; +import static org.folio.util.AuditEventDBConstants.ACTION_DATE_FIELD; +import static org.folio.util.AuditEventDBConstants.ACTION_FIELD; +import static org.folio.util.AuditEventDBConstants.EVENT_DATE_FIELD; +import static org.folio.util.AuditEventDBConstants.ID_FIELD; +import static org.folio.util.AuditEventDBConstants.INVOICE_ID_FIELD; +import static org.folio.util.AuditEventDBConstants.INVOICE_LINE_ID_FIELD; +import static org.folio.util.AuditEventDBConstants.MODIFIED_CONTENT_FIELD; +import static org.folio.util.AuditEventDBConstants.ORDER_BY_PATTERN; +import static org.folio.util.AuditEventDBConstants.TOTAL_RECORDS_FIELD; +import static org.folio.util.AuditEventDBConstants.USER_ID_FIELD; +import static org.folio.util.DbUtils.formatDBTableName; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.util.Date; +import java.util.UUID; + +import io.vertx.core.Future; +import io.vertx.core.json.JsonObject; +import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.RowSet; +import io.vertx.sqlclient.Tuple; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.dao.acquisition.InvoiceLineEventsDao; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEventCollection; +import org.folio.util.PostgresClientFactory; +import org.springframework.stereotype.Repository; + +@Repository +public class InvoiceLineEventsDaoImpl implements InvoiceLineEventsDao { + + private static final Logger LOGGER = LogManager.getLogger(); + + public static final String TABLE_NAME = "acquisition_invoice_line_log"; + + public static final String GET_BY_INVOICE_LINE_ID_SQL = "SELECT id, action, invoice_id, invoice_line_id, user_id, event_date, action_date, modified_content_snapshot," + + " (SELECT count(*) AS total_records FROM %s WHERE invoice_line_id = $1) " + + " FROM %s WHERE invoice_line_id = $1 %s LIMIT $2 OFFSET $3"; + + private static final String INSERT_SQL = "INSERT INTO %s (id, action, invoice_id, invoice_line_id, user_id, event_date, action_date, modified_content_snapshot) " + + "VALUES ($1, $2, $3, $4, $5, $6, $7, $8)"; + + private final PostgresClientFactory pgClientFactory; + + public InvoiceLineEventsDaoImpl(PostgresClientFactory pgClientFactory) { + this.pgClientFactory = pgClientFactory; + } + + @Override + public Future> save(InvoiceLineAuditEvent invoiceLineAuditEvent, String tenantId) { + LOGGER.debug("save:: Saving InvoiceLine AuditEvent with tenant id : {}", tenantId); + String logTable = formatDBTableName(tenantId, TABLE_NAME); + String query = format(INSERT_SQL, logTable); + return makeSaveCall(query, invoiceLineAuditEvent, tenantId) + .onSuccess(rows -> LOGGER.info("save:: Saved InvoiceLine AuditEvent with tenant id : {}", tenantId)) + .onFailure(e -> LOGGER.error("Failed to save record with id: {} for invoice line id: {} in to table {}", + invoiceLineAuditEvent.getId(), invoiceLineAuditEvent.getInvoiceLineId(), TABLE_NAME, e)); + } + + @Override + public Future getAuditEventsByInvoiceLineId(String invoiceLineId, String sortBy, String sortOrder, int limit, int offset, String tenantId) { + LOGGER.debug("getAuditEventsByInvoiceLineId:: Retrieving AuditEvent with invoice line id : {}", invoiceLineId); + String logTable = formatDBTableName(tenantId, TABLE_NAME); + String query = format(GET_BY_INVOICE_LINE_ID_SQL, logTable, logTable, format(ORDER_BY_PATTERN, sortBy, sortOrder)); + return pgClientFactory.createInstance(tenantId).execute(query, Tuple.of(UUID.fromString(invoiceLineId), limit, offset)) + .map(rowSet -> rowSet.rowCount() == 0 ? new InvoiceLineAuditEventCollection().withTotalItems(0) + : mapRowToListOfInvoiceLineEvent(rowSet)); + } + + private Future> makeSaveCall(String query, InvoiceLineAuditEvent invoiceLineAuditEvent, String tenantId) { + LOGGER.debug("makeSaveCall:: Making save call with query : {} and tenant id : {}", query, tenantId); + try { + return pgClientFactory.createInstance(tenantId).execute(query, Tuple.of(invoiceLineAuditEvent.getId(), + invoiceLineAuditEvent.getAction(), + invoiceLineAuditEvent.getInvoiceId(), + invoiceLineAuditEvent.getInvoiceLineId(), + invoiceLineAuditEvent.getUserId(), + LocalDateTime.ofInstant(invoiceLineAuditEvent.getEventDate().toInstant(), ZoneId.systemDefault()), + LocalDateTime.ofInstant(invoiceLineAuditEvent.getActionDate().toInstant(), ZoneId.systemDefault()), + JsonObject.mapFrom(invoiceLineAuditEvent.getInvoiceLineSnapshot()))); + } catch (Exception e) { + return Future.failedFuture(e); + } + } + + private InvoiceLineAuditEventCollection mapRowToListOfInvoiceLineEvent(RowSet rowSet) { + LOGGER.debug("mapRowToListOfInvoiceLineEvent:: Mapping row to List of Invoice Line Events"); + InvoiceLineAuditEventCollection invoiceLineAuditEventCollection = new InvoiceLineAuditEventCollection(); + rowSet.iterator().forEachRemaining(row -> { + invoiceLineAuditEventCollection.getInvoiceLineAuditEvents().add(mapRowToInvoiceLineEvent(row)); + invoiceLineAuditEventCollection.setTotalItems(row.getInteger(TOTAL_RECORDS_FIELD)); + }); + LOGGER.debug("mapRowToListOfInvoiceLineEvent:: Mapped row to List of Invoice Line Events"); + return invoiceLineAuditEventCollection; + } + + private InvoiceLineAuditEvent mapRowToInvoiceLineEvent(Row row) { + LOGGER.debug("mapRowToInvoiceLineEvent:: Mapping row to Invoice Line Event"); + return new InvoiceLineAuditEvent() + .withId(row.getValue(ID_FIELD).toString()) + .withAction(row.get(InvoiceLineAuditEvent.Action.class, ACTION_FIELD)) + .withInvoiceId(row.getValue(INVOICE_ID_FIELD).toString()) + .withInvoiceLineId(row.getValue(INVOICE_LINE_ID_FIELD).toString()) + .withUserId(row.getValue(USER_ID_FIELD).toString()) + .withEventDate(Date.from(row.getLocalDateTime(EVENT_DATE_FIELD).toInstant(ZoneOffset.UTC))) + .withActionDate(Date.from(row.getLocalDateTime(ACTION_DATE_FIELD).toInstant(ZoneOffset.UTC))) + .withInvoiceLineSnapshot(JsonObject.mapFrom(row.getValue(MODIFIED_CONTENT_FIELD))); + } +} diff --git a/mod-audit-server/src/main/java/org/folio/rest/impl/AuditDataAcquisitionImpl.java b/mod-audit-server/src/main/java/org/folio/rest/impl/AuditDataAcquisitionImpl.java index 152aaa84..c42c9dd1 100644 --- a/mod-audit-server/src/main/java/org/folio/rest/impl/AuditDataAcquisitionImpl.java +++ b/mod-audit-server/src/main/java/org/folio/rest/impl/AuditDataAcquisitionImpl.java @@ -8,6 +8,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.folio.rest.jaxrs.model.AuditDataAcquisitionInvoiceIdGetSortOrder; +import org.folio.rest.jaxrs.model.AuditDataAcquisitionInvoiceLineIdGetSortOrder; import org.folio.rest.jaxrs.model.AuditDataAcquisitionOrderIdGetSortOrder; import org.folio.rest.jaxrs.model.AuditDataAcquisitionOrderLineIdGetSortOrder; import org.folio.rest.jaxrs.model.AuditDataAcquisitionPieceIdGetSortOrder; @@ -15,6 +16,7 @@ import org.folio.rest.jaxrs.resource.AuditDataAcquisition; import org.folio.rest.tools.utils.TenantTool; import org.folio.services.acquisition.InvoiceAuditEventsService; +import org.folio.services.acquisition.InvoiceLineAuditEventsService; import org.folio.services.acquisition.OrderAuditEventsService; import org.folio.services.acquisition.OrderLineAuditEventsService; import org.folio.services.acquisition.PieceAuditEventsService; @@ -39,6 +41,8 @@ public class AuditDataAcquisitionImpl implements AuditDataAcquisition { private PieceAuditEventsService pieceAuditEventsService; @Autowired private InvoiceAuditEventsService invoiceAuditEventsService; + @Autowired + private InvoiceLineAuditEventsService invoiceLineAuditEventsService; public AuditDataAcquisitionImpl() { SpringContextUtil.autowireDependencies(this, Vertx.currentContext()); @@ -118,7 +122,7 @@ public void getAuditDataAcquisitionPieceStatusChangeHistoryById(String pieceId, public void getAuditDataAcquisitionInvoiceById(String invoiceId, String sortBy, AuditDataAcquisitionInvoiceIdGetSortOrder sortOrder, int limit, int offset, Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) { - LOGGER.debug("getAuditDataAcquisitionOrderLineById:: Retrieving Audit Data Acquisition Invoice Line By Id : {}", invoiceId); + LOGGER.debug("getAuditDataAcquisitionOrderLineById:: Retrieving Audit Data Acquisition Invoice By Id : {}", invoiceId); String tenantId = TenantTool.tenantId(okapiHeaders); try { invoiceAuditEventsService.getAuditEventsByInvoiceId(invoiceId, sortBy, sortOrder.name(), limit, offset, tenantId) @@ -132,6 +136,23 @@ public void getAuditDataAcquisitionInvoiceById(String invoiceId, String sortBy, } } + @Override + public void getAuditDataAcquisitionInvoiceLineById(String invoiceLineId, String sortBy, AuditDataAcquisitionInvoiceLineIdGetSortOrder sortOrder, + int limit, int offset, Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) { + LOGGER.debug("getAuditDataAcquisitionInvoiceLineById:: Retrieving Audit Data Acquisition Invoice Line By Id : {}", invoiceLineId); + String tenantId = TenantTool.tenantId(okapiHeaders); + try { + invoiceLineAuditEventsService.getAuditEventsByInvoiceLineId(invoiceLineId, sortBy, sortOrder.name(), limit, offset, tenantId) + .map(GetAuditDataAcquisitionInvoiceLineByIdResponse::respond200WithApplicationJson) + .map(Response.class::cast) + .otherwise(this::mapExceptionToResponse) + .onComplete(asyncResultHandler); + } catch (Exception e) { + LOGGER.error("Failed to get invoice line audit events by order line id: {}", invoiceLineId, e); + asyncResultHandler.handle(Future.succeededFuture(mapExceptionToResponse(e))); + } + } + private Response mapExceptionToResponse(Throwable throwable) { LOGGER.debug("mapExceptionToResponse:: Mapping Exception :{} to Response", throwable.getMessage(), throwable); return GetAuditDataAcquisitionOrderByIdResponse diff --git a/mod-audit-server/src/main/java/org/folio/rest/impl/InitAPIs.java b/mod-audit-server/src/main/java/org/folio/rest/impl/InitAPIs.java index c1491e9d..9e60c8dd 100644 --- a/mod-audit-server/src/main/java/org/folio/rest/impl/InitAPIs.java +++ b/mod-audit-server/src/main/java/org/folio/rest/impl/InitAPIs.java @@ -17,6 +17,7 @@ import org.folio.spring.SpringContextUtil; import org.folio.verticle.SpringVerticleFactory; import org.folio.verticle.acquisition.InvoiceEventConsumersVerticle; +import org.folio.verticle.acquisition.InvoiceLineEventConsumersVerticle; import org.folio.verticle.acquisition.OrderEventConsumersVerticle; import org.folio.verticle.acquisition.OrderLineEventConsumersVerticle; import org.folio.verticle.acquisition.PieceEventConsumersVerticle; @@ -46,9 +47,14 @@ public class InitAPIs implements InitAPI { @Value("${acq.invoices.kafka.consumer.instancesNumber:1}") private int acqInvoiceConsumerInstancesNumber; - @Value("${acq.orders.kafka.consumer.pool.size:5}") + @Value("${acq.invoices.kafka.consumer.pool.size:5}") private int acqInvoiceConsumerPoolSize; + @Value("${acq.invoice-lines.kafka.consumer.instancesNumber:1}") + private int acqInvoiceLineConsumerInstancesNumber; + @Value("${acq.invoice-lines.kafka.consumer.pool.size:5}") + private int acqInvoiceLineConsumerPoolSize; + @Override public void init(Vertx vertx, Context context, Handler> handler) { LOGGER.debug("init:: InitAPI starting..."); @@ -80,18 +86,21 @@ private Future deployConsumersVerticles(Vertx vertx) { Promise orderLineEventsConsumer = Promise.promise(); Promise pieceEventsConsumer = Promise.promise(); Promise invoiceEventsConsumer = Promise.promise(); + Promise invoiceLineEventsConsumer = Promise.promise(); deployVerticle(vertx, verticleFactory, OrderEventConsumersVerticle.class, acqOrderConsumerInstancesNumber, acqOrderConsumerPoolSize, orderEventsConsumer); deployVerticle(vertx, verticleFactory, OrderLineEventConsumersVerticle.class, acqOrderLineConsumerInstancesNumber, acqOrderLineConsumerPoolSize, orderLineEventsConsumer); deployVerticle(vertx, verticleFactory, PieceEventConsumersVerticle.class, acqPieceConsumerInstancesNumber, acqPieceConsumerPoolSize, pieceEventsConsumer); deployVerticle(vertx, verticleFactory, InvoiceEventConsumersVerticle.class, acqInvoiceConsumerInstancesNumber, acqInvoiceConsumerPoolSize, invoiceEventsConsumer); + deployVerticle(vertx, verticleFactory, InvoiceLineEventConsumersVerticle.class, acqInvoiceLineConsumerInstancesNumber, acqInvoiceLineConsumerPoolSize, invoiceLineEventsConsumer); LOGGER.info("deployConsumersVerticles:: All consumer verticles were successfully deployed"); return GenericCompositeFuture.all(Arrays.asList( orderEventsConsumer.future(), orderLineEventsConsumer.future(), pieceEventsConsumer.future(), - invoiceEventsConsumer.future())); + invoiceEventsConsumer.future(), + invoiceLineEventsConsumer.future())); } private void deployVerticle(Vertx vertx, VerticleFactory verticleFactory, Class consumerClass, diff --git a/mod-audit-server/src/main/java/org/folio/services/acquisition/InvoiceLineAuditEventsService.java b/mod-audit-server/src/main/java/org/folio/services/acquisition/InvoiceLineAuditEventsService.java new file mode 100644 index 00000000..f096c7dc --- /dev/null +++ b/mod-audit-server/src/main/java/org/folio/services/acquisition/InvoiceLineAuditEventsService.java @@ -0,0 +1,31 @@ +package org.folio.services.acquisition; + +import io.vertx.core.Future; +import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.RowSet; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEventCollection; + +public interface InvoiceLineAuditEventsService { + + /** + * Saves InvoiceLineAuditEvent + * + * @param invoiceLineAuditEvent invoice line event to save + * @param tenantId id of tenant + * @return successful future if event has not been processed, or failed future otherwise + */ + Future> saveInvoiceLineAuditEvent(InvoiceLineAuditEvent invoiceLineAuditEvent, String tenantId); + + /** + * Searches for invoice line audit events by invoice line id + * + * @param invoiceLineId invoice line id + * @param sortBy sort by + * @param sortOrder sort order + * @param limit limit + * @param offset offset + * @return future with InvoiceLineAuditEventCollection + */ + Future getAuditEventsByInvoiceLineId(String invoiceLineId, String sortBy, String sortOrder, int limit, int offset, String tenantId); +} diff --git a/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/InvoiceAuditEventsServiceImpl.java b/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/InvoiceAuditEventsServiceImpl.java index 5541f564..d2e427c9 100644 --- a/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/InvoiceAuditEventsServiceImpl.java +++ b/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/InvoiceAuditEventsServiceImpl.java @@ -35,8 +35,8 @@ public Future> saveInvoiceAuditEvent(InvoiceAuditEvent invoiceAuditE } @Override - public Future getAuditEventsByInvoiceId(String invoiceId, String sortBy, String sortInvoice, int limit, int offset, String tenantId) { + public Future getAuditEventsByInvoiceId(String invoiceId, String sortBy, String sortOrder, int limit, int offset, String tenantId) { LOGGER.debug("getAuditEventsByInvoiceId:: Retrieving audit events for invoiceId={} and tenantId={}", invoiceId, tenantId); - return invoiceEventsDao.getAuditEventsByInvoiceId(invoiceId, sortBy, sortInvoice, limit, offset, tenantId); + return invoiceEventsDao.getAuditEventsByInvoiceId(invoiceId, sortBy, sortOrder, limit, offset, tenantId); } } diff --git a/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/InvoiceLineAuditEventsServiceImpl.java b/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/InvoiceLineAuditEventsServiceImpl.java new file mode 100644 index 00000000..9fc6c4d3 --- /dev/null +++ b/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/InvoiceLineAuditEventsServiceImpl.java @@ -0,0 +1,42 @@ +package org.folio.services.acquisition.impl; + +import static org.folio.util.ErrorUtils.handleFailures; + +import io.vertx.core.Future; +import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.RowSet; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.dao.acquisition.InvoiceLineEventsDao; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEventCollection; +import org.folio.services.acquisition.InvoiceLineAuditEventsService; +import org.springframework.stereotype.Service; + +@Service +public class InvoiceLineAuditEventsServiceImpl implements InvoiceLineAuditEventsService { + + private static final Logger LOGGER = LogManager.getLogger(); + + private final InvoiceLineEventsDao invoiceLineEventsDao; + + public InvoiceLineAuditEventsServiceImpl(InvoiceLineEventsDao invoiceLineEventsDao) { + this.invoiceLineEventsDao = invoiceLineEventsDao; + } + + @Override + public Future> saveInvoiceLineAuditEvent(InvoiceLineAuditEvent invoiceLineAuditEvent, String tenantId) { + LOGGER.debug("saveInvoiceLineAuditEvent:: Saving invoice line audit event with invoiceLineId={} in tenant Id={}", invoiceLineAuditEvent.getId(), tenantId); + return invoiceLineEventsDao.save(invoiceLineAuditEvent, tenantId) + .recover(throwable -> { + LOGGER.error("handleFailures:: Could not save invoice audit event for InvoiceLine id: {} in tenantId: {}", invoiceLineAuditEvent.getInvoiceLineId(), tenantId); + return handleFailures(throwable, invoiceLineAuditEvent.getId()); + }); + } + + @Override + public Future getAuditEventsByInvoiceLineId(String invoiceLineId, String sortBy, String sortOrder, int limit, int offset, String tenantId) { + LOGGER.debug("getAuditEventsByInvoiceLineId:: Retrieving audit events for invoiceLineId={} and tenantId={}", invoiceLineId, tenantId); + return invoiceLineEventsDao.getAuditEventsByInvoiceLineId(invoiceLineId, sortBy, sortOrder, limit, offset, tenantId); + } +} diff --git a/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/OrderAuditEventsServiceImpl.java b/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/OrderAuditEventsServiceImpl.java index 5f254824..b14dcfe7 100644 --- a/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/OrderAuditEventsServiceImpl.java +++ b/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/OrderAuditEventsServiceImpl.java @@ -39,5 +39,4 @@ public Future getAuditEventsByOrderId(String orderId, LOGGER.debug("getAuditEventsByOrderId:: Retrieving audit events for orderId={} and tenantId={}", orderId, tenantId); return orderEventsDao.getAuditEventsByOrderId(orderId, sortBy, sortOrder, limit, offset, tenantId); } - } diff --git a/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/OrderLineAuditEventsServiceImpl.java b/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/OrderLineAuditEventsServiceImpl.java index 0e3d299d..c9f902e9 100644 --- a/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/OrderLineAuditEventsServiceImpl.java +++ b/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/OrderLineAuditEventsServiceImpl.java @@ -39,5 +39,4 @@ public Future getAuditEventsByOrderLineId(String LOGGER.debug("getAuditEventsByOrderLineId:: Retrieving audit events for order line Id : {} and tenant Id : {}", orderLineId, tenantId); return orderLineEventsDao.getAuditEventsByOrderLineId(orderLineId, sortBy, sortOrder, limit, offset, tenantId); } - } diff --git a/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/PieceAuditEventsServiceImpl.java b/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/PieceAuditEventsServiceImpl.java index 299b6e0a..5d9be4d0 100644 --- a/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/PieceAuditEventsServiceImpl.java +++ b/mod-audit-server/src/main/java/org/folio/services/acquisition/impl/PieceAuditEventsServiceImpl.java @@ -44,5 +44,4 @@ public Future getAuditEventsWithStatusChangesByPieceI LOGGER.debug("getAuditEventsByOrderId:: Retrieving audit events with unique status changes for pieceId={} and tenantId={}", pieceId, tenantId); return pieceEventsDao.getAuditEventsWithStatusChangesByPieceId(pieceId, sortBy, sortOrder, limit, offset, tenantId); } - } diff --git a/mod-audit-server/src/main/java/org/folio/util/AcquisitionEventType.java b/mod-audit-server/src/main/java/org/folio/util/AcquisitionEventType.java index 4ae865a5..f30acdf4 100644 --- a/mod-audit-server/src/main/java/org/folio/util/AcquisitionEventType.java +++ b/mod-audit-server/src/main/java/org/folio/util/AcquisitionEventType.java @@ -4,7 +4,8 @@ public enum AcquisitionEventType { ACQ_ORDER_CHANGED("ACQ_ORDER_CHANGED"), ACQ_ORDER_LINE_CHANGED("ACQ_ORDER_LINE_CHANGED"), ACQ_PIECE_CHANGED("ACQ_PIECE_CHANGED"), - ACQ_INVOICE_CHANGED("ACQ_INVOICE_CHANGED"); + ACQ_INVOICE_CHANGED("ACQ_INVOICE_CHANGED"), + ACQ_INVOICE_LINE_CHANGED("ACQ_INVOICE_LINE_CHANGED"); private final String topicName; diff --git a/mod-audit-server/src/main/java/org/folio/util/AuditEventDBConstants.java b/mod-audit-server/src/main/java/org/folio/util/AuditEventDBConstants.java index cfbded99..27f0cbca 100644 --- a/mod-audit-server/src/main/java/org/folio/util/AuditEventDBConstants.java +++ b/mod-audit-server/src/main/java/org/folio/util/AuditEventDBConstants.java @@ -16,6 +16,8 @@ private AuditEventDBConstants() {} public static final String INVOICE_ID_FIELD = "invoice_id"; + public static final String INVOICE_LINE_ID_FIELD = "invoice_line_id"; + public static final String USER_ID_FIELD = "user_id"; public static final String EVENT_DATE_FIELD = "event_date"; diff --git a/mod-audit-server/src/main/java/org/folio/verticle/acquisition/InvoiceLineEventConsumersVerticle.java b/mod-audit-server/src/main/java/org/folio/verticle/acquisition/InvoiceLineEventConsumersVerticle.java new file mode 100644 index 00000000..ff371601 --- /dev/null +++ b/mod-audit-server/src/main/java/org/folio/verticle/acquisition/InvoiceLineEventConsumersVerticle.java @@ -0,0 +1,26 @@ +package org.folio.verticle.acquisition; + +import org.folio.kafka.AsyncRecordHandler; +import org.folio.util.AcquisitionEventType; +import org.folio.verticle.AbstractConsumersVerticle; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class InvoiceLineEventConsumersVerticle extends AbstractConsumersVerticle { + + @Autowired + private AsyncRecordHandler invoiceLineEventsHandler; + + @Override + public List getEvents() { + return List.of(AcquisitionEventType.ACQ_INVOICE_LINE_CHANGED.getTopicName()); + } + + @Override + public AsyncRecordHandler getHandler() { + return invoiceLineEventsHandler; + } +} diff --git a/mod-audit-server/src/main/java/org/folio/verticle/acquisition/consumers/InvoiceLineEventsHandler.java b/mod-audit-server/src/main/java/org/folio/verticle/acquisition/consumers/InvoiceLineEventsHandler.java new file mode 100644 index 00000000..538ac0e4 --- /dev/null +++ b/mod-audit-server/src/main/java/org/folio/verticle/acquisition/consumers/InvoiceLineEventsHandler.java @@ -0,0 +1,62 @@ +package org.folio.verticle.acquisition.consumers; + +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import io.vertx.core.json.JsonObject; +import io.vertx.kafka.client.consumer.KafkaConsumerRecord; +import io.vertx.kafka.client.producer.KafkaHeader; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.kafka.AsyncRecordHandler; +import org.folio.kafka.KafkaHeaderUtils; +import org.folio.kafka.exception.DuplicateEventException; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent; +import org.folio.rest.util.OkapiConnectionParams; +import org.folio.services.acquisition.InvoiceLineAuditEventsService; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class InvoiceLineEventsHandler implements AsyncRecordHandler { + + private static final Logger LOGGER = LogManager.getLogger(); + + private final InvoiceLineAuditEventsService invoiceLineAuditEventsService; + private final Vertx vertx; + + public InvoiceLineEventsHandler(Vertx vertx, + InvoiceLineAuditEventsService invoiceLineAuditEventsService) { + this.vertx = vertx; + this.invoiceLineAuditEventsService = invoiceLineAuditEventsService; + } + + @Override + public Future handle(KafkaConsumerRecord kafkaConsumerRecord) { + Promise result = Promise.promise(); + List kafkaHeaders = kafkaConsumerRecord.headers(); + OkapiConnectionParams okapiConnectionParams = new OkapiConnectionParams(KafkaHeaderUtils.kafkaHeadersToMap(kafkaHeaders), vertx); + InvoiceLineAuditEvent event = new JsonObject(kafkaConsumerRecord.value()).mapTo(InvoiceLineAuditEvent.class); + LOGGER.info("handle:: Starting processing of Invoice Line audit event with id: {} for invoice line id: {}", + event.getId(), event.getInvoiceLineId()); + + invoiceLineAuditEventsService.saveInvoiceLineAuditEvent(event, okapiConnectionParams.getTenantId()) + .onSuccess(ar -> { + LOGGER.info("handle:: Invoice Line audit event with id: {} has been processed for invoice line id: {}", + event.getId(), event.getInvoiceLineId()); + result.complete(event.getId()); + }) + .onFailure(e -> { + if (e instanceof DuplicateEventException) { + LOGGER.info("handle:: Duplicate Invoice Line audit event with id: {} for invoice line id: {} received, skipped processing", event.getId(), event.getInvoiceLineId()); + result.complete(event.getId()); + } else { + LOGGER.error("Processing of Invoice Line audit event with id: {} for invoice line id: {} has been failed", event.getId(), event.getInvoiceLineId(), e); + result.fail(e); + } + }); + + return result.future(); + } +} diff --git a/mod-audit-server/src/main/resources/templates/db_scripts/acquisition/create_acquisition_invoice_line_log_table.sql b/mod-audit-server/src/main/resources/templates/db_scripts/acquisition/create_acquisition_invoice_line_log_table.sql new file mode 100644 index 00000000..3bfce0c6 --- /dev/null +++ b/mod-audit-server/src/main/resources/templates/db_scripts/acquisition/create_acquisition_invoice_line_log_table.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS acquisition_invoice_line_log ( + id uuid PRIMARY KEY, + action text NOT NULL, + invoice_id uuid NOT NULL, + invoice_line_id uuid NOT NULL, + user_id uuid NOT NULL, + event_date timestamp NOT NULL, + action_date timestamp NOT NULL, + modified_content_snapshot jsonb +); + +CREATE INDEX IF NOT EXISTS invoice_line_id_index ON acquisition_invoice_line_log USING BTREE (invoice_line_id); diff --git a/mod-audit-server/src/main/resources/templates/db_scripts/schema.json b/mod-audit-server/src/main/resources/templates/db_scripts/schema.json index bebe29d3..4254e723 100644 --- a/mod-audit-server/src/main/resources/templates/db_scripts/schema.json +++ b/mod-audit-server/src/main/resources/templates/db_scripts/schema.json @@ -84,6 +84,11 @@ "run": "after", "snippetPath": "acquisition/create_acquisition_invoice_log_table.sql", "fromModuleVersion": "mod-audit-3.0.0" + }, + { + "run": "after", + "snippetPath": "acquisition/create_acquisition_invoice_line_log_table.sql", + "fromModuleVersion": "mod-audit-3.0.0" } ] } diff --git a/mod-audit-server/src/test/java/org/folio/TestSuite.java b/mod-audit-server/src/test/java/org/folio/TestSuite.java index 5b1b3a30..06ddbe3c 100644 --- a/mod-audit-server/src/test/java/org/folio/TestSuite.java +++ b/mod-audit-server/src/test/java/org/folio/TestSuite.java @@ -25,6 +25,7 @@ import org.folio.builder.service.NoticeRecordBuilderTest; import org.folio.builder.service.RequestRecordBuilderTest; import org.folio.dao.InvoiceEventsDaoTest; +import org.folio.dao.InvoiceLineEventsDaoTest; import org.folio.dao.OrderEventsDaoTest; import org.folio.dao.OrderLineEventsDaoTest; import org.folio.dao.PieceEventsDaoTest; @@ -35,11 +36,13 @@ import org.folio.rest.impl.AuditHandlersImplApiTest; import org.folio.rest.impl.CirculationLogsImplApiTest; import org.folio.rest.impl.InvoiceEventsHandlerMockTest; +import org.folio.rest.impl.InvoiceLineEventsHandlerMockTest; import org.folio.rest.impl.OrderEventsHandlerMockTest; import org.folio.rest.impl.OrderLineEventsHandlerMockTest; import org.folio.rest.impl.PieceEventsHandlerMockTest; import org.folio.rest.persist.PostgresClient; import org.folio.services.InvoiceAuditEventsServiceTest; +import org.folio.services.InvoiceLineAuditEventsServiceTest; import org.folio.services.OrderAuditEventsServiceTest; import org.folio.services.OrderLineAuditEventsServiceTest; import org.folio.services.PieceAuditEventsServiceTest; @@ -210,6 +213,18 @@ class InvoiceAuditEventsServiceNestedTest extends InvoiceAuditEventsServiceTest class InvoiceEventsDaoNestedTest extends InvoiceEventsDaoTest { } + @Nested + class InvoiceLineEventsHandlerMockNestedTest extends InvoiceLineEventsHandlerMockTest { + } + + @Nested + class InvoiceLineAuditEventsServiceNestedTest extends InvoiceLineAuditEventsServiceTest { + } + + @Nested + class InvoiceLineEventsDaoNestedTest extends InvoiceLineEventsDaoTest { + } + @Nested class AuditDataImplApiTestNested extends AuditDataImplApiTest { } diff --git a/mod-audit-server/src/test/java/org/folio/dao/InvoiceEventsDaoTest.java b/mod-audit-server/src/test/java/org/folio/dao/InvoiceEventsDaoTest.java index 79f9a672..e1c5e05a 100644 --- a/mod-audit-server/src/test/java/org/folio/dao/InvoiceEventsDaoTest.java +++ b/mod-audit-server/src/test/java/org/folio/dao/InvoiceEventsDaoTest.java @@ -4,24 +4,16 @@ import static org.folio.utils.EntityUtils.createInvoiceAuditEvent; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import java.util.List; import java.util.UUID; -import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.pgclient.PgException; -import io.vertx.sqlclient.Row; -import io.vertx.sqlclient.RowSet; -import io.vertx.sqlclient.Tuple; import org.folio.CopilotGenerated; import org.folio.dao.acquisition.impl.InvoiceEventsDaoImpl; import org.folio.rest.jaxrs.model.InvoiceAuditEvent; -import org.folio.rest.jaxrs.model.InvoiceAuditEventCollection; import org.folio.util.PostgresClientFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -35,7 +27,7 @@ public class InvoiceEventsDaoTest { @Spy private PostgresClientFactory postgresClientFactory = new PostgresClientFactory(Vertx.vertx()); @InjectMocks - InvoiceEventsDaoImpl invoiceEventDao = new InvoiceEventsDaoImpl(postgresClientFactory); + InvoiceEventsDaoImpl invoiceEventDao; @BeforeEach public void setUp() { @@ -47,44 +39,42 @@ public void setUp() { void shouldCreateEventProcessed() { var invoiceAuditEvent = createInvoiceAuditEvent(UUID.randomUUID().toString()); - Future> saveFuture = invoiceEventDao.save(invoiceAuditEvent, TENANT_ID); - saveFuture.onComplete(ar -> { - assertTrue(ar.succeeded()); - verify(postgresClientFactory, times(1)).createInstance(TENANT_ID).execute(anyString(), any(Tuple.class)); - }); + var saveFuture = invoiceEventDao.save(invoiceAuditEvent, TENANT_ID); + saveFuture.onComplete(ar -> assertTrue(ar.succeeded())); + verify(postgresClientFactory, times(1)).createInstance(TENANT_ID); } @Test void shouldThrowConstraintViolation() { var invoiceAuditEvent = createInvoiceAuditEvent(UUID.randomUUID().toString()); - Future> saveFuture = invoiceEventDao.save(invoiceAuditEvent, TENANT_ID); + var saveFuture = invoiceEventDao.save(invoiceAuditEvent, TENANT_ID); saveFuture.onComplete(ar -> { - Future> reSaveFuture = invoiceEventDao.save(invoiceAuditEvent, TENANT_ID); + var reSaveFuture = invoiceEventDao.save(invoiceAuditEvent, TENANT_ID); reSaveFuture.onComplete(re -> { assertTrue(re.failed()); assertTrue(re.cause() instanceof PgException); assertEquals("ERROR: duplicate key value violates unique constraint \"acquisition_invoice_log_pkey\" (23505)", re.cause().getMessage()); - verify(postgresClientFactory, times(1)).createInstance(TENANT_ID).execute(anyString(), any(Tuple.class)); }); }); + verify(postgresClientFactory, times(1)).createInstance(TENANT_ID); } @Test void shouldGetCreatedEvent() { - String id = UUID.randomUUID().toString(); + var id = UUID.randomUUID().toString(); var invoiceAuditEvent = createInvoiceAuditEvent(id); invoiceEventDao.save(invoiceAuditEvent, TENANT_ID); - Future dto = invoiceEventDao.getAuditEventsByInvoiceId(id, "action_date", "desc", 1, 1, TENANT_ID); + var dto = invoiceEventDao.getAuditEventsByInvoiceId(id, "action_date", "desc", 1, 1, TENANT_ID); dto.onComplete(ar -> { - InvoiceAuditEventCollection invoiceAuditEventOptional = ar.result(); - List invoiceAuditEventList = invoiceAuditEventOptional.getInvoiceAuditEvents(); + var invoiceAuditEventOptional = ar.result(); + var invoiceAuditEventList = invoiceAuditEventOptional.getInvoiceAuditEvents(); assertEquals(invoiceAuditEventList.get(0).getId(), id); assertEquals(InvoiceAuditEvent.Action.CREATE.value(), invoiceAuditEventList.get(0).getAction().value()); - verify(postgresClientFactory, times(1)).createInstance(TENANT_ID).selectRead(anyString(), any(Tuple.class), any()); }); + verify(postgresClientFactory, times(2)).createInstance(TENANT_ID); } } diff --git a/mod-audit-server/src/test/java/org/folio/dao/InvoiceLineEventsDaoTest.java b/mod-audit-server/src/test/java/org/folio/dao/InvoiceLineEventsDaoTest.java new file mode 100644 index 00000000..9d7ec138 --- /dev/null +++ b/mod-audit-server/src/test/java/org/folio/dao/InvoiceLineEventsDaoTest.java @@ -0,0 +1,80 @@ +package org.folio.dao; + +import static org.folio.utils.EntityUtils.TENANT_ID; +import static org.folio.utils.EntityUtils.createInvoiceLineAuditEvent; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.UUID; + +import io.vertx.core.Vertx; +import io.vertx.pgclient.PgException; +import org.folio.CopilotGenerated; +import org.folio.dao.acquisition.impl.InvoiceLineEventsDaoImpl; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent; +import org.folio.util.PostgresClientFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +@CopilotGenerated +public class InvoiceLineEventsDaoTest { + + @Spy + private PostgresClientFactory postgresClientFactory = new PostgresClientFactory(Vertx.vertx()); + @InjectMocks + InvoiceLineEventsDaoImpl invoiceLineEventsDao; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + invoiceLineEventsDao = new InvoiceLineEventsDaoImpl(postgresClientFactory); + } + + @Test + void shouldCreateEventProcessed() { + var invoiceLineAuditEvent = createInvoiceLineAuditEvent(UUID.randomUUID().toString()); + + var saveFuture = invoiceLineEventsDao.save(invoiceLineAuditEvent, TENANT_ID); + saveFuture.onComplete(ar -> assertTrue(ar.succeeded())); + verify(postgresClientFactory, times(1)).createInstance(TENANT_ID); + } + + @Test + void shouldThrowConstraintViolation() { + var invoiceLineAuditEvent = createInvoiceLineAuditEvent(UUID.randomUUID().toString()); + + var saveFuture = invoiceLineEventsDao.save(invoiceLineAuditEvent, TENANT_ID); + saveFuture.onComplete(ar -> { + var reSaveFuture = invoiceLineEventsDao.save(invoiceLineAuditEvent, TENANT_ID); + reSaveFuture.onComplete(re -> { + assertTrue(re.failed()); + assertTrue(re.cause() instanceof PgException); + assertEquals("ERROR: duplicate key value violates unique constraint \"acquisition_invoice_line_log_pkey\" (23505)", re.cause().getMessage()); + }); + }); + verify(postgresClientFactory, times(1)).createInstance(TENANT_ID); + } + + @Test + void shouldGetCreatedEvent() { + var id = UUID.randomUUID().toString(); + var invoiceLineAuditEvent = createInvoiceLineAuditEvent(id); + + invoiceLineEventsDao.save(invoiceLineAuditEvent, TENANT_ID); + + var dto = invoiceLineEventsDao.getAuditEventsByInvoiceLineId(id, "action_date", "asc", 1, 1, TENANT_ID); + dto.onComplete(ar -> { + var invoiceLineAuditEventOptional = ar.result(); + var invoiceLineAuditEventList = invoiceLineAuditEventOptional.getInvoiceLineAuditEvents(); + + assertEquals(invoiceLineAuditEventList.get(0).getId(), id); + assertEquals(InvoiceLineAuditEvent.Action.CREATE.value(), invoiceLineAuditEventList.get(0).getAction().value()); + }); + verify(postgresClientFactory, times(2)).createInstance(TENANT_ID); + } +} diff --git a/mod-audit-server/src/test/java/org/folio/dao/OrderEventsDaoTest.java b/mod-audit-server/src/test/java/org/folio/dao/OrderEventsDaoTest.java index d052046e..36293f26 100644 --- a/mod-audit-server/src/test/java/org/folio/dao/OrderEventsDaoTest.java +++ b/mod-audit-server/src/test/java/org/folio/dao/OrderEventsDaoTest.java @@ -4,18 +4,15 @@ import static org.folio.utils.EntityUtils.createOrderAuditEvent; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; -import java.util.List; import java.util.UUID; -import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.pgclient.PgException; -import io.vertx.sqlclient.Row; -import io.vertx.sqlclient.RowSet; import org.folio.dao.acquisition.impl.OrderEventsDaoImpl; import org.folio.rest.jaxrs.model.OrderAuditEvent; -import org.folio.rest.jaxrs.model.OrderAuditEventCollection; import org.folio.util.PostgresClientFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -28,7 +25,7 @@ public class OrderEventsDaoTest { @Spy private PostgresClientFactory postgresClientFactory = new PostgresClientFactory(Vertx.vertx()); @InjectMocks - OrderEventsDaoImpl orderEventDao = new OrderEventsDaoImpl(postgresClientFactory); + OrderEventsDaoImpl orderEventDao; @BeforeEach public void setUp() { @@ -40,23 +37,25 @@ public void setUp() { void shouldCreateEventProcessed() { var orderAuditEvent = createOrderAuditEvent(UUID.randomUUID().toString()); - Future> saveFuture = orderEventDao.save(orderAuditEvent, TENANT_ID); + var saveFuture = orderEventDao.save(orderAuditEvent, TENANT_ID); saveFuture.onComplete(ar -> assertTrue(ar.succeeded())); + verify(postgresClientFactory, times(1)).createInstance(TENANT_ID); } @Test void shouldThrowConstraintViolation() { var orderAuditEvent = createOrderAuditEvent(UUID.randomUUID().toString()); - Future> saveFuture = orderEventDao.save(orderAuditEvent, TENANT_ID); + var saveFuture = orderEventDao.save(orderAuditEvent, TENANT_ID); saveFuture.onComplete(ar -> { - Future> reSaveFuture = orderEventDao.save(orderAuditEvent, TENANT_ID); + var reSaveFuture = orderEventDao.save(orderAuditEvent, TENANT_ID); reSaveFuture.onComplete(re -> { assertTrue(re.failed()); assertTrue(re.cause() instanceof PgException); assertEquals("ERROR: duplicate key value violates unique constraint \"acquisition_order_log_pkey\" (23505)", re.cause().getMessage()); }); }); + verify(postgresClientFactory, times(1)).createInstance(TENANT_ID); } @Test @@ -66,16 +65,16 @@ void shouldGetCreatedEvent() { orderEventDao.save(orderAuditEvent, TENANT_ID); - Future dto = orderEventDao.getAuditEventsByOrderId(id, "action_date", "desc", 1, 1, TENANT_ID); + var dto = orderEventDao.getAuditEventsByOrderId(id, "action_date", "desc", 1, 1, TENANT_ID); dto.onComplete(ar -> { - OrderAuditEventCollection orderAuditEventOptional = ar.result(); - List orderAuditEventList = orderAuditEventOptional.getOrderAuditEvents(); + var orderAuditEventOptional = ar.result(); + var orderAuditEventList = orderAuditEventOptional.getOrderAuditEvents(); assertEquals(orderAuditEventList.get(0).getId(), id); assertEquals(OrderAuditEvent.Action.CREATE.value(), orderAuditEventList.get(0).getAction().value()); }); + verify(postgresClientFactory, times(2)).createInstance(TENANT_ID); } - } diff --git a/mod-audit-server/src/test/java/org/folio/dao/OrderLineEventsDaoTest.java b/mod-audit-server/src/test/java/org/folio/dao/OrderLineEventsDaoTest.java index 88d9ae87..61c0961a 100644 --- a/mod-audit-server/src/test/java/org/folio/dao/OrderLineEventsDaoTest.java +++ b/mod-audit-server/src/test/java/org/folio/dao/OrderLineEventsDaoTest.java @@ -4,19 +4,15 @@ import static org.folio.utils.EntityUtils.createOrderLineAuditEvent; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; -import java.util.List; import java.util.UUID; -import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.pgclient.PgException; -import io.vertx.sqlclient.Row; -import io.vertx.sqlclient.RowSet; import org.folio.dao.acquisition.impl.OrderLineEventsDaoImpl; import org.folio.rest.jaxrs.model.OrderAuditEvent; -import org.folio.rest.jaxrs.model.OrderLineAuditEvent; -import org.folio.rest.jaxrs.model.OrderLineAuditEventCollection; import org.folio.util.PostgresClientFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -28,9 +24,8 @@ public class OrderLineEventsDaoTest { @Spy private PostgresClientFactory postgresClientFactory = new PostgresClientFactory(Vertx.vertx()); - @InjectMocks - OrderLineEventsDaoImpl orderLineEventsDao = new OrderLineEventsDaoImpl(postgresClientFactory); + OrderLineEventsDaoImpl orderLineEventsDao; @BeforeEach public void setUp() { @@ -41,42 +36,45 @@ public void setUp() { @Test void shouldCreateEventProcessed() { var orderLineAuditEvent = createOrderLineAuditEvent(UUID.randomUUID().toString()); - Future> saveFuture = orderLineEventsDao.save(orderLineAuditEvent, TENANT_ID); + + var saveFuture = orderLineEventsDao.save(orderLineAuditEvent, TENANT_ID); saveFuture.onComplete(ar -> assertTrue(ar.succeeded())); + verify(postgresClientFactory, times(1)).createInstance(TENANT_ID); } @Test void shouldThrowConstraintViolation() { var orderLineAuditEvent = createOrderLineAuditEvent(UUID.randomUUID().toString()); - Future> saveFuture = orderLineEventsDao.save(orderLineAuditEvent, TENANT_ID); + var saveFuture = orderLineEventsDao.save(orderLineAuditEvent, TENANT_ID); saveFuture.onComplete(ar -> { - Future> reSaveFuture = orderLineEventsDao.save(orderLineAuditEvent, TENANT_ID); + var reSaveFuture = orderLineEventsDao.save(orderLineAuditEvent, TENANT_ID); reSaveFuture.onComplete(re -> { assertTrue(re.failed()); assertTrue(re.cause() instanceof PgException); assertEquals("ERROR: duplicate key value violates unique constraint \"acquisition_order_line_log_pkey\" (23505)", re.cause().getMessage()); }); }); + verify(postgresClientFactory, times(1)).createInstance(TENANT_ID); } @Test void shouldGetCreatedEvent() { - String id = UUID.randomUUID().toString(); + var id = UUID.randomUUID().toString(); var orderLineAuditEvent = createOrderLineAuditEvent(id); orderLineEventsDao.save(orderLineAuditEvent, TENANT_ID); - Future dto = orderLineEventsDao.getAuditEventsByOrderLineId(id, "action_date", "asc", 1, 1, TENANT_ID); + var dto = orderLineEventsDao.getAuditEventsByOrderLineId(id, "action_date", "asc", 1, 1, TENANT_ID); dto.onComplete(ar -> { - OrderLineAuditEventCollection orderLineAuditEventOptional = ar.result(); - List orderLineAuditEventList = orderLineAuditEventOptional.getOrderLineAuditEvents(); + var orderLineAuditEventOptional = ar.result(); + var orderLineAuditEventList = orderLineAuditEventOptional.getOrderLineAuditEvents(); assertEquals(orderLineAuditEventList.get(0).getId(), id); assertEquals(OrderAuditEvent.Action.CREATE.value(), orderLineAuditEventList.get(0).getAction().value()); }); + verify(postgresClientFactory, times(2)).createInstance(TENANT_ID); } - } diff --git a/mod-audit-server/src/test/java/org/folio/dao/PieceEventsDaoTest.java b/mod-audit-server/src/test/java/org/folio/dao/PieceEventsDaoTest.java index 817d0b78..65bb7030 100644 --- a/mod-audit-server/src/test/java/org/folio/dao/PieceEventsDaoTest.java +++ b/mod-audit-server/src/test/java/org/folio/dao/PieceEventsDaoTest.java @@ -4,18 +4,15 @@ import static org.folio.utils.EntityUtils.createPieceAuditEvent; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; -import java.util.List; import java.util.UUID; -import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.pgclient.PgException; -import io.vertx.sqlclient.Row; -import io.vertx.sqlclient.RowSet; import org.folio.dao.acquisition.impl.PieceEventsDaoImpl; import org.folio.rest.jaxrs.model.PieceAuditEvent; -import org.folio.rest.jaxrs.model.PieceAuditEventCollection; import org.folio.util.PostgresClientFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,38 +37,42 @@ public void setUp() { void shouldCreateEventProcessed() { var pieceAuditEvent = createPieceAuditEvent(UUID.randomUUID().toString()); - Future> saveFuture = pieceEventsDao.save(pieceAuditEvent, TENANT_ID); + var saveFuture = pieceEventsDao.save(pieceAuditEvent, TENANT_ID); saveFuture.onComplete(ar -> assertTrue(ar.succeeded())); + verify(postgresClientFactory, times(1)).createInstance(TENANT_ID); } @Test void shouldThrowConstrainViolation() { var pieceAuditEvent = createPieceAuditEvent(UUID.randomUUID().toString()); - Future> saveFuture = pieceEventsDao.save(pieceAuditEvent, TENANT_ID); + var saveFuture = pieceEventsDao.save(pieceAuditEvent, TENANT_ID); saveFuture.onComplete(ar -> { - Future> reSaveFuture = pieceEventsDao.save(pieceAuditEvent, TENANT_ID); + var reSaveFuture = pieceEventsDao.save(pieceAuditEvent, TENANT_ID); reSaveFuture.onComplete(re -> { assertTrue(re.failed()); assertTrue(re.cause() instanceof PgException); assertEquals("ERROR: duplicate key value violates unique constraint \"acquisition_piece_log_pkey\" (23505)", re.cause().getMessage()); }); }); + verify(postgresClientFactory, times(1)).createInstance(TENANT_ID); } @Test void shouldGetCreatedEvent() { - String id = UUID.randomUUID().toString(); + var id = UUID.randomUUID().toString(); var pieceAuditEvent = createPieceAuditEvent(id); pieceEventsDao.save(pieceAuditEvent, TENANT_ID); - Future saveFuture = pieceEventsDao.getAuditEventsByPieceId(id, "action_date", "desc", 1, 1, TENANT_ID); + var saveFuture = pieceEventsDao.getAuditEventsByPieceId(id, "action_date", "desc", 1, 1, TENANT_ID); saveFuture.onComplete(ar -> { - PieceAuditEventCollection pieceAuditEventCollection = ar.result(); - List pieceAuditEventList = pieceAuditEventCollection.getPieceAuditEvents(); + var pieceAuditEventCollection = ar.result(); + var pieceAuditEventList = pieceAuditEventCollection.getPieceAuditEvents(); + assertEquals(pieceAuditEventList.get(0).getId(), id); assertEquals(PieceAuditEvent.Action.CREATE.value(), pieceAuditEventList.get(0).getAction().value()); }); + verify(postgresClientFactory, times(2)).createInstance(TENANT_ID); } } diff --git a/mod-audit-server/src/test/java/org/folio/rest/impl/AuditDataAcquisitionAPITest.java b/mod-audit-server/src/test/java/org/folio/rest/impl/AuditDataAcquisitionAPITest.java index 63f97052..c14a4199 100644 --- a/mod-audit-server/src/test/java/org/folio/rest/impl/AuditDataAcquisitionAPITest.java +++ b/mod-audit-server/src/test/java/org/folio/rest/impl/AuditDataAcquisitionAPITest.java @@ -18,10 +18,12 @@ import io.vertx.core.json.JsonObject; import org.folio.CopilotGenerated; import org.folio.dao.acquisition.impl.InvoiceEventsDaoImpl; +import org.folio.dao.acquisition.impl.InvoiceLineEventsDaoImpl; import org.folio.dao.acquisition.impl.OrderEventsDaoImpl; import org.folio.dao.acquisition.impl.OrderLineEventsDaoImpl; import org.folio.dao.acquisition.impl.PieceEventsDaoImpl; import org.folio.rest.jaxrs.model.InvoiceAuditEvent; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent; import org.folio.rest.jaxrs.model.OrderAuditEvent; import org.folio.rest.jaxrs.model.OrderLineAuditEvent; import org.folio.rest.jaxrs.model.PieceAuditEvent; @@ -43,6 +45,7 @@ public class AuditDataAcquisitionAPITest extends ApiTestBase { private static final String ACQ_AUDIT_PIECE_PATH = "/audit-data/acquisition/piece/"; private static final String ACQ_AUDIT_PIECE_STATUS_CHANGE_HISTORY_PATH = "/status-change-history"; private static final String ACQ_AUDIT_INVOICE_PATH = "/audit-data/acquisition/invoice/"; + private static final String ACQ_AUDIT_INVOICE_LINE_PATH = "/audit-data/acquisition/invoice-line/"; private static final String TENANT_ID = "modaudittest"; @Spy @@ -56,6 +59,8 @@ public class AuditDataAcquisitionAPITest extends ApiTestBase { PieceEventsDaoImpl pieceEventsDao; @InjectMocks InvoiceEventsDaoImpl invoiceEventsDao; + @InjectMocks + InvoiceLineEventsDaoImpl invoiceLineEventsDao; @BeforeEach public void setUp() { @@ -63,6 +68,7 @@ public void setUp() { orderEventDao = new OrderEventsDaoImpl(postgresClientFactory); orderLineEventDao = new OrderLineEventsDaoImpl(postgresClientFactory); invoiceEventsDao = new InvoiceEventsDaoImpl(postgresClientFactory); + invoiceLineEventsDao = new InvoiceLineEventsDaoImpl(postgresClientFactory); } @Test @@ -299,4 +305,42 @@ void shouldReturnInvoiceEventsOnGetByInvoiceId() { .then().log().all().statusCode(500) .body(containsString("UUID string too large")); } + + @Test + void shouldReturnInvoiceLineEventsOnGetByInvoiceLineId() { + JsonObject jsonObject = new JsonObject(); + jsonObject.put("name", "Test Product2"); + + InvoiceLineAuditEvent invoiceLineAuditEvent = new InvoiceLineAuditEvent() + .withId(UUID.randomUUID().toString()) + .withAction(InvoiceLineAuditEvent.Action.CREATE) + .withInvoiceId(UUID.randomUUID().toString()) + .withInvoiceLineId(UUID.randomUUID().toString()) + .withUserId(UUID.randomUUID().toString()) + .withEventDate(new Date()) + .withActionDate(new Date()) + .withInvoiceLineSnapshot(jsonObject); + + invoiceLineEventsDao.save(invoiceLineAuditEvent, TENANT_ID).onComplete(v -> { + given().header(CONTENT_TYPE).header(TENANT).header(PERMS).get(ACQ_AUDIT_INVOICE_LINE_PATH + INVALID_ID) + .then().log().all().statusCode(200) + .body(containsString("invoiceLineAuditEvents")).body(containsString("totalItems")); + + given().header(CONTENT_TYPE).header(TENANT).header(PERMS).get(ACQ_AUDIT_INVOICE_LINE_PATH + invoiceLineAuditEvent.getInvoiceLineId()) + .then().log().all().statusCode(200) + .body(containsString(invoiceLineAuditEvent.getInvoiceLineId())); + + given().header(CONTENT_TYPE).header(TENANT).header(PERMS).get(ACQ_AUDIT_INVOICE_LINE_PATH + invoiceLineAuditEvent.getInvoiceLineId() + "?limit=1") + .then().log().all().statusCode(200) + .body(containsString(invoiceLineAuditEvent.getInvoiceLineId())); + + given().header(CONTENT_TYPE).header(TENANT).header(PERMS).get(ACQ_AUDIT_INVOICE_LINE_PATH + invoiceLineAuditEvent.getInvoiceLineId() + "?sortBy=action_date") + .then().log().all().statusCode(200) + .body(containsString(invoiceLineAuditEvent.getInvoiceLineId())); + + given().header(CONTENT_TYPE).header(TENANT).header(PERMS).get(ACQ_AUDIT_INVOICE_LINE_PATH + invoiceLineAuditEvent.getInvoiceLineId() + 123) + .then().log().all().statusCode(500) + .body(containsString("UUID string too large")); + }); + } } diff --git a/mod-audit-server/src/test/java/org/folio/rest/impl/InvoiceLineEventsHandlerMockTest.java b/mod-audit-server/src/test/java/org/folio/rest/impl/InvoiceLineEventsHandlerMockTest.java new file mode 100644 index 00000000..1da80381 --- /dev/null +++ b/mod-audit-server/src/test/java/org/folio/rest/impl/InvoiceLineEventsHandlerMockTest.java @@ -0,0 +1,88 @@ +package org.folio.rest.impl; + +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.core.json.Json; +import io.vertx.core.json.JsonObject; +import io.vertx.kafka.client.consumer.KafkaConsumerRecord; +import io.vertx.kafka.client.consumer.impl.KafkaConsumerRecordImpl; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.common.header.internals.RecordHeader; +import org.folio.CopilotGenerated; +import org.folio.dao.acquisition.impl.InvoiceLineEventsDaoImpl; +import org.folio.kafka.KafkaTopicNameHelper; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent; +import org.folio.rest.util.OkapiConnectionParams; +import org.folio.services.acquisition.impl.InvoiceLineAuditEventsServiceImpl; +import org.folio.util.PostgresClientFactory; +import org.folio.verticle.acquisition.consumers.InvoiceLineEventsHandler; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Spy; + +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.UUID; + +import static org.folio.kafka.KafkaTopicNameHelper.getDefaultNameSpace; + +@CopilotGenerated(partiallyGenerated = true) +public class InvoiceLineEventsHandlerMockTest { + + private static final String TENANT_ID = "diku"; + protected static final String TOKEN = "token"; + private static final String KAFKA_ENV = "folio"; + + public static final String OKAPI_TOKEN_HEADER = "x-okapi-token"; + public static final String OKAPI_URL_HEADER = "x-okapi-url"; + + @Spy + private Vertx vertx = Vertx.vertx(); + + @Spy + private PostgresClientFactory postgresClientFactory = new PostgresClientFactory(Vertx.vertx()); + + @InjectMocks + InvoiceLineEventsDaoImpl invoiceLineEventsDao = new InvoiceLineEventsDaoImpl(postgresClientFactory); + + @InjectMocks + InvoiceLineAuditEventsServiceImpl invoiceLineAuditEventServiceImpl = new InvoiceLineAuditEventsServiceImpl(invoiceLineEventsDao); + + @InjectMocks + private InvoiceLineEventsHandler invoiceLineEventsHandler = new InvoiceLineEventsHandler(vertx, invoiceLineAuditEventServiceImpl); + + @Test + void shouldProcessEvent() { + JsonObject jsonObject = new JsonObject(); + jsonObject.put("Test", "TestValue"); + + InvoiceLineAuditEvent invoiceLineAuditEvent = new InvoiceLineAuditEvent() + .withId(UUID.randomUUID().toString()) + .withInvoiceLineId(UUID.randomUUID().toString()) + .withEventDate(new Date()) + .withInvoiceId(UUID.randomUUID().toString()) + .withActionDate(new Date()) + .withAction(InvoiceLineAuditEvent.Action.CREATE) + .withInvoiceLineSnapshot(jsonObject) + .withUserId(UUID.randomUUID().toString()); + KafkaConsumerRecord kafkaConsumerRecord = buildKafkaConsumerRecord(invoiceLineAuditEvent); + + Future saveFuture = invoiceLineEventsHandler.handle(kafkaConsumerRecord); + saveFuture.onComplete(ar -> Assertions.assertTrue(ar.succeeded())); + } + + private KafkaConsumerRecord buildKafkaConsumerRecord(InvoiceLineAuditEvent kafkaConsumerRecord) { + String topic = KafkaTopicNameHelper.formatTopicName(KAFKA_ENV, getDefaultNameSpace(), TENANT_ID, kafkaConsumerRecord.getAction().toString()); + ConsumerRecord consumerRecord = buildConsumerRecord(topic, kafkaConsumerRecord); + return new KafkaConsumerRecordImpl<>(consumerRecord); + } + + protected ConsumerRecord buildConsumerRecord(String topic, InvoiceLineAuditEvent event) { + ConsumerRecord consumerRecord = new ConsumerRecord<>(topic, 0, 0, topic, Json.encode(event)); + consumerRecord.headers().add(new RecordHeader(OkapiConnectionParams.OKAPI_TENANT_HEADER, TENANT_ID.getBytes(StandardCharsets.UTF_8))); + consumerRecord.headers().add(new RecordHeader(OKAPI_URL_HEADER, ("http://localhost:" + 8080).getBytes(StandardCharsets.UTF_8))); + consumerRecord.headers().add(new RecordHeader(OKAPI_TOKEN_HEADER, TOKEN.getBytes(StandardCharsets.UTF_8))); + return consumerRecord; + } +} diff --git a/mod-audit-server/src/test/java/org/folio/rest/impl/OrderLineEventsHandlerMockTest.java b/mod-audit-server/src/test/java/org/folio/rest/impl/OrderLineEventsHandlerMockTest.java index 6abf7639..eccd6d62 100644 --- a/mod-audit-server/src/test/java/org/folio/rest/impl/OrderLineEventsHandlerMockTest.java +++ b/mod-audit-server/src/test/java/org/folio/rest/impl/OrderLineEventsHandlerMockTest.java @@ -73,9 +73,9 @@ void shouldProcessEvent() { }); } - private KafkaConsumerRecord buildKafkaConsumerRecord(OrderLineAuditEvent record) { - String topic = KafkaTopicNameHelper.formatTopicName(KAFKA_ENV, getDefaultNameSpace(), TENANT_ID, record.getAction().toString()); - ConsumerRecord consumerRecord = buildConsumerRecord(topic, record); + private KafkaConsumerRecord buildKafkaConsumerRecord(OrderLineAuditEvent kafkaConsumerRecord) { + String topic = KafkaTopicNameHelper.formatTopicName(KAFKA_ENV, getDefaultNameSpace(), TENANT_ID, kafkaConsumerRecord.getAction().toString()); + ConsumerRecord consumerRecord = buildConsumerRecord(topic, kafkaConsumerRecord); return new KafkaConsumerRecordImpl<>(consumerRecord); } diff --git a/mod-audit-server/src/test/java/org/folio/services/InvoiceAuditEventsServiceTest.java b/mod-audit-server/src/test/java/org/folio/services/InvoiceAuditEventsServiceTest.java index 83f4c5db..f7e06bb9 100644 --- a/mod-audit-server/src/test/java/org/folio/services/InvoiceAuditEventsServiceTest.java +++ b/mod-audit-server/src/test/java/org/folio/services/InvoiceAuditEventsServiceTest.java @@ -1,9 +1,20 @@ package org.folio.services; +import static org.folio.utils.EntityUtils.ACTION_DATE_SORT_BY; +import static org.folio.utils.EntityUtils.DESC_ORDER; +import static org.folio.utils.EntityUtils.LIMIT; +import static org.folio.utils.EntityUtils.OFFSET; import static org.folio.utils.EntityUtils.TENANT_ID; import static org.folio.utils.EntityUtils.createInvoiceAuditEvent; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import java.util.List; import java.util.UUID; @@ -12,53 +23,70 @@ import io.vertx.core.Vertx; import io.vertx.sqlclient.Row; import io.vertx.sqlclient.RowSet; +import io.vertx.sqlclient.Tuple; import org.folio.CopilotGenerated; import org.folio.dao.acquisition.InvoiceEventsDao; import org.folio.dao.acquisition.impl.InvoiceEventsDaoImpl; import org.folio.rest.jaxrs.model.InvoiceAuditEvent; import org.folio.rest.jaxrs.model.InvoiceAuditEventCollection; +import org.folio.rest.persist.PostgresClient; import org.folio.services.acquisition.impl.InvoiceAuditEventsServiceImpl; import org.folio.util.PostgresClientFactory; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Spy; +import org.mockito.MockitoAnnotations; @CopilotGenerated public class InvoiceAuditEventsServiceTest { - @Spy - private PostgresClientFactory postgresClientFactory = new PostgresClientFactory(Vertx.vertx()); @Mock - InvoiceEventsDao invoiceEventsDao = new InvoiceEventsDaoImpl(postgresClientFactory); + private RowSet rowSet; + @Mock + private PostgresClient postgresClient; + + private InvoiceEventsDao invoiceEventsDao; + private InvoiceAuditEventsServiceImpl invoiceAuditEventService; - @InjectMocks - InvoiceAuditEventsServiceImpl invoiceAuditEventService = new InvoiceAuditEventsServiceImpl(invoiceEventsDao); + @BeforeEach + public void setUp() throws Exception { + try (var ignored = MockitoAnnotations.openMocks(this)) { + var postgresClientFactory = spy(new PostgresClientFactory(Vertx.vertx())); + invoiceEventsDao = spy(new InvoiceEventsDaoImpl(postgresClientFactory)); + invoiceAuditEventService = new InvoiceAuditEventsServiceImpl(invoiceEventsDao); + + doReturn(postgresClient).when(postgresClientFactory).createInstance(TENANT_ID); + } + } @Test void shouldCallDaoForSuccessfulCase() { var invoiceAuditEvent = createInvoiceAuditEvent(UUID.randomUUID().toString()); + doReturn(Future.succeededFuture(rowSet)).when(postgresClient).execute(anyString(), any(Tuple.class)); - Future> saveFuture = invoiceAuditEventService.saveInvoiceAuditEvent(invoiceAuditEvent, TENANT_ID); - saveFuture.onComplete(ar -> { - assertTrue(ar.succeeded()); - }); + var saveFuture = invoiceAuditEventService.saveInvoiceAuditEvent(invoiceAuditEvent, TENANT_ID); + saveFuture.onComplete(asyncResult -> assertTrue(asyncResult.succeeded())); + + verify(invoiceEventsDao, times(1)).save(invoiceAuditEvent, TENANT_ID); } @Test void shouldGetDto() { - String id = UUID.randomUUID().toString(); + var id = UUID.randomUUID().toString(); var invoiceAuditEvent = createInvoiceAuditEvent(id); + var invoiceAuditEventCollection = new InvoiceAuditEventCollection().withInvoiceAuditEvents(List.of(invoiceAuditEvent)).withTotalItems(1); - invoiceAuditEventService.saveInvoiceAuditEvent(invoiceAuditEvent, TENANT_ID); + doReturn(Future.succeededFuture(invoiceAuditEventCollection)).when(invoiceEventsDao).getAuditEventsByInvoiceId(anyString(), anyString(), anyString(), anyInt(), anyInt(), anyString()); - Future dto = invoiceAuditEventService.getAuditEventsByInvoiceId(id, "action_date", "asc", 1, 1, TENANT_ID); - dto.onComplete(ar -> { - InvoiceAuditEventCollection invoiceAuditEventOptional = ar.result(); - List invoiceAuditEventList = invoiceAuditEventOptional.getInvoiceAuditEvents(); + var dto = invoiceAuditEventService.getAuditEventsByInvoiceId(id, ACTION_DATE_SORT_BY, DESC_ORDER, LIMIT, OFFSET, TENANT_ID); + dto.onComplete(asyncResult -> { + var invoiceAuditEventOptional = asyncResult.result(); + var invoiceAuditEventList = invoiceAuditEventOptional.getInvoiceAuditEvents(); assertEquals(invoiceAuditEventList.get(0).getId(), id); - assertEquals(InvoiceAuditEvent.Action.CREATE.value(), invoiceAuditEventList.get(0).getAction().value()); + assertEquals(InvoiceAuditEvent.Action.CREATE, invoiceAuditEventList.get(0).getAction()); }); + + verify(invoiceEventsDao, times(1)).getAuditEventsByInvoiceId(id, ACTION_DATE_SORT_BY, DESC_ORDER, LIMIT, OFFSET, TENANT_ID); } } diff --git a/mod-audit-server/src/test/java/org/folio/services/InvoiceLineAuditEventsServiceTest.java b/mod-audit-server/src/test/java/org/folio/services/InvoiceLineAuditEventsServiceTest.java new file mode 100644 index 00000000..f9920063 --- /dev/null +++ b/mod-audit-server/src/test/java/org/folio/services/InvoiceLineAuditEventsServiceTest.java @@ -0,0 +1,92 @@ +package org.folio.services; + +import static org.folio.utils.EntityUtils.ACTION_DATE_SORT_BY; +import static org.folio.utils.EntityUtils.DESC_ORDER; +import static org.folio.utils.EntityUtils.LIMIT; +import static org.folio.utils.EntityUtils.OFFSET; +import static org.folio.utils.EntityUtils.TENANT_ID; +import static org.folio.utils.EntityUtils.createInvoiceLineAuditEvent; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.List; +import java.util.UUID; + +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.RowSet; +import io.vertx.sqlclient.Tuple; +import org.folio.CopilotGenerated; +import org.folio.dao.acquisition.InvoiceLineEventsDao; +import org.folio.dao.acquisition.impl.InvoiceLineEventsDaoImpl; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEventCollection; +import org.folio.rest.persist.PostgresClient; +import org.folio.services.acquisition.impl.InvoiceLineAuditEventsServiceImpl; +import org.folio.util.PostgresClientFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@CopilotGenerated +public class InvoiceLineAuditEventsServiceTest { + + @Mock + private RowSet rowSet; + @Mock + private PostgresClient postgresClient; + + private InvoiceLineEventsDao invoiceLineEventsDao; + private InvoiceLineAuditEventsServiceImpl invoiceLineAuditEventService; + + @BeforeEach + public void setUp() throws Exception { + try (var ignored = MockitoAnnotations.openMocks(this)) { + var postgresClientFactory = spy(new PostgresClientFactory(Vertx.vertx())); + invoiceLineEventsDao = spy(new InvoiceLineEventsDaoImpl(postgresClientFactory)); + invoiceLineAuditEventService = new InvoiceLineAuditEventsServiceImpl(invoiceLineEventsDao); + + doReturn(postgresClient).when(postgresClientFactory).createInstance(TENANT_ID); + } + } + + @Test + public void shouldCallDaoForSuccessfulCase() { + var invoiceLineAuditEvent = createInvoiceLineAuditEvent(UUID.randomUUID().toString()); + doReturn(Future.succeededFuture(rowSet)).when(postgresClient).execute(anyString(), any(Tuple.class)); + + var saveFuture = invoiceLineAuditEventService.saveInvoiceLineAuditEvent(invoiceLineAuditEvent, TENANT_ID); + saveFuture.onComplete(asyncResult -> assertTrue(asyncResult.succeeded())); + + verify(invoiceLineEventsDao, times(1)).save(invoiceLineAuditEvent, TENANT_ID); + } + + @Test + void shouldGetInvoiceLineDto() { + var id = UUID.randomUUID().toString(); + var invoiceLineAuditEvent = createInvoiceLineAuditEvent(id); + var invoiceLineAuditEventCollection = new InvoiceLineAuditEventCollection().withInvoiceLineAuditEvents(List.of(invoiceLineAuditEvent)).withTotalItems(1); + + doReturn(Future.succeededFuture(invoiceLineAuditEventCollection)).when(invoiceLineEventsDao).getAuditEventsByInvoiceLineId(anyString(), anyString(), anyString(), anyInt(), anyInt(), anyString()); + + var dto = invoiceLineAuditEventService.getAuditEventsByInvoiceLineId(id, ACTION_DATE_SORT_BY, DESC_ORDER, LIMIT, OFFSET, TENANT_ID); + dto.onComplete(asyncResult -> { + var invoiceLineAuditEventOptional = asyncResult.result(); + var invoiceLineAuditEventList = invoiceLineAuditEventOptional.getInvoiceLineAuditEvents(); + + assertEquals(invoiceLineAuditEventList.get(0).getId(), id); + assertEquals(InvoiceLineAuditEvent.Action.CREATE, invoiceLineAuditEventList.get(0).getAction()); + }); + + verify(invoiceLineEventsDao, times(1)).getAuditEventsByInvoiceLineId(id, ACTION_DATE_SORT_BY, DESC_ORDER, LIMIT, OFFSET, TENANT_ID); + } +} diff --git a/mod-audit-server/src/test/java/org/folio/services/OrderAuditEventsServiceTest.java b/mod-audit-server/src/test/java/org/folio/services/OrderAuditEventsServiceTest.java index 2bd3904c..0001f9a8 100644 --- a/mod-audit-server/src/test/java/org/folio/services/OrderAuditEventsServiceTest.java +++ b/mod-audit-server/src/test/java/org/folio/services/OrderAuditEventsServiceTest.java @@ -1,9 +1,20 @@ package org.folio.services; +import static org.folio.utils.EntityUtils.ACTION_DATE_SORT_BY; +import static org.folio.utils.EntityUtils.DESC_ORDER; +import static org.folio.utils.EntityUtils.LIMIT; +import static org.folio.utils.EntityUtils.OFFSET; import static org.folio.utils.EntityUtils.TENANT_ID; import static org.folio.utils.EntityUtils.createOrderAuditEvent; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import java.util.List; import java.util.UUID; @@ -12,52 +23,68 @@ import io.vertx.core.Vertx; import io.vertx.sqlclient.Row; import io.vertx.sqlclient.RowSet; +import io.vertx.sqlclient.Tuple; import org.folio.dao.acquisition.OrderEventsDao; import org.folio.dao.acquisition.impl.OrderEventsDaoImpl; import org.folio.rest.jaxrs.model.OrderAuditEvent; import org.folio.rest.jaxrs.model.OrderAuditEventCollection; +import org.folio.rest.persist.PostgresClient; import org.folio.services.acquisition.impl.OrderAuditEventsServiceImpl; import org.folio.util.PostgresClientFactory; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Spy; +import org.mockito.MockitoAnnotations; public class OrderAuditEventsServiceTest { - @Spy - private PostgresClientFactory postgresClientFactory = new PostgresClientFactory(Vertx.vertx()); @Mock - OrderEventsDao orderEventsDao = new OrderEventsDaoImpl(postgresClientFactory); + private RowSet rowSet; + @Mock + private PostgresClient postgresClient; + + private OrderEventsDao orderEventsDao; + private OrderAuditEventsServiceImpl orderAuditEventService; - @InjectMocks - OrderAuditEventsServiceImpl orderAuditEventService = new OrderAuditEventsServiceImpl(orderEventsDao); + @BeforeEach + public void setUp() throws Exception { + try (var ignored = MockitoAnnotations.openMocks(this)) { + var postgresClientFactory = spy(new PostgresClientFactory(Vertx.vertx())); + orderEventsDao = spy(new OrderEventsDaoImpl(postgresClientFactory)); + orderAuditEventService = new OrderAuditEventsServiceImpl(orderEventsDao); + + doReturn(postgresClient).when(postgresClientFactory).createInstance(TENANT_ID); + } + } @Test void shouldCallDaoForSuccessfulCase() { var orderAuditEvent = createOrderAuditEvent(UUID.randomUUID().toString()); + doReturn(Future.succeededFuture(rowSet)).when(postgresClient).execute(anyString(), any(Tuple.class)); - Future> saveFuture = orderAuditEventService.saveOrderAuditEvent(orderAuditEvent, TENANT_ID); - saveFuture.onComplete(ar -> { - assertTrue(ar.succeeded()); - }); + var saveFuture = orderAuditEventService.saveOrderAuditEvent(orderAuditEvent, TENANT_ID); + saveFuture.onComplete(asyncResult -> assertTrue(asyncResult.succeeded())); + + verify(orderEventsDao, times(1)).save(orderAuditEvent, TENANT_ID); } @Test void shouldGetDto() { - String id = UUID.randomUUID().toString(); + var id = UUID.randomUUID().toString(); var orderAuditEvent = createOrderAuditEvent(id); + var orderAuditEventCollection = new OrderAuditEventCollection().withOrderAuditEvents(List.of(orderAuditEvent)).withTotalItems(1); - orderAuditEventService.saveOrderAuditEvent(orderAuditEvent, TENANT_ID); + doReturn(Future.succeededFuture(orderAuditEventCollection)).when(orderEventsDao).getAuditEventsByOrderId(anyString(), anyString(), anyString(), anyInt(), anyInt(), anyString()); - Future dto = orderAuditEventService.getAuditEventsByOrderId(id, "action_date", "asc", 1, 1, TENANT_ID); - dto.onComplete(ar -> { - OrderAuditEventCollection orderAuditEventOptional = ar.result(); - List orderAuditEventList = orderAuditEventOptional.getOrderAuditEvents(); + var dto = orderAuditEventService.getAuditEventsByOrderId(id, ACTION_DATE_SORT_BY, DESC_ORDER, LIMIT, OFFSET, TENANT_ID); + dto.onComplete(asyncResult -> { + var orderAuditEventOptional = asyncResult.result(); + var orderAuditEventList = orderAuditEventOptional.getOrderAuditEvents(); assertEquals(orderAuditEventList.get(0).getId(), id); - assertEquals(OrderAuditEvent.Action.CREATE.value(), orderAuditEventList.get(0).getAction().value()); + assertEquals(OrderAuditEvent.Action.CREATE, orderAuditEventList.get(0).getAction()); }); - } + verify(orderEventsDao, times(1)).getAuditEventsByOrderId(id, ACTION_DATE_SORT_BY, DESC_ORDER, LIMIT, OFFSET, TENANT_ID); + } } diff --git a/mod-audit-server/src/test/java/org/folio/services/OrderLineAuditEventsServiceTest.java b/mod-audit-server/src/test/java/org/folio/services/OrderLineAuditEventsServiceTest.java index 272c9816..4d5412ca 100644 --- a/mod-audit-server/src/test/java/org/folio/services/OrderLineAuditEventsServiceTest.java +++ b/mod-audit-server/src/test/java/org/folio/services/OrderLineAuditEventsServiceTest.java @@ -1,64 +1,90 @@ package org.folio.services; +import static org.folio.utils.EntityUtils.ACTION_DATE_SORT_BY; +import static org.folio.utils.EntityUtils.DESC_ORDER; +import static org.folio.utils.EntityUtils.LIMIT; +import static org.folio.utils.EntityUtils.OFFSET; +import static org.folio.utils.EntityUtils.TENANT_ID; +import static org.folio.utils.EntityUtils.createOrderLineAuditEvent; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.List; +import java.util.UUID; + import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.sqlclient.Row; import io.vertx.sqlclient.RowSet; +import io.vertx.sqlclient.Tuple; import org.folio.dao.acquisition.OrderLineEventsDao; import org.folio.dao.acquisition.impl.OrderLineEventsDaoImpl; import org.folio.rest.jaxrs.model.OrderLineAuditEvent; import org.folio.rest.jaxrs.model.OrderLineAuditEventCollection; +import org.folio.rest.persist.PostgresClient; import org.folio.services.acquisition.impl.OrderLineAuditEventsServiceImpl; import org.folio.util.PostgresClientFactory; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Spy; - -import java.util.List; -import java.util.UUID; - -import static org.folio.utils.EntityUtils.TENANT_ID; -import static org.folio.utils.EntityUtils.createOrderLineAuditEvent; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.mockito.MockitoAnnotations; public class OrderLineAuditEventsServiceTest { - @Spy - private PostgresClientFactory postgresClientFactory = new PostgresClientFactory(Vertx.vertx()); @Mock - OrderLineEventsDao orderLineEventsDao = new OrderLineEventsDaoImpl(postgresClientFactory); - @InjectMocks - OrderLineAuditEventsServiceImpl orderLineAuditEventService = new OrderLineAuditEventsServiceImpl(orderLineEventsDao); + private RowSet rowSet; + @Mock + private PostgresClient postgresClient; + + private OrderLineEventsDao orderLineEventsDao; + private OrderLineAuditEventsServiceImpl orderLineAuditEventService; + + @BeforeEach + public void setUp() throws Exception { + try (var ignored = MockitoAnnotations.openMocks(this)) { + var postgresClientFactory = spy(new PostgresClientFactory(Vertx.vertx())); + orderLineEventsDao = spy(new OrderLineEventsDaoImpl(postgresClientFactory)); + orderLineAuditEventService = new OrderLineAuditEventsServiceImpl(orderLineEventsDao); + + doReturn(postgresClient).when(postgresClientFactory).createInstance(TENANT_ID); + } + } @Test public void shouldCallDaoForSuccessfulCase() { var orderLineAuditEvent = createOrderLineAuditEvent(UUID.randomUUID().toString()); + doReturn(Future.succeededFuture(rowSet)).when(postgresClient).execute(anyString(), any(Tuple.class)); - Future> saveFuture = orderLineAuditEventService.saveOrderLineAuditEvent(orderLineAuditEvent, TENANT_ID); + var saveFuture = orderLineAuditEventService.saveOrderLineAuditEvent(orderLineAuditEvent, TENANT_ID); + saveFuture.onComplete(asyncResult -> assertTrue(asyncResult.succeeded())); - saveFuture.onComplete(ar -> { - assertTrue(ar.succeeded()); - }); + verify(orderLineEventsDao, times(1)).save(orderLineAuditEvent, TENANT_ID); } @Test void shouldGetOrderLineDto() { - String id = UUID.randomUUID().toString(); + var id = UUID.randomUUID().toString(); var orderLineAuditEvent = createOrderLineAuditEvent(id); + var orderLineAuditEventCollection = new OrderLineAuditEventCollection().withOrderLineAuditEvents(List.of(orderLineAuditEvent)).withTotalItems(1); - orderLineAuditEventService.saveOrderLineAuditEvent(orderLineAuditEvent, TENANT_ID); + doReturn(Future.succeededFuture(orderLineAuditEventCollection)).when(orderLineEventsDao).getAuditEventsByOrderLineId(anyString(), anyString(), anyString(), anyInt(), anyInt(), anyString()); - Future dto = orderLineAuditEventService.getAuditEventsByOrderLineId(id, "action_date", "desc", 1, 1, TENANT_ID); - dto.onComplete(ar -> { - OrderLineAuditEventCollection orderLineAuditEventOptional = ar.result(); - List orderLineAuditEventList = orderLineAuditEventOptional.getOrderLineAuditEvents(); + var dto = orderLineAuditEventService.getAuditEventsByOrderLineId(id, ACTION_DATE_SORT_BY, DESC_ORDER, LIMIT, OFFSET, TENANT_ID); + dto.onComplete(asyncResult -> { + var orderLineAuditEventOptional = asyncResult.result(); + var orderLineAuditEventList = orderLineAuditEventOptional.getOrderLineAuditEvents(); assertEquals(orderLineAuditEventList.get(0).getId(), id); assertEquals(OrderLineAuditEvent.Action.CREATE, orderLineAuditEventList.get(0).getAction()); - }); - } + verify(orderLineEventsDao, times(1)).getAuditEventsByOrderLineId(id, ACTION_DATE_SORT_BY, DESC_ORDER, LIMIT, OFFSET, TENANT_ID); + } } diff --git a/mod-audit-server/src/test/java/org/folio/services/PieceAuditEventsServiceTest.java b/mod-audit-server/src/test/java/org/folio/services/PieceAuditEventsServiceTest.java index df2af446..b1e24561 100644 --- a/mod-audit-server/src/test/java/org/folio/services/PieceAuditEventsServiceTest.java +++ b/mod-audit-server/src/test/java/org/folio/services/PieceAuditEventsServiceTest.java @@ -1,40 +1,90 @@ package org.folio.services; +import static org.folio.utils.EntityUtils.ACTION_DATE_SORT_BY; +import static org.folio.utils.EntityUtils.DESC_ORDER; +import static org.folio.utils.EntityUtils.LIMIT; +import static org.folio.utils.EntityUtils.OFFSET; import static org.folio.utils.EntityUtils.TENANT_ID; import static org.folio.utils.EntityUtils.createPieceAuditEvent; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import java.util.List; import java.util.UUID; import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.sqlclient.Row; import io.vertx.sqlclient.RowSet; +import io.vertx.sqlclient.Tuple; import org.folio.dao.acquisition.PieceEventsDao; import org.folio.dao.acquisition.impl.PieceEventsDaoImpl; +import org.folio.rest.jaxrs.model.PieceAuditEvent; +import org.folio.rest.jaxrs.model.PieceAuditEventCollection; +import org.folio.rest.persist.PostgresClient; import org.folio.services.acquisition.impl.PieceAuditEventsServiceImpl; import org.folio.util.PostgresClientFactory; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Spy; +import org.mockito.MockitoAnnotations; public class PieceAuditEventsServiceTest { - @Spy - private PostgresClientFactory postgresClientFactory = new PostgresClientFactory(Vertx.vertx()); @Mock - PieceEventsDao pieceEventsDao = new PieceEventsDaoImpl(postgresClientFactory); - @InjectMocks - PieceAuditEventsServiceImpl pieceAuditEventsService = new PieceAuditEventsServiceImpl(pieceEventsDao); + private RowSet rowSet; + @Mock + private PostgresClient postgresClient; + + private PieceEventsDao pieceEventsDao; + private PieceAuditEventsServiceImpl pieceAuditEventsService; + + @BeforeEach + public void setUp() throws Exception { + try (var ignored = MockitoAnnotations.openMocks(this)) { + var postgresClientFactory = spy(new PostgresClientFactory(Vertx.vertx())); + pieceEventsDao = spy(new PieceEventsDaoImpl(postgresClientFactory)); + pieceAuditEventsService = new PieceAuditEventsServiceImpl(pieceEventsDao); + + doReturn(postgresClient).when(postgresClientFactory).createInstance(TENANT_ID); + } + } @Test void shouldCallDaoSuccessfully() { var pieceAuditEvent = createPieceAuditEvent(UUID.randomUUID().toString()); + doReturn(Future.succeededFuture(rowSet)).when(postgresClient).execute(anyString(), any(Tuple.class)); + + var saveFuture = pieceAuditEventsService.savePieceAuditEvent(pieceAuditEvent, TENANT_ID); + saveFuture.onComplete(asyncResult -> assertTrue(asyncResult.succeeded())); - Future> saveFuture = pieceAuditEventsService.savePieceAuditEvent(pieceAuditEvent, TENANT_ID); - saveFuture.onComplete(ar -> { - assertTrue(ar.succeeded()); + verify(pieceEventsDao, times(1)).save(pieceAuditEvent, TENANT_ID); + } + + @Test + void shouldGetDto() { + var id = UUID.randomUUID().toString(); + var pieceAuditEvent = createPieceAuditEvent(id); + var pieceAuditEventCollection = new PieceAuditEventCollection().withPieceAuditEvents(List.of(pieceAuditEvent)).withTotalItems(1); + + doReturn(Future.succeededFuture(pieceAuditEventCollection)).when(pieceEventsDao).getAuditEventsByPieceId(anyString(), anyString(), anyString(), anyInt(), anyInt(), anyString()); + + var dto = pieceAuditEventsService.getAuditEventsByPieceId(id, ACTION_DATE_SORT_BY, DESC_ORDER, LIMIT, OFFSET, TENANT_ID); + dto.onComplete(asyncResult -> { + var pieceAuditEventOptional = asyncResult.result(); + var pieceAuditEventList = pieceAuditEventOptional.getPieceAuditEvents(); + + assertEquals(pieceAuditEventList.get(0).getId(), id); + assertEquals(PieceAuditEvent.Action.CREATE, pieceAuditEventList.get(0).getAction()); }); + + verify(pieceEventsDao, times(1)).getAuditEventsByPieceId(id, ACTION_DATE_SORT_BY, DESC_ORDER, LIMIT, OFFSET, TENANT_ID); } } diff --git a/mod-audit-server/src/test/java/org/folio/utils/EntityUtils.java b/mod-audit-server/src/test/java/org/folio/utils/EntityUtils.java index ed4008b7..71c4b82d 100644 --- a/mod-audit-server/src/test/java/org/folio/utils/EntityUtils.java +++ b/mod-audit-server/src/test/java/org/folio/utils/EntityUtils.java @@ -5,17 +5,23 @@ import io.vertx.core.json.JsonObject; import org.folio.rest.jaxrs.model.InvoiceAuditEvent; +import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent; import org.folio.rest.jaxrs.model.OrderAuditEvent; import org.folio.rest.jaxrs.model.OrderLineAuditEvent; import org.folio.rest.jaxrs.model.PieceAuditEvent; public class EntityUtils { + public static final String ACTION_DATE_SORT_BY = "action_date"; + public static final String DESC_ORDER = "desc"; + public static final int LIMIT = 1; + public static final int OFFSET = 1; public static final String TENANT_ID = "diku"; public static final String PIECE_ID = "2cd4adc4-f287-49b6-a9c6-9eacdc4868e7"; public static final String ORDER_ID = "a21fc51c-d46b-439b-8c79-9b2be41b79a6"; public static final String ORDER_LINE_ID = "a22fc51c-d46b-439b-8c79-9b2be41b79a6"; public static final String INVOICE_ID = "3f29b1a4-8c2b-4d3a-9b1e-5f2a1b4c8d3a"; + public static final String INVOICE_LINE_ID = "550e8400-e29b-41d4-a716-446655440001"; public static OrderAuditEvent createOrderAuditEvent(String id) { JsonObject jsonObject = new JsonObject(); @@ -123,4 +129,19 @@ public static InvoiceAuditEvent createInvoiceAuditEventWithoutSnapshot() { .withActionDate(new Date()) .withInvoiceSnapshot("Test"); } + + public static InvoiceLineAuditEvent createInvoiceLineAuditEvent(String id) { + JsonObject jsonObject = new JsonObject(); + jsonObject.put("name", "Test Product"); + + return new InvoiceLineAuditEvent() + .withId(id) + .withAction(InvoiceLineAuditEvent.Action.CREATE) + .withInvoiceId(INVOICE_ID) + .withInvoiceLineId(INVOICE_LINE_ID) + .withUserId(UUID.randomUUID().toString()) + .withEventDate(new Date()) + .withActionDate(new Date()) + .withInvoiceLineSnapshot(jsonObject); + } } diff --git a/ramls/acquisition-events.raml b/ramls/acquisition-events.raml index f173a8e3..9b21ea0b 100644 --- a/ramls/acquisition-events.raml +++ b/ramls/acquisition-events.raml @@ -19,6 +19,7 @@ types: piece-audit-event-collection: !include piece_audit_event_collection.json invoice-audit-event: !include invoice_audit_event.json invoice-audit-event-collection: !include invoice_audit_event_collection.json + invoice-line-audit-event-collection: !include invoice_line_audit_event_collection.json traits: searchable: !include raml-util/traits/searchable.raml @@ -196,3 +197,35 @@ traits: example: strict: false value: !include raml-util/examples/errors.sample + + /invoice-line/{id}: + get: + description: Get list of invoice_line events by invoice_line_id + is: [ + pageable, + validate + ] + queryParameters: + sortBy: + description: "sorting by field: actionDate" + type: string + default: action_date + sortOrder: + description: "sort order: asc or desc" + enum: [asc, desc] + type: string + default: desc + limit: + default: 2147483647 + offset: + default: 0 + responses: + 200: + body: + application/json: + type: invoice-line-audit-event-collection + 500: + description: "Internal server error" + body: + application/json: + type: errors diff --git a/ramls/invoice_line_audit_event.json b/ramls/invoice_line_audit_event.json new file mode 100644 index 00000000..6c481fe4 --- /dev/null +++ b/ramls/invoice_line_audit_event.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Invoice line audit event", + "type": "object", + "properties": { + "id": { + "description": "UUID of the event", + "$ref": "common/uuid.json" + }, + "action": { + "description": "Action for invoice line (Create, Edit or Delete)", + "type": "string", + "$ref": "event_action.json" + }, + "invoiceId": { + "description": "UUID of the invoice", + "$ref": "common/uuid.json" + }, + "invoiceLineId": { + "description": "UUID of the invoice line", + "$ref": "common/uuid.json" + }, + "userId": { + "description": "UUID of the user who performed the action", + "$ref": "common/uuid.json" + }, + "eventDate": { + "description": "Date time when event triggered", + "format": "date-time", + "type": "string" + }, + "actionDate": { + "description": "Date time when invoice action occurred", + "format": "date-time", + "type": "string" + }, + "invoiceLineSnapshot": { + "description": "Full snapshot of the invoice line", + "type": "object", + "javaType": "java.lang.Object" + } + }, + "additionalProperties": true +} diff --git a/ramls/invoice_line_audit_event_collection.json b/ramls/invoice_line_audit_event_collection.json new file mode 100644 index 00000000..b6832e71 --- /dev/null +++ b/ramls/invoice_line_audit_event_collection.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Collection of invoiceLineAuditEvents", + "type": "object", + "additionalProperties": false, + "properties": { + "invoiceLineAuditEvents": { + "description": "List of invoiceLineAuditEvents", + "type": "array", + "id": "invoiceLineAuditEventsList", + "items": { + "type": "object", + "$ref": "invoice_line_audit_event.json" + } + }, + "totalItems": { + "description": "total records", + "type": "integer" + } + }, + "required": [ + "invoiceLineAuditEvents", + "totalItems" + ] +}