Skip to content

Commit

Permalink
Merge branch 'master' of github.com:folio-org/mod-circulation-storage…
Browse files Browse the repository at this point in the history
… into ecs-tlr-feature

# Conflicts:
#	src/test/java/org/folio/rest/api/StaffSlipsApiTest.java
#	src/test/java/org/folio/rest/support/builders/RequestRequestBuilder.java
  • Loading branch information
roman-barannyk committed Oct 18, 2024
2 parents 078733e + afa222f commit 5591f35
Show file tree
Hide file tree
Showing 12 changed files with 205 additions and 21 deletions.
5 changes: 4 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
* ## 17.2.0 2024-03-20
## 17.3.0
* [CIRCSTORE-533](https://folio-org.atlassian.net/browse/CIRCSTORE-533) Upgrade "holdings-storage" to 8.0

## 17.2.0 2024-03-20
* Upgrade RMB to 35.2.0, Vert.x to 4.5.5, Spring to 6.1.5 (CIRCSTORE-494)
* Add `displaySummary` field to `ActualCostRecord` schema (CIRCSTORE-493)
* Fix NPE for requests with no position (CIRCSTORE-422)
Expand Down
2 changes: 1 addition & 1 deletion descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
{
"id": "holdings-storage",
"version": "1.3 2.0 3.0 4.0 5.0 6.0 7.0"
"version": "1.3 2.0 3.0 4.0 5.0 6.0 7.0 8.0"
},
{
"id": "pubsub-event-types",
Expand Down
28 changes: 28 additions & 0 deletions ramls/request.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,30 @@
"description": "Tags",
"$ref": "raml-util/schemas/tags.schema"
},
"printDetails": {
"type": "object",
"description": "PrintDetails",
"properties": {
"printCount": {
"type": "integer",
"description": "Number of times print slip generated."
},
"requesterId": {
"type": "string",
"description": "UUID of print slip requester."
},
"isPrinted": {
"type": "boolean",
"description": "Whether print slip was printed in past."
},
"printEventDate": {
"type": "string",
"format": "date-time",
"description": "Date and time when print slip was generated last time."
}
},
"additionalProperties": false
},
"awaitingPickupRequestClosedDate": {
"description": "A date when the request with awaiting pickup status was closed",
"type": "string",
Expand All @@ -236,6 +260,10 @@
"description": "Request fields used for search",
"type": "object",
"$ref": "request-search-index.json"
},
"itemLocationCode": {
"description": "Allow specifying item location when creating title-level requests",
"type": "string"
}
},
"additionalProperties": false,
Expand Down
6 changes: 1 addition & 5 deletions src/main/java/org/folio/rest/impl/PrintEventsApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,14 @@
import javax.ws.rs.core.Response;
import java.util.Map;

import static io.vertx.core.Future.succeededFuture;

public class PrintEventsApi implements PrintEventsStorage {
private static final Logger LOG = LoggerFactory.getLogger(PrintEventsApi.class);

@Override
public void postPrintEventsStoragePrintEventsEntry(PrintEventsRequest printEventsRequest, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
LOG.info("postPrintEventsStoragePrintEvents:: save print events {}", printEventsRequest);
new PrintEventsService(vertxContext, okapiHeaders)
.create(printEventsRequest)
.onSuccess(response -> asyncResultHandler.handle(succeededFuture(response)))
.onFailure(throwable -> asyncResultHandler.handle(succeededFuture(PostPrintEventsStoragePrintEventsEntryResponse.respond500WithTextPlain(throwable.getMessage()))));
.create(printEventsRequest, asyncResultHandler);
}

@Override
Expand Down
89 changes: 80 additions & 9 deletions src/main/java/org/folio/service/PrintEventsService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import io.vertx.core.AsyncResult;
import io.vertx.core.Context;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
Expand All @@ -14,28 +13,31 @@
import org.folio.rest.model.PrintEvent;
import org.folio.rest.persist.PgUtil;
import org.folio.rest.persist.PostgresClient;
import org.folio.rest.tools.utils.MetadataUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.core.Response;
import java.text.SimpleDateFormat;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.stream.Collectors;

import static io.vertx.core.Future.succeededFuture;
import static org.folio.rest.persist.PgUtil.postgresClient;
import static org.folio.rest.persist.PostgresClient.convertToPsqlStandard;
import static org.folio.support.ModuleConstants.PRINT_EVENTS_TABLE;
import static org.folio.support.ModuleConstants.REQUEST_TABLE;

public class PrintEventsService {

private static final Logger LOG = LoggerFactory.getLogger(PrintEventsService.class);
private static final int MAX_ENTITIES = 10000;
private final Context vertxContext;
private final Map<String, String> okapiHeaders;
private final PostgresClient postgresClient;

private static final String PRINT_EVENT_FETCH_QUERY = """
WITH cte AS (
Expand All @@ -53,31 +55,100 @@ ORDER BY (jsonb->>'printEventDate')::timestamptz DESC) AS rank
rank = 1;
""";

private static String requestPrintSyncQueryString = """
WITH print_counts AS (
SELECT
jsonb->>'requestId' AS request_id,
COUNT(*) AS print_count
FROM %s
WHERE jsonb->>'requestId' IN (%s)
GROUP BY jsonb->>'requestId'
)
UPDATE %s
SET jsonb =
(jsonb
|| jsonb_build_object(
'printDetails',
jsonb_build_object(
'printCount', print_counts.print_count,
'requesterId', %s,
'isPrinted', true,
'printEventDate', %s
)
)
)
FROM print_counts
WHERE id = print_counts.request_id::uuid;
""";



public PrintEventsService(Context vertxContext, Map<String, String> okapiHeaders) {
this.vertxContext = vertxContext;
this.okapiHeaders = okapiHeaders;
this.postgresClient = PgUtil.postgresClient(vertxContext, okapiHeaders);
}

public Future<Response> create(PrintEventsRequest printEventRequest) {
public void create(PrintEventsRequest printEventRequest,
Handler<AsyncResult<Response>> asyncResultHandler) {
LOG.info("create:: save print events {}", printEventRequest);
List<PrintEvent> printEvents = printEventRequest.getRequestIds().stream().map(requestId -> {
List<PrintEvent> printEvents =
printEventRequest.getRequestIds().stream().map(requestId -> {
PrintEvent event = new PrintEvent();
event.setRequestId(requestId);
event.setRequesterId(printEventRequest.getRequesterId());
event.setRequesterName(printEventRequest.getRequesterName());
event.setPrintEventDate(printEventRequest.getPrintEventDate());
return event;
}).toList();
return PgUtil.postSync(PRINT_EVENTS_TABLE, printEvents, MAX_ENTITIES, false, okapiHeaders, vertxContext,
PrintEventsStorage.PostPrintEventsStoragePrintEventsEntryResponse.class);
try {
MetadataUtil.populateMetadata(printEvents, okapiHeaders);
} catch (Exception e) {
String msg =
"Cannot populate metadata of printEvents list elements: " + e.getMessage();
LOG.error(msg, e);
asyncResultHandler.handle(succeededFuture(PrintEventsStorage.PostPrintEventsStoragePrintEventsEntryResponse.respond500WithTextPlain(msg)));
return;
}

postgresClient.withTrans(conn -> conn.saveBatch(PRINT_EVENTS_TABLE,
printEvents)
.compose(printEventsResult -> conn.execute(
buildRequestSyncQuery(printEventRequest, okapiHeaders)
))).onFailure(handler ->
asyncResultHandler.handle(
succeededFuture(PrintEventsStorage.PostPrintEventsStoragePrintEventsEntryResponse.respond500WithTextPlain(handler.getMessage()))
)
).onSuccess(handler ->
asyncResultHandler.handle(
succeededFuture(PrintEventsStorage.PostPrintEventsStoragePrintEventsEntryResponse.respond201())
));
}

private String buildRequestSyncQuery(PrintEventsRequest printEventRequest,
Map<String, String> okapiHeaders) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
df.setTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC));

String tenantId = okapiHeaders.get(RestVerticle.OKAPI_HEADER_TENANT);
String printEventTableName =
convertToPsqlStandard(tenantId) + "." + PRINT_EVENTS_TABLE;
String requestTableName =
convertToPsqlStandard(tenantId) + "." + REQUEST_TABLE;
String requestIds = printEventRequest.getRequestIds().stream()
.map(requestId -> "'" + requestId + "'")
.collect(Collectors.joining(", "));
String requesterId = "'" + printEventRequest.getRequesterId() + "'";
String printEventDate =
"'" + df.format(printEventRequest.getPrintEventDate()) + "'";

return requestPrintSyncQueryString.formatted(printEventTableName,
requestIds, requestTableName, requesterId,
printEventDate);
}

public void getPrintEventRequestDetails(List<String> requestIds, Handler<AsyncResult<Response>> asyncResultHandler) {
LOG.debug("getPrintEventRequestDetails:: Fetching print event details for requestIds {}", requestIds);
String tenantId = okapiHeaders.get(RestVerticle.OKAPI_HEADER_TENANT);
PostgresClient postgresClient = postgresClient(vertxContext, okapiHeaders);
postgresClient.execute(formatQuery(tenantId, requestIds), handler -> {
try {
if (handler.succeeded()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
INSERT INTO ${myuniversity}_${mymodule}.staff_slips(id, jsonb)
VALUES ('0b52bca7-db17-4e91-a740-7872ed6d7323',
jsonb_build_object(
'id', '0b52bca7-db17-4e91-a740-7872ed6d7323',
'name', 'Due date receipt',
'active', true,
'template', '<p></p>'))
ON CONFLICT DO NOTHING;
5 changes: 5 additions & 0 deletions src/main/resources/templates/db_scripts/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,11 @@
"run": "after",
"snippetPath": "fixSpellingOfFulfillmentPreference.sql",
"fromModuleVersion": "17.1.2"
},
{
"run": "after",
"snippetPath": "add_due_date_slips.sql",
"fromModuleVersion": "17.3.0"
}
]
}
13 changes: 13 additions & 0 deletions src/test/java/org/folio/rest/api/PrintEventsAPITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import static org.folio.rest.support.http.InterfaceUrls.printEventsUrl;
import static org.folio.rest.support.matchers.HttpResponseStatusCodeMatchers.isCreated;
import static org.folio.rest.support.matchers.HttpResponseStatusCodeMatchers.isInternalServerError;
import static org.folio.rest.support.matchers.HttpResponseStatusCodeMatchers.isOk;
import static org.folio.rest.support.matchers.HttpResponseStatusCodeMatchers.isUnprocessableEntity;
import static org.hamcrest.core.Is.is;
Expand Down Expand Up @@ -180,6 +181,18 @@ public void getPrintEventStatusWithInvalidRequestIds() throws MalformedURLExcept
assertThat(jsonObject.getJsonArray("printEventsStatusResponses").size(), is(0));
}

@Test
public void createPrintEventLogAndValidate5XX() throws MalformedURLException,
ExecutionException, InterruptedException {
JsonObject printEventsJson = getPrintEvent();
final CompletableFuture<JsonResponse> postCompleted = new CompletableFuture<>();
client.post(printEventsUrl("/print-events-entry"), printEventsJson,
"INVALID_TENANT_ID",
ResponseHandler.json(postCompleted));
final JsonResponse postResponse = postCompleted.get();
assertThat(postResponse, isInternalServerError());
}

private JsonObject getPrintEvent() {
List<String> requestIds = List.of("5f5751b4-e352-4121-adca-204b0c2aec43", "5f5751b4-e352-4121-adca-204b0c2aec44");
return new JsonObject()
Expand Down
3 changes: 3 additions & 0 deletions src/test/java/org/folio/rest/api/RequestsApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public void canCreateARequest(String requestLevel) throws InterruptedException,
.withPosition(1)
.withPickupServicePointId(pickupServicePointId)
.withTags(new Tags().withTagList(asList("new", "important")))
.withItemLocationCode("CN/P1")
.create(),
requestStorageUrl())
.getJson();
Expand Down Expand Up @@ -228,6 +229,8 @@ public void canCreateARequest(String requestLevel) throws InterruptedException,
assertThat(tagsRepresentation.containsKey("tagList"), is(true));
assertThat(tagsRepresentation.getJsonArray("tagList"), contains("new", "important"));

assertThat(representation.getString("itemLocationCode"), is("CN/P1"));

assertCreateEventForRequest(representation);
}

Expand Down
2 changes: 2 additions & 0 deletions src/test/java/org/folio/rest/api/StaffSlipsApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ private void canGetStaffSlipReferenceData()
JsonArray slipsJsonArray = getResponse.getJson().getJsonArray(STAFF_SLIPS_KEY);
Object [] names = slipsJsonArray.stream().map(o -> ((JsonObject) o).getString(NAME_KEY)).toArray();
assertThat(names, arrayContainingInAnyOrder(STAFF_SLIP_NAMES));
assertThat(names, arrayContainingInAnyOrder("Hold", "Transit", "Request delivery", "Pick slip",
"Search slip (Hold requests)","Due date receipt"));
}

/* Begin Tests */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.folio.rest.api.migration;

import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.folio.rest.api.StorageTestSuite;
import org.folio.rest.support.JsonResponse;
import org.folio.rest.support.ResponseHandler;
import org.hamcrest.core.Is;
import org.junit.Before;
import org.junit.Test;

import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import static org.hamcrest.MatcherAssert.assertThat;


public class DueDateSlipsMigrationScriptTest extends StaffSlipsMigrationTestBase {
public static final String DUE_DATE_RECEIPT_ID = "0b52bca7-db17-4e91-a740-7872ed6d7323";
private static final String MIGRATION_SCRIPT = loadScript("add_due_date_slips.sql");

@Before
public void beforeEach() throws MalformedURLException {
StorageTestSuite.deleteAll(staffSlipsStorageUrl(""));
}

@Test
public void canMigrateStaffSlips() throws Exception {
executeMultipleSqlStatements(MIGRATION_SCRIPT);
CompletableFuture<JsonResponse> getCompleted = new CompletableFuture<>();
client.get(staffSlipsStorageUrl(""), StorageTestSuite.TENANT_ID,
ResponseHandler.json(getCompleted));
JsonResponse getResponse = getCompleted.get(5, TimeUnit.SECONDS);

assertThat(getResponse.getStatusCode(), Is.is(HttpURLConnection.HTTP_OK));

JsonArray slipsJsonArray = getResponse.getJson().getJsonArray("staffSlips");
JsonObject staffSlips = getRecordById(slipsJsonArray, DUE_DATE_RECEIPT_ID);

assertStaffSlip(staffSlips, DUE_DATE_RECEIPT_ID, "Due date receipt");
}
}
Loading

0 comments on commit 5591f35

Please sign in to comment.