Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[MODAUD-174] - Consume piece change events and implement endpoints #155

Merged
merged 51 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
854e82a
[MODAUD-174] - Consume piece change events and implement endpoints
azizbekxm Nov 9, 2023
3a35fb9
[MODAUD-174] - Fixed unit tests
azizbekxm Nov 9, 2023
dbaaf9f
[MODAUD-174] - Fixed Code smell
azizbekxm Nov 9, 2023
38c6613
[MODAUD-174] - Fixed Code smell
azizbekxm Nov 9, 2023
11961c6
[MODAUD-174] - Fixed Unit tests
azizbekxm Nov 9, 2023
066fbac
[MODAUD-174] - Minor improvements
azizbekxm Nov 9, 2023
536e9ea
[MODAUD-174] - Implements kafka handler and creating table
azizbekxm Nov 10, 2023
97e24a2
[MODAUD-174] - Increased code coverage
azizbekxm Nov 10, 2023
0d3017b
[MODAUD-174] - Increased code coverage
azizbekxm Nov 10, 2023
3262ab6
[MODAUD-174] - Minor improvements
azizbekxm Nov 10, 2023
42fbbad
[MODAUD-174] - Minor improvements
azizbekxm Nov 10, 2023
a2d8ae2
[MODAUD-174] - Minor improvements
azizbekxm Nov 10, 2023
7516c51
[MODAUD-174] - Minor improvements
azizbekxm Nov 13, 2023
90c5205
[MODAUD-174] - Minor improvements
azizbekxm Nov 13, 2023
9a12aac
[MODAUD-174] - Minor improvements
azizbekxm Nov 13, 2023
20a1b7f
[MODAUD-174] - Implemented uniq-status api
azizbekxm Nov 13, 2023
ecd4f24
[MODAUD-174] - Implemented status change history feature and cleaned …
azizbekxm Nov 14, 2023
3811d8a
[MODAUD-174] - Implemented status change history feature and cleaned …
azizbekxm Nov 14, 2023
1646698
[MODAUD-174] - Implemented status change history feature and cleaned …
azizbekxm Nov 14, 2023
df807cc
[MODAUD-174] - draft changes
azizbekxm Nov 14, 2023
aa96b19
[MODAUD-174] - Fixed error
azizbekxm Nov 14, 2023
821a46a
[MODAUD-174] - minor improvements
azizbekxm Nov 14, 2023
d43d0ee
[MODAUD-174] - minor improvements
azizbekxm Nov 14, 2023
d32bc03
[MODAUD-174] - minor improvements
azizbekxm Nov 14, 2023
6aef666
[MODAUD-174] - minor improvements
azizbekxm Nov 14, 2023
86d171f
[MODAUD-174] - minor improvements
azizbekxm Nov 14, 2023
18d7d11
[MODAUD-174] - Fixed unit tests
azizbekxm Nov 15, 2023
ba9d212
[MODAUD-174] - Fixed unit tests
azizbekxm Nov 15, 2023
97307d7
[MODAUD-174] - Fixed unit tests
azizbekxm Nov 15, 2023
3152592
[MODAUD-174] - Fixed unit tests
azizbekxm Nov 15, 2023
d3f3a3d
[MODAUD-174] - draft changes
azizbekxm Nov 15, 2023
c30ce60
[MODAUD-174] - Fixed unit tests
azizbekxm Nov 15, 2023
08482dc
[MODAUD-174] - Minor improvements
azizbekxm Nov 15, 2023
a462a7c
[MODAUD-174] - Minor improvements
azizbekxm Nov 15, 2023
2376e3c
[MODAUD-174] - Minor improvements
azizbekxm Nov 15, 2023
9d66e94
[MODAUD-174] - Improved code by changing field injection to construct…
azizbekxm Nov 15, 2023
f7e9581
[MODAUD-174] - Fixed unit tests
azizbekxm Nov 15, 2023
19d7e9f
[MODAUD-174] - Fixed unit tests
azizbekxm Nov 15, 2023
c72b7b8
[MODAUD-174] - Fixed sql query
azizbekxm Nov 15, 2023
6438c25
[MODAUD-174] - Extracted duplicate methods
azizbekxm Nov 15, 2023
49b39b2
[MODAUD-174] - Added java docs
azizbekxm Nov 15, 2023
d04fa1f
[MODAUD-174] - Improved java integration test
azizbekxm Nov 15, 2023
3132186
[MODAUD-174] - Extracted handleFauilures and optimized codebase
azizbekxm Nov 15, 2023
f8c661e
[MODAUD-174] - Fixed sql query
azizbekxm Nov 15, 2023
82b48db
[MODAUD-174] - Fixed sql query
azizbekxm Nov 15, 2023
af27863
[MODAUD-174] - Minor improvement
azizbekxm Nov 15, 2023
ec50fb3
[MODAUD-174] - Improved AuditDataAcquisitionImpl
azizbekxm Nov 15, 2023
26bcef4
[MODAUD-174] - Improved AuditDataAcquisitionImpl and changed debug to…
azizbekxm Nov 15, 2023
1f00735
[MODAUD-174] - Fixed ZoneOffset
azizbekxm Nov 15, 2023
fda19eb
[MODAUD-174] - Improved log.error message
azizbekxm Nov 15, 2023
3881bdc
[MODAUD-174] - Minor improvements
azizbekxm Nov 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore

Choose a reason for hiding this comment

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

Doubt
Do we need to push this?

Copy link
Contributor Author

@azizbekxm azizbekxm Nov 15, 2023

Choose a reason for hiding this comment

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

it avoid situation where intellij idea asks whether add postgres-conf.json to git or not. For me it is useful

Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ nbproject/
.settings/
.classpath
/bin/
/src/main/resources/postgres-conf.json
32 changes: 31 additions & 1 deletion descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,30 @@
}
]
},
{
"id": "acquisition-piece-events",
"version": "1.0",
"handlers": [
{
"methods": [
"GET"
],
"pathPattern": "/audit-data/acquisition/piece/{id}",
"permissionsRequired": [
"acquisition.piece.events.get"
]
},
{
"methods": [
"GET"
],
"pathPattern": "/audit-data/acquisition/piece/{id}/unique-status",
SerhiiNosko marked this conversation as resolved.
Show resolved Hide resolved
"permissionsRequired": [
"acquisition.piece.events.get"
]
}
]
},
{
"id": "circulation-logs",
"version": "1.2",
Expand Down Expand Up @@ -223,6 +247,11 @@
"displayName": "Acquisition order-line events - get order-line change events",
"description": "Get order-line change events"
},
{
"permissionName": "acquisition.piece.events.get",
"displayName": "Acquisition piece events - get piece change events",
"description": "Get piece change events"
},
{
"permissionName": "audit.all",
"displayName": "Audit - all permissions",
Expand All @@ -235,7 +264,8 @@
"audit.item.delete",
"circulation-logs.collection.get",
"acquisition.order.events.get",
"acquisition.order-line.events.get"
"acquisition.order-line.events.get",
"acquisition.piece.events.get"
]
}
],
Expand Down
4 changes: 2 additions & 2 deletions mod-audit-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>mod-audit-server</artifactId>
<version>2.8.1-SNAPSHOT</version>
<version>2.9.0-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<artifactId>mod-audit</artifactId>
<groupId>org.folio</groupId>
<version>2.8.1-SNAPSHOT</version>
<version>2.9.0-SNAPSHOT</version>
</parent>

<licenses>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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.PieceAuditEvent;
import org.folio.rest.jaxrs.model.PieceAuditEventCollection;

public interface PieceEventsDao {

/**
* Saves pieceAuditEvent entity to DB
*
* @param pieceAuditEvent pieceAuditEvent entity to save
* @param tenantId tenant id
* @return future with created row
*/
Future<RowSet<Row>> save(PieceAuditEvent pieceAuditEvent, String tenantId);

/**
* Searches for piece audit events by id
*
* @param pieceId piece id
* @param sortBy sort by
* @param sortOrder sort order
* @param limit limit
* @param offset offset
* @param tenantId tenant id
* @return future with PieceAuditEventCollection
*/
Future<PieceAuditEventCollection> getAuditEventsByPieceId(String pieceId, String sortBy, String sortOrder,
int limit, int offset, String tenantId);

Future<PieceAuditEventCollection> getAuditEventsWithUniqueStatusByPieceId(String pieceId, String sortBy, String sortOrder,
SerhiiNosko marked this conversation as resolved.
Show resolved Hide resolved
int limit, int offset, String tenantId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import static java.lang.String.format;
import static org.folio.rest.persist.PostgresClient.convertToPsqlStandard;
import static org.folio.util.OrderAuditEventDBConstants.*;
import static org.folio.util.AuditEventDBConstants.*;

@Repository
public class OrderEventsDaoImpl implements OrderEventsDao {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import static java.lang.String.format;
import static org.folio.rest.persist.PostgresClient.convertToPsqlStandard;
import static org.folio.util.OrderAuditEventDBConstants.*;
import static org.folio.util.AuditEventDBConstants.*;

@Repository
public class OrderLineEventsDaoImpl implements OrderLineEventsDao {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package org.folio.dao.acquisition.impl;

import static java.lang.String.format;
import static org.folio.rest.persist.PostgresClient.convertToPsqlStandard;
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.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.USER_ID_FIELD;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.UUID;

import io.vertx.core.Future;
import io.vertx.core.Promise;
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.PieceEventsDao;
import org.folio.rest.jaxrs.model.PieceAuditEvent;
import org.folio.rest.jaxrs.model.PieceAuditEventCollection;
import org.folio.util.PostgresClientFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
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" +
" 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)";

private final PostgresClientFactory pgClientFactory;

@Autowired
public PieceEventsDaoImpl(PostgresClientFactory pgClientFactory) {
this.pgClientFactory = pgClientFactory;
}

@Override
public Future<RowSet<Row>> save(PieceAuditEvent pieceAuditEvent, String tenantId) {
LOGGER.debug("save:: Trying to save Piece AuditEvent with tenant id : {}", tenantId);
Promise<RowSet<Row>> promise = Promise.promise();

String logTable = formatDBTableName(tenantId, TABLE_NAME);
String query = format(INSERT_SQL, logTable);

makeSaveCall(promise, query, pieceAuditEvent, tenantId);
LOGGER.info("save:: Saved Piece AuditEvent for pieceId={} in tenant id={}", pieceAuditEvent.getPieceId(), tenantId);
return promise.future();
}

@Override
public Future<PieceAuditEventCollection> getAuditEventsByPieceId(String pieceId, String sortBy, String sortOrder, int limit, int offset, String tenantId) {
LOGGER.debug("getAuditEventsByOrderId:: Trying to retrieve AuditEvent with piece id : {}", pieceId);
Promise<RowSet<Row>> promise = Promise.promise();
try {
String logTable = formatDBTableName(tenantId, TABLE_NAME);
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);
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));
}

@Override
public Future<PieceAuditEventCollection> getAuditEventsWithUniqueStatusByPieceId(String pieceId, String sortBy, String sortOrder, int limit, int offset, String tenantId) {
LOGGER.debug("getAuditEventsByOrderId:: Retrieving AuditEvent with piece id : {}", pieceId);
Promise<RowSet<Row>> 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<Row> 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));
});
// set total records
int totalRecords = pieceAuditEventCollection.getPieceAuditEvents().size();
pieceAuditEventCollection.setTotalItems(totalRecords);

LOGGER.info("mapRowToListOfOrderEvent:: Mapped row to List of Piece Events");
SerhiiNosko marked this conversation as resolved.
Show resolved Hide resolved
return pieceAuditEventCollection;
}

private PieceAuditEvent mapRowToPieceEvent(Row row) {
LOGGER.debug("mapRowToPieceEvent:: Mapping row to Order Event");
return new PieceAuditEvent()
.withId(row.getValue(ID_FIELD).toString())
.withAction(row.get(PieceAuditEvent.Action.class, ACTION_FIELD))
.withPieceId(row.getValue(PIECE_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)))
.withPieceSnapshot(JsonObject.mapFrom(row.getValue(MODIFIED_CONTENT_FIELD)));
}

private void makeSaveCall(Promise<RowSet<Row>> promise, String query, PieceAuditEvent pieceAuditEvent, String tenantId) {
LOGGER.debug("makeSaveCall:: Making save call with query : {} and tenant id : {}", query, tenantId);
try {
pgClientFactory.createInstance(tenantId).execute(query, Tuple.of(pieceAuditEvent.getId(),
pieceAuditEvent.getActionDate(),
pieceAuditEvent.getPieceId(),
pieceAuditEvent.getUserId(),
LocalDateTime.ofInstant(pieceAuditEvent.getEventDate().toInstant(), ZoneOffset.UTC),
LocalDateTime.ofInstant(pieceAuditEvent.getActionDate().toInstant(), ZoneOffset.UTC),
JsonObject.mapFrom(pieceAuditEvent.getPieceSnapshot())),
promise);
} catch (Exception e) {
LOGGER.error("Failed to save record with id: {} for order id: {} in to table {}",
pieceAuditEvent.getId(), pieceAuditEvent.getPieceId(), TABLE_NAME, e);
promise.fail(e);
}
}

private String formatDBTableName(String tenantId, String table) {
SerhiiNosko marked this conversation as resolved.
Show resolved Hide resolved
LOGGER.debug("formatDBTableName:: Formatting DB Table Name with tenant id : {}", tenantId);
return format("%s.%s", convertToPsqlStandard(tenantId), table);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
import org.apache.logging.log4j.Logger;
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;
import org.folio.services.acquisition.OrderLineAuditEventsService;
import org.folio.services.acquisition.PieceAuditEventsService;
import org.folio.spring.SpringContextUtil;
import org.folio.util.ErrorUtils;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -32,6 +35,9 @@ public class AuditDataAcquisitionImpl implements AuditDataAcquisition {
@Autowired
private OrderLineAuditEventsService orderLineAuditEventsService;

@Autowired
private PieceAuditEventsService pieceAuditEventsService;

public AuditDataAcquisitionImpl() {
SpringContextUtil.autowireDependencies(this, Vertx.currentContext());
}
Expand Down Expand Up @@ -78,11 +84,50 @@ public void getAuditDataAcquisitionOrderLineById(String orderLineId, String sort
});
}

@Override
public void getAuditDataAcquisitionPieceById(String pieceId, String sortBy, AuditDataAcquisitionPieceIdGetSortOrder sortOrder,
int limit, int offset, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
LOGGER.debug("getAuditDataAcquisitionOrderById:: Retrieving Audit Data Acquisition Piece By Id : {}", pieceId);
String tenantId = TenantTool.tenantId(okapiHeaders);

vertxContext.runOnContext(c -> {
SerhiiNosko marked this conversation as resolved.
Show resolved Hide resolved
try {
pieceAuditEventsService.getAuditEventsByPieceId(pieceId, sortBy, sortOrder.name(), limit, offset, tenantId)
.map(GetAuditDataAcquisitionPieceByIdResponse::respond200WithApplicationJson)
.map(Response.class::cast)
.otherwise(this::mapExceptionToResponse)
.onComplete(asyncResultHandler);
} catch (Exception e) {
LOGGER.error("Failed to get piece audit events by piece id: {}", pieceId, e);
asyncResultHandler.handle(Future.succeededFuture(mapExceptionToResponse(e)));
}
});
}

@Override
public void getAuditDataAcquisitionPieceUniqueStatusById(String pieceId, String sortBy, AuditDataAcquisitionPieceIdUniqueStatusGetSortOrder sortOrder, int limit, int offset, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> 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
.respond500WithApplicationJson(ErrorUtils.buildErrors(GENERIC_ERROR_CODE.getCode(), throwable));
.respond500WithApplicationJson(ErrorUtils.buildErrors(GENERIC_ERROR_CODE.getCode(), throwable));
}

}

Loading