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",