From 28aa0b72a3e651c8e5fccfa0ad8f7a222287d27e Mon Sep 17 00:00:00 2001
From: SreejaMangarapu <164345887+SreejaMangarapu@users.noreply.github.com>
Date: Wed, 18 Sep 2024 17:27:56 +0530
Subject: [PATCH 1/4] CIRCSTORE-529 Persisting due date slip template. (#490)
* CIRCSTORE-529 persisting due date slip template
* CIRCSTORE-529 module version changed
* CIRCSTORE-529 fixed sonar issue
---
.../db_scripts/add_due_date_slips.sql | 8 ++++
.../templates/db_scripts/schema.json | 5 +++
.../org/folio/rest/api/StaffSlipsApiTest.java | 2 +-
.../DueDateSlipsMigrationScriptTest.java | 44 +++++++++++++++++++
4 files changed, 58 insertions(+), 1 deletion(-)
create mode 100644 src/main/resources/templates/db_scripts/add_due_date_slips.sql
create mode 100644 src/test/java/org/folio/rest/api/migration/DueDateSlipsMigrationScriptTest.java
diff --git a/src/main/resources/templates/db_scripts/add_due_date_slips.sql b/src/main/resources/templates/db_scripts/add_due_date_slips.sql
new file mode 100644
index 00000000..d4a989e2
--- /dev/null
+++ b/src/main/resources/templates/db_scripts/add_due_date_slips.sql
@@ -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', '
'))
+ON CONFLICT DO NOTHING;
diff --git a/src/main/resources/templates/db_scripts/schema.json b/src/main/resources/templates/db_scripts/schema.json
index f9602bc8..b0390bd6 100644
--- a/src/main/resources/templates/db_scripts/schema.json
+++ b/src/main/resources/templates/db_scripts/schema.json
@@ -587,6 +587,11 @@
"run": "after",
"snippetPath": "fixSpellingOfFulfillmentPreference.sql",
"fromModuleVersion": "17.1.2"
+ },
+ {
+ "run": "after",
+ "snippetPath": "add_due_date_slips.sql",
+ "fromModuleVersion": "17.3.0"
}
]
}
diff --git a/src/test/java/org/folio/rest/api/StaffSlipsApiTest.java b/src/test/java/org/folio/rest/api/StaffSlipsApiTest.java
index d3218b8d..1810d9b7 100644
--- a/src/test/java/org/folio/rest/api/StaffSlipsApiTest.java
+++ b/src/test/java/org/folio/rest/api/StaffSlipsApiTest.java
@@ -69,7 +69,7 @@ private void canGetStaffSlipReferenceData()
JsonArray slipsJsonArray = getResponse.getJson().getJsonArray("staffSlips");
Object [] names = slipsJsonArray.stream().map(o -> ((JsonObject) o).getString(NAME_KEY)).toArray();
assertThat(names, arrayContainingInAnyOrder("Hold", "Transit", "Request delivery", "Pick slip",
- "Search slip (Hold requests)"));
+ "Search slip (Hold requests)","Due date receipt"));
}
/* Begin Tests */
diff --git a/src/test/java/org/folio/rest/api/migration/DueDateSlipsMigrationScriptTest.java b/src/test/java/org/folio/rest/api/migration/DueDateSlipsMigrationScriptTest.java
new file mode 100644
index 00000000..e79db0a4
--- /dev/null
+++ b/src/test/java/org/folio/rest/api/migration/DueDateSlipsMigrationScriptTest.java
@@ -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 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");
+ }
+}
From 651d888df52c80b76f458d5d9f38b79b09b0087b Mon Sep 17 00:00:00 2001
From: Kapil Verma <85541312+kapil-epam@users.noreply.github.com>
Date: Wed, 25 Sep 2024 15:43:35 +0530
Subject: [PATCH 2/4] CIRC-2138: added sync for request table post PrintEvent
insertions (#487)
* CIRC-2138: added sync for request table post PrintEvent insertions
* CIRC-2138: set additionalProperties false for request.printDetails
* CIRC-2138: fixed printed to isPrinted in printDetails in request sync query
* CIRC-2138: fixed sonar issues
* CIRC-2138: addded test case for 5XX case
* CIRC-2138: Bump up interface version
---
descriptors/ModuleDescriptor-template.json | 2 +-
ramls/request.json | 24 +++++
.../org/folio/rest/impl/PrintEventsApi.java | 6 +-
.../org/folio/service/PrintEventsService.java | 89 +++++++++++++++++--
.../folio/rest/api/PrintEventsAPITest.java | 13 +++
5 files changed, 119 insertions(+), 15 deletions(-)
diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json
index f4f17887..f3cf1ef2 100644
--- a/descriptors/ModuleDescriptor-template.json
+++ b/descriptors/ModuleDescriptor-template.json
@@ -179,7 +179,7 @@
},
{
"id": "request-storage",
- "version": "6.0",
+ "version": "6.1",
"handlers": [
{
"methods": ["GET"],
diff --git a/ramls/request.json b/ramls/request.json
index aa582e03..17b591a4 100644
--- a/ramls/request.json
+++ b/ramls/request.json
@@ -221,6 +221,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",
diff --git a/src/main/java/org/folio/rest/impl/PrintEventsApi.java b/src/main/java/org/folio/rest/impl/PrintEventsApi.java
index 023de7d0..fc707b13 100644
--- a/src/main/java/org/folio/rest/impl/PrintEventsApi.java
+++ b/src/main/java/org/folio/rest/impl/PrintEventsApi.java
@@ -13,8 +13,6 @@
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);
@@ -22,9 +20,7 @@ public class PrintEventsApi implements PrintEventsStorage {
public void postPrintEventsStoragePrintEventsEntry(PrintEventsRequest printEventsRequest, Map okapiHeaders, Handler> 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
diff --git a/src/main/java/org/folio/service/PrintEventsService.java b/src/main/java/org/folio/service/PrintEventsService.java
index be083823..d1e6b634 100644
--- a/src/main/java/org/folio/service/PrintEventsService.java
+++ b/src/main/java/org/folio/service/PrintEventsService.java
@@ -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;
@@ -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 okapiHeaders;
+ private final PostgresClient postgresClient;
private static final String PRINT_EVENT_FETCH_QUERY = """
WITH cte AS (
@@ -53,16 +55,44 @@ 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 okapiHeaders) {
- this.vertxContext = vertxContext;
this.okapiHeaders = okapiHeaders;
+ this.postgresClient = PgUtil.postgresClient(vertxContext, okapiHeaders);
}
- public Future create(PrintEventsRequest printEventRequest) {
+ public void create(PrintEventsRequest printEventRequest,
+ Handler> asyncResultHandler) {
LOG.info("create:: save print events {}", printEventRequest);
- List printEvents = printEventRequest.getRequestIds().stream().map(requestId -> {
+ List printEvents =
+ printEventRequest.getRequestIds().stream().map(requestId -> {
PrintEvent event = new PrintEvent();
event.setRequestId(requestId);
event.setRequesterId(printEventRequest.getRequesterId());
@@ -70,14 +100,55 @@ public Future create(PrintEventsRequest printEventRequest) {
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 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 requestIds, Handler> 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()) {
diff --git a/src/test/java/org/folio/rest/api/PrintEventsAPITest.java b/src/test/java/org/folio/rest/api/PrintEventsAPITest.java
index c7f93248..2ea8445b 100644
--- a/src/test/java/org/folio/rest/api/PrintEventsAPITest.java
+++ b/src/test/java/org/folio/rest/api/PrintEventsAPITest.java
@@ -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;
@@ -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 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 requestIds = List.of("5f5751b4-e352-4121-adca-204b0c2aec43", "5f5751b4-e352-4121-adca-204b0c2aec44");
return new JsonObject()
From 2d6a06088e2c2fd9e218a96816a69b25d356ae57 Mon Sep 17 00:00:00 2001
From: JanisSaldabols <143704574+JanisSaldabols@users.noreply.github.com>
Date: Thu, 3 Oct 2024 11:13:08 +0300
Subject: [PATCH 3/4] CIRC-2141 Add itemLocationCode to request object schema
(#491)
* CIRC-2141 Add itemLocationCode to request object schema
* CIRC-2141 Add test for itemLocationCode
---
ramls/request.json | 4 ++++
.../org/folio/rest/api/RequestsApiTest.java | 3 +++
.../builders/RequestRequestBuilder.java | 21 ++++++++++++++-----
3 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/ramls/request.json b/ramls/request.json
index 17b591a4..65d64544 100644
--- a/ramls/request.json
+++ b/ramls/request.json
@@ -255,6 +255,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,
diff --git a/src/test/java/org/folio/rest/api/RequestsApiTest.java b/src/test/java/org/folio/rest/api/RequestsApiTest.java
index 14521c85..4137de0d 100644
--- a/src/test/java/org/folio/rest/api/RequestsApiTest.java
+++ b/src/test/java/org/folio/rest/api/RequestsApiTest.java
@@ -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();
@@ -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);
}
diff --git a/src/test/java/org/folio/rest/support/builders/RequestRequestBuilder.java b/src/test/java/org/folio/rest/support/builders/RequestRequestBuilder.java
index ccb8f60c..a6c86ee5 100644
--- a/src/test/java/org/folio/rest/support/builders/RequestRequestBuilder.java
+++ b/src/test/java/org/folio/rest/support/builders/RequestRequestBuilder.java
@@ -52,6 +52,7 @@ public class RequestRequestBuilder extends JsonBuilder {
private final String patronComments;
private final UUID holdingsRecordId;
private final SearchIndex searchIndex;
+ private final String itemLocationCode;
public RequestRequestBuilder() {
this(UUID.randomUUID(),
@@ -79,6 +80,7 @@ public RequestRequestBuilder() {
null,
null,
UUID.randomUUID(),
+ null,
null);
}
@@ -162,6 +164,10 @@ public JsonObject create() {
put(request, "searchIndex", JsonObject.mapFrom(searchIndex));
}
+ if (itemLocationCode != null) {
+ put(request, "itemLocationCode", this.itemLocationCode);
+ }
+
return request;
}
@@ -204,7 +210,8 @@ public RequestRequestBuilder withNoId() {
this.tags,
this.patronComments,
this.holdingsRecordId,
- this.searchIndex);
+ this.searchIndex,
+ this.itemLocationCode);
}
public RequestRequestBuilder toHoldShelf() {
@@ -247,7 +254,8 @@ public RequestRequestBuilder withItem(RequestItemSummary item) {
this.tags,
this.patronComments,
this.holdingsRecordId,
- this.searchIndex);
+ this.searchIndex,
+ this.itemLocationCode);
}
public RequestRequestBuilder withRequester(
@@ -282,7 +290,8 @@ public RequestRequestBuilder withRequester(
this.tags,
this.patronComments,
this.holdingsRecordId,
- this.searchIndex);
+ this.searchIndex,
+ this.itemLocationCode);
}
public RequestRequestBuilder withRequester(
@@ -316,7 +325,8 @@ public RequestRequestBuilder withRequester(
this.tags,
this.patronComments,
this.holdingsRecordId,
- this.searchIndex);
+ this.searchIndex,
+ this.itemLocationCode);
}
public RequestRequestBuilder withProxy(
@@ -350,7 +360,8 @@ public RequestRequestBuilder withProxy(
this.tags,
this.patronComments,
this.holdingsRecordId,
- this.searchIndex);
+ this.searchIndex,
+ this.itemLocationCode);
}
public RequestRequestBuilder withNoPosition() {
return withPosition(null);
From afa222fee170bcf542cf06b3b8f95e18bae9dc53 Mon Sep 17 00:00:00 2001
From: SvitlanaKovalova1
Date: Wed, 16 Oct 2024 16:44:32 +0300
Subject: [PATCH 4/4] [CIRCSTORE-533] Upgrade "holdings-storage" API (#492)
---
NEWS.md | 5 ++++-
descriptors/ModuleDescriptor-template.json | 2 +-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/NEWS.md b/NEWS.md
index 7f3c2d66..52c1ae70 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -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)
diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json
index f3cf1ef2..7dc9be02 100644
--- a/descriptors/ModuleDescriptor-template.json
+++ b/descriptors/ModuleDescriptor-template.json
@@ -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",