diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 8c6c835..190404c 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -133,6 +133,15 @@ "permissionsRequired": [ "acquisition.piece.events.get" ] + }, + { + "methods": [ + "GET" + ], + "pathPattern": "/audit-data/acquisition/piece/{id}/unique-status", + "permissionsRequired": [ + "acquisition.piece.events.get" + ] } ] }, diff --git a/mod-audit-server/pom.xml b/mod-audit-server/pom.xml index 1bc17c9..9e13cbf 100644 --- a/mod-audit-server/pom.xml +++ b/mod-audit-server/pom.xml @@ -2,13 +2,13 @@ 4.0.0 mod-audit-server - 2.8.1-SNAPSHOT + 2.9.0-SNAPSHOT jar mod-audit org.folio - 2.8.1-SNAPSHOT + 2.9.0-SNAPSHOT diff --git a/mod-audit-server/src/main/java/org/folio/dao/acquisition/PieceEventsDao.java b/mod-audit-server/src/main/java/org/folio/dao/acquisition/PieceEventsDao.java index 8824901..d9d03b7 100644 --- a/mod-audit-server/src/main/java/org/folio/dao/acquisition/PieceEventsDao.java +++ b/mod-audit-server/src/main/java/org/folio/dao/acquisition/PieceEventsDao.java @@ -28,6 +28,9 @@ public interface PieceEventsDao { * @param tenantId tenant id * @return future with PieceAuditEventCollection */ - Future getAuditEventsByPieceId(String pieceId, String sortBy, String sortOrder, int limit, int offset, String tenantId); + Future getAuditEventsByPieceId(String pieceId, String sortBy, String sortOrder, + int limit, int offset, String tenantId); + Future getAuditEventsWithUniqueStatusByPieceId(String pieceId, String sortBy, String sortOrder, + int limit, int offset, String tenantId); } diff --git a/mod-audit-server/src/main/java/org/folio/dao/acquisition/impl/PieceEventsDaoImpl.java b/mod-audit-server/src/main/java/org/folio/dao/acquisition/impl/PieceEventsDaoImpl.java index 2661781..4848335 100644 --- a/mod-audit-server/src/main/java/org/folio/dao/acquisition/impl/PieceEventsDaoImpl.java +++ b/mod-audit-server/src/main/java/org/folio/dao/acquisition/impl/PieceEventsDaoImpl.java @@ -9,7 +9,6 @@ import static org.folio.util.AuditEventDBConstants.MODIFIED_CONTENT_FIELD; import static org.folio.util.AuditEventDBConstants.ORDER_BY_PATTERN; import static org.folio.util.AuditEventDBConstants.PIECE_ID_FIELD; -import static org.folio.util.AuditEventDBConstants.TOTAL_RECORDS_FIELD; import static org.folio.util.AuditEventDBConstants.USER_ID_FIELD; import java.time.LocalDateTime; @@ -36,8 +35,13 @@ public class PieceEventsDaoImpl implements PieceEventsDao { private static final Logger LOGGER = LogManager.getLogger(); private static final String TABLE_NAME = "acquisition_piece_log"; - private static final String GET_BY_PIECE_ID_SQL = "SELECT id, action, piece_id, user_id, event_date, action_date, modified_content_snapshot," + - " (SELECT count(*) AS total_records FROM %s WHERE piece_id = $1) FROM %s WHERE piece_id = $1 %s LIMIT $2 OFFSET $3"; + private static final String GET_BY_PIECE_ID_SQL = "SELECT id, action, piece_id, user_id, event_date, action_date, modified_content_snapshot" + + " FROM %s WHERE piece_id = $1 %s LIMIT $2 OFFSET $3"; + private static final String GET_UNIQUE_STATUS_BY_PIECE_ID_SQL = "SELECT id, action, piece_id, user_id, event_date, action_date, modified_content_snapshot" + + " FROM %s WHERE piece_id = $1 AND" + + " action IN" + + " (SELECT action FROM %s WHERE piece_id = $1 GROUP BY action HAVING COUNT(action) = 1)" + + " %s LIMIT $2 OFFSET $3"; private static final String INSERT_SQL = "INSERT INTO %s (id, action, piece_id, user_id, event_date, action_date, modified_content_snapshot)" + " VALUES ($1, $2, $3, $4, $5, $6, $7)"; @@ -67,9 +71,8 @@ public Future getAuditEventsByPieceId(String pieceId, Promise> promise = Promise.promise(); try { String logTable = formatDBTableName(tenantId, TABLE_NAME); - String query = format(GET_BY_PIECE_ID_SQL, logTable, logTable, format(ORDER_BY_PATTERN, sortBy, sortOrder)); + String query = format(GET_BY_PIECE_ID_SQL, logTable, format(ORDER_BY_PATTERN, sortBy, sortOrder)); Tuple queryParams = Tuple.of(UUID.fromString(pieceId), limit, offset); - pgClientFactory.createInstance(tenantId).selectRead(query, queryParams, promise); } catch (Exception e) { LOGGER.warn("Error getting piece audit events by piece id: {}", pieceId, e); @@ -81,13 +84,37 @@ public Future getAuditEventsByPieceId(String pieceId, mapRowToListOfPieceEvent(rowSet)); } + @Override + public Future getAuditEventsWithUniqueStatusByPieceId(String pieceId, String sortBy, String sortOrder, int limit, int offset, String tenantId) { + LOGGER.debug("getAuditEventsByOrderId:: Retrieving AuditEvent with piece id : {}", pieceId); + Promise> promise = Promise.promise(); + try { + LOGGER.info("getAuditEventsByOrderId:: Trying to Retrieve AuditEvent with order id : {}", pieceId); + String logTable = formatDBTableName(tenantId, TABLE_NAME); + String query = format(GET_UNIQUE_STATUS_BY_PIECE_ID_SQL, logTable, logTable, format(ORDER_BY_PATTERN, sortBy, sortOrder)); + Tuple queryParams = Tuple.of(UUID.fromString(pieceId), limit, offset); + pgClientFactory.createInstance(tenantId).selectRead(query, queryParams, promise); + } catch (Exception e) { + LOGGER.warn("Error getting order audit events by piece id: {}", pieceId, e); + promise.fail(e); + } + LOGGER.info("getAuditEventsByOrderId:: Retrieved AuditEvent with piece id: {}", pieceId); + return promise.future().map(rowSet -> rowSet.rowCount() == 0 ? new PieceAuditEventCollection().withTotalItems(0) + : mapRowToListOfPieceEvent(rowSet)); + } + private PieceAuditEventCollection mapRowToListOfPieceEvent(RowSet rowSet) { LOGGER.debug("mapRowToListOfOrderEvent:: Mapping row to List of Piece Events"); PieceAuditEventCollection pieceAuditEventCollection = new PieceAuditEventCollection(); + + // set audit piece change record(s) by mapping rowSet one by one rowSet.iterator().forEachRemaining(row -> { pieceAuditEventCollection.getPieceAuditEvents().add(mapRowToPieceEvent(row)); - pieceAuditEventCollection.setTotalItems(row.getInteger(TOTAL_RECORDS_FIELD)); }); + // set total records + int totalRecords = pieceAuditEventCollection.getPieceAuditEvents().size(); + pieceAuditEventCollection.setTotalItems(totalRecords); + LOGGER.info("mapRowToListOfOrderEvent:: Mapped row to List of Piece Events"); return pieceAuditEventCollection; } 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 be70b07..5c210c8 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 @@ -10,6 +10,7 @@ import org.folio.rest.jaxrs.model.AuditDataAcquisitionOrderIdGetSortOrder; import org.folio.rest.jaxrs.model.AuditDataAcquisitionOrderLineIdGetSortOrder; import org.folio.rest.jaxrs.model.AuditDataAcquisitionPieceIdGetSortOrder; +import org.folio.rest.jaxrs.model.AuditDataAcquisitionPieceIdUniqueStatusGetSortOrder; import org.folio.rest.jaxrs.resource.AuditDataAcquisition; import org.folio.rest.tools.utils.TenantTool; import org.folio.services.acquisition.OrderAuditEventsService; @@ -103,6 +104,26 @@ public void getAuditDataAcquisitionPieceById(String pieceId, String sortBy, Audi }); } + @Override + public void getAuditDataAcquisitionPieceUniqueStatusById(String pieceId, String sortBy, AuditDataAcquisitionPieceIdUniqueStatusGetSortOrder sortOrder, int limit, int offset, Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) { + LOGGER.debug("getAuditDataAcquisitionOrderById:: Retrieving Audit Data Acquisition Piece with unique status By Id : {}", pieceId); + String tenantId = TenantTool.tenantId(okapiHeaders); + + vertxContext.runOnContext(c -> { + try { + LOGGER.warn("Trying to get piece audit events with unique status by piece id: {}", pieceId); + pieceAuditEventsService.getAuditEventsWithUniqueStatusByPieceId(pieceId, sortBy, sortOrder.name(), limit, offset, tenantId) + .map(GetAuditDataAcquisitionPieceByIdResponse::respond200WithApplicationJson) + .map(Response.class::cast) + .otherwise(this::mapExceptionToResponse) + .onComplete(asyncResultHandler); + } catch (Exception e) { + LOGGER.warn("Failed to get piece audit events with unique status change by piece id: {}", pieceId, 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/services/acquisition/PieceAuditEventsService.java b/mod-audit-server/src/main/java/org/folio/services/acquisition/PieceAuditEventsService.java index 1165ffa..51e8202 100644 --- a/mod-audit-server/src/main/java/org/folio/services/acquisition/PieceAuditEventsService.java +++ b/mod-audit-server/src/main/java/org/folio/services/acquisition/PieceAuditEventsService.java @@ -30,4 +30,15 @@ public interface PieceAuditEventsService { Future getAuditEventsByPieceId(String pieceId, String sortBy, String sortOrder, int limit, int offset, String tenantId); + /** + * Searches for piece audit events which has unique status changes by piece id + * @param pieceId piece id + * @param sortBy sort by + * @param sortOrder sort order + * @param limit limit + * @param offset offset + * @return future with PieceAuditEventCollection + */ + Future getAuditEventsWithUniqueStatusByPieceId(String pieceId, String sortBy, String sortOrder, + int limit, int offset, String 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 dbf3c2f..7839f2a 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 @@ -38,6 +38,12 @@ public Future getAuditEventsByPieceId(String pieceId, return pieceEventsDao.getAuditEventsByPieceId(pieceId, sortBy, sortOrder, limit, offset, tenantId); } + @Override + public Future getAuditEventsWithUniqueStatusByPieceId(String pieceId, String sortBy, String sortOrder, int limit, int offset, String tenantId) { + LOGGER.debug("getAuditEventsByOrderId:: Retrieving audit events with unique status changes for pieceId={} and tenantId={}", pieceId, tenantId); + return pieceEventsDao.getAuditEventsWithUniqueStatusByPieceId(pieceId, sortBy, sortOrder, limit, offset, tenantId); + } + private Future handleFailures(Throwable throwable, String id) { LOGGER.debug("handleFailures:: Handling Failures with id={}", id); return (throwable instanceof PgException pgException && pgException.getCode().equals(UNIQUE_CONSTRAINT_VIOLATION_CODE)) ? diff --git a/pom.xml b/pom.xml index 8af9fa4..a4fd0e4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.folio mod-audit - 2.8.1-SNAPSHOT + 2.9.0-SNAPSHOT pom diff --git a/ramls/acquisition-events.raml b/ramls/acquisition-events.raml index 19b1ec4..e83a998 100644 --- a/ramls/acquisition-events.raml +++ b/ramls/acquisition-events.raml @@ -125,3 +125,37 @@ traits: example: strict: false value: !include raml-util/examples/errors.sample + /piece/{id}/unique-status: + get: + description: Get list of piece events which have unique status changes by piece_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: piece-audit-event-collection + 500: + description: "Internal server error" + body: + application/json: + type: errors + example: + strict: false + value: !include raml-util/examples/errors.sample