From 96b560756e0193fef4cd7583a7941cbe41ea9dc4 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Tue, 2 Jul 2024 13:34:45 +0530 Subject: [PATCH 01/16] CIRC-2099 POST Api implementation --- descriptors/ModuleDescriptor-template.json | 25 ++++++++ ramls/print-events-request.json | 38 ++++++++++++ ramls/print-events.raml | 47 +++++++++++++++ .../circulation/CirculationVerticle.java | 2 + .../circulation/domain/PrintEventRequest.java | 59 +++++++++++++++++++ .../storage/PrintEventsRepository.java | 35 +++++++++++ .../resources/PrintEventsResource.java | 47 +++++++++++++++ .../folio/circulation/support/Clients.java | 15 +++++ .../api/printEvents/PrintEventsTests.java | 34 +++++++++++ src/test/java/api/support/APITests.java | 3 + .../java/api/support/fakes/FakeOkapi.java | 5 ++ .../java/api/support/http/InterfaceUrls.java | 4 ++ .../java/api/support/http/ResourceClient.java | 4 ++ 13 files changed, 318 insertions(+) create mode 100644 ramls/print-events-request.json create mode 100644 ramls/print-events.raml create mode 100644 src/main/java/org/folio/circulation/domain/PrintEventRequest.java create mode 100644 src/main/java/org/folio/circulation/infrastructure/storage/PrintEventsRepository.java create mode 100644 src/main/java/org/folio/circulation/resources/PrintEventsResource.java create mode 100644 src/test/java/api/printEvents/PrintEventsTests.java diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 37fcbce920..178dca4dd6 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -737,6 +737,22 @@ } ] }, + { + "id": "print-events", + "version": "1.0", + "handlers": [ + { + "methods": ["POST"], + "pathPattern": "/circulation/print-events", + "permissionsRequired": [ + "circulation.print-events.post" + ], + "modulePermissions": [ + "circulation-storage.print-events.post" + ] + } + ] + }, { "id": "_timer", "version": "1.0", @@ -1318,6 +1334,10 @@ { "id": "circulation-settings-storage", "version": "1.0" + }, + { + "id": "print-events-storage", + "version": "1.0" } ], "optional": [ @@ -1327,6 +1347,11 @@ } ], "permissionSets": [ + { + "permissionName": "circulation.print-events.post", + "displayName": "circulation - create print events", + "description": "create print event logs" + }, { "permissionName": "circulation.requests.queue.reorder.collection.post", "displayName": "circulation - reorder queue for an item", diff --git a/ramls/print-events-request.json b/ramls/print-events-request.json new file mode 100644 index 0000000000..12d0d6d389 --- /dev/null +++ b/ramls/print-events-request.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Print Events Request", + "type": "object", + "properties": { + "requestIds": { + "description": "List of request IDs", + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "requesterId": { + "description": "ID of the requester", + "type": "string", + "minLength": 1, + "pattern": "^(?!\\s*$).+" + }, + "requesterName": { + "description": "Name of the requester", + "type": "string", + "minLength": 1, + "pattern": "^(?!\\s*$).+" + }, + "printEventDate": { + "description": "Date and time when the print command is executed", + "type": "string", + "format": "date-time" + } + }, + "required": [ + "requestIds", + "requesterId", + "requesterName", + "printEventDate" + ] +} diff --git a/ramls/print-events.raml b/ramls/print-events.raml new file mode 100644 index 0000000000..cf8b36c63f --- /dev/null +++ b/ramls/print-events.raml @@ -0,0 +1,47 @@ +#%RAML 1.0 +title: Print Events +version: v1.0 +protocols: [ HTTP, HTTPS ] +baseUri: http://localhost:9130 + +documentation: + - title: Print Events API + content: Api for print events + +types: + print-events-request: !include print-events-request.json + errors: !include raml-util/schemas/errors.schema + +traits: + validate: !include raml-util/traits/validation.raml + +/circulation/print-events: + post: + is: [validate] + description: save a print event log + body: + application/json: + type: print-events-request + responses: + 201: + description: "All items have been successfully created or updated" + 409: + description: "Optimistic locking version conflict" + body: + text/plain: + example: "version error" + 413: + description: "Payload Too Large" + body: + text/plain: + example: "Payload Too Large" + 422: + description: "Unprocessable Entity" + body: + application/json: + type: errors + 500: + description: "Internal server error" + body: + text/plain: + example: "Internal server error" diff --git a/src/main/java/org/folio/circulation/CirculationVerticle.java b/src/main/java/org/folio/circulation/CirculationVerticle.java index f0d2371440..4956ddb53a 100644 --- a/src/main/java/org/folio/circulation/CirculationVerticle.java +++ b/src/main/java/org/folio/circulation/CirculationVerticle.java @@ -31,6 +31,7 @@ import org.folio.circulation.resources.OverdueFineCirculationRulesEngineResource; import org.folio.circulation.resources.OverdueFineScheduledNoticeProcessingResource; import org.folio.circulation.resources.PickSlipsResource; +import org.folio.circulation.resources.PrintEventsResource; import org.folio.circulation.resources.RequestByInstanceIdResource; import org.folio.circulation.resources.RequestCirculationRulesEngineResource; import org.folio.circulation.resources.RequestCollectionResource; @@ -152,6 +153,7 @@ public void start(Promise startFuture) { new LoanRelatedFeeFineClosedHandlerResource(client).register(router); new FeeFineBalanceChangedHandlerResource(client).register(router); new CirculationSettingsResource(client).register(router); + new PrintEventsResource(client).register(router); server.requestHandler(router) .listen(config().getInteger("port"), result -> { diff --git a/src/main/java/org/folio/circulation/domain/PrintEventRequest.java b/src/main/java/org/folio/circulation/domain/PrintEventRequest.java new file mode 100644 index 0000000000..0145f4256b --- /dev/null +++ b/src/main/java/org/folio/circulation/domain/PrintEventRequest.java @@ -0,0 +1,59 @@ +package org.folio.circulation.domain; + +import io.vertx.core.json.JsonObject; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.Set; + +import static lombok.AccessLevel.PRIVATE; +import static org.folio.circulation.support.json.JsonPropertyFetcher.getArrayProperty; +import static org.folio.circulation.support.json.JsonPropertyFetcher.getProperty; + +@AllArgsConstructor(access = PRIVATE) +@ToString(onlyExplicitlyIncluded = true) +public class PrintEventRequest { + private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + public static final String REQUEST_IDS_FIELD = "requestIds"; + public static final String REQUESTER_ID_FIELD = "requesterId"; + public static final String REQUESTER_NAME_FIELD = "requesterName"; + public static final String PRINT_DATE_FIELD = "printEventDate"; + + @ToString.Include + @Getter + private final JsonObject representation; + + @Getter + private final List requestIds; + @Getter + private final String requesterId; + @Getter + private final String requesterName; + @Getter + private final String printEventDate; + + public static PrintEventRequest from(JsonObject representation) { + final var requestIds = getArrayProperty(representation, REQUEST_IDS_FIELD).stream() + .map(String.class::cast) + .toList(); + final var requesterId = getProperty(representation, REQUESTER_ID_FIELD); + final var requesterName = getProperty(representation, REQUESTER_NAME_FIELD); + final var printEventDate = getProperty(representation, PRINT_DATE_FIELD); + + if (requestIds.isEmpty() || null == requesterId || null == requesterName || null == printEventDate || !containsOnlyKnownFields(representation)) { + log.info("from:: Print Event Request JSON is invalid: {},{},{},{},{}", representation, requestIds, requesterName, requesterId, printEventDate); + return null; + } + return new PrintEventRequest(representation, requestIds, requesterId, requesterName, printEventDate); + } + + private static boolean containsOnlyKnownFields(JsonObject representation) { + return Set.of(REQUEST_IDS_FIELD, REQUESTER_ID_FIELD, REQUESTER_NAME_FIELD, PRINT_DATE_FIELD) + .containsAll(representation.fieldNames()); + } +} diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/PrintEventsRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/PrintEventsRepository.java new file mode 100644 index 0000000000..1a179857ff --- /dev/null +++ b/src/main/java/org/folio/circulation/infrastructure/storage/PrintEventsRepository.java @@ -0,0 +1,35 @@ +package org.folio.circulation.infrastructure.storage; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.circulation.domain.PrintEventRequest; +import org.folio.circulation.support.Clients; +import org.folio.circulation.support.CollectionResourceClient; +import org.folio.circulation.support.http.client.ResponseInterpreter; +import org.folio.circulation.support.results.Result; + +import java.lang.invoke.MethodHandles; +import java.util.concurrent.CompletableFuture; + +import static org.folio.circulation.support.http.ResponseMapping.forwardOnFailure; +import static org.folio.circulation.support.results.Result.succeeded; + +public class PrintEventsRepository { + private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + + private final CollectionResourceClient printEventsStorageClient; + + public PrintEventsRepository(Clients clients) { + printEventsStorageClient = clients.printEventsStorageClient(); + } + + public CompletableFuture> create(PrintEventRequest printEventRequest) { + log.info("create:: parameters printEvent: {}", printEventRequest); + final var storagePrintEventRequest = printEventRequest.getRepresentation(); + final ResponseInterpreter interpreter = new ResponseInterpreter() + .on(201, succeeded(null)) + .otherwise(forwardOnFailure()); + return printEventsStorageClient.post(storagePrintEventRequest).thenApply(interpreter::flatMap); + } + +} diff --git a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java new file mode 100644 index 0000000000..e284206b0d --- /dev/null +++ b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java @@ -0,0 +1,47 @@ +package org.folio.circulation.resources; + +import io.vertx.core.http.HttpClient; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.circulation.domain.PrintEventRequest; +import org.folio.circulation.infrastructure.storage.PrintEventsRepository; +import org.folio.circulation.support.Clients; +import org.folio.circulation.support.RouteRegistration; +import org.folio.circulation.support.http.server.JsonHttpResponse; +import org.folio.circulation.support.http.server.WebContext; + +import java.lang.invoke.MethodHandles; + +import static org.folio.circulation.support.results.Result.ofAsync; + +public class PrintEventsResource extends Resource { + private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + + public PrintEventsResource(HttpClient client) { + super(client); + } + + @Override + public void register(Router router) { + new RouteRegistration("/circulation/print-events", router) + .create(this::create); + } + + void create(RoutingContext routingContext) { + final var context = new WebContext(routingContext); + final var clients = Clients.create(context, client); + final var printEventsRepository = new PrintEventsRepository(clients); + + final var incomingRepresentation = routingContext.body().asJsonObject(); + final var printEventRequest = PrintEventRequest.from(incomingRepresentation); + + log.info("create:: Creating print event: {}", () -> printEventRequest); + + ofAsync(printEventRequest) + .thenCompose(r -> r.after(printEventsRepository::create)) + .thenApply(r -> r.map(response -> JsonHttpResponse.created(null, null))) + .thenAccept(context::writeResultToHttpResponse); + } +} diff --git a/src/main/java/org/folio/circulation/support/Clients.java b/src/main/java/org/folio/circulation/support/Clients.java index 404b433461..78cd6271dc 100644 --- a/src/main/java/org/folio/circulation/support/Clients.java +++ b/src/main/java/org/folio/circulation/support/Clients.java @@ -70,6 +70,8 @@ public class Clients { private final CollectionResourceClient circulationItemClient; private final GetManyRecordsClient settingsStorageClient; private final CollectionResourceClient circulationSettingsStorageClient; + private final CollectionResourceClient printEventsStorageClient; + public static Clients create(WebContext context, HttpClient httpClient) { return new Clients(context.createHttpClient(httpClient), context); @@ -138,6 +140,8 @@ private Clients(OkapiHttpClient client, WebContext context) { settingsStorageClient = createSettingsStorageClient(client, context); circulationItemClient = createCirculationItemClient(client, context); circulationSettingsStorageClient = createCirculationSettingsStorageClient(client, context); + printEventsStorageClient = createPrintEventsStorageClient(client, context); + } catch(MalformedURLException e) { throw new InvalidOkapiLocationException(context.getOkapiLocation(), e); @@ -380,6 +384,10 @@ public CollectionResourceClient circulationSettingsStorageClient() { return circulationSettingsStorageClient; } + public CollectionResourceClient printEventsStorageClient() { + return printEventsStorageClient; + } + private static CollectionResourceClient getCollectionResourceClient( OkapiHttpClient client, WebContext context, String path) @@ -814,6 +822,13 @@ private CollectionResourceClient createCirculationSettingsStorageClient( "/circulation-settings-storage/circulation-settings"); } + private CollectionResourceClient createPrintEventsStorageClient( + OkapiHttpClient client, WebContext context) throws MalformedURLException { + + return getCollectionResourceClient(client, context, + "/print-events-storage/print-events"); + } + private GetManyRecordsClient createSettingsStorageClient( OkapiHttpClient client, WebContext context) throws MalformedURLException { diff --git a/src/test/java/api/printEvents/PrintEventsTests.java b/src/test/java/api/printEvents/PrintEventsTests.java new file mode 100644 index 0000000000..744f36db5c --- /dev/null +++ b/src/test/java/api/printEvents/PrintEventsTests.java @@ -0,0 +1,34 @@ +package api.printEvents; + +import api.support.APITests; +import io.vertx.core.json.JsonObject; +import org.folio.circulation.support.http.client.Response; +import org.hamcrest.core.Is; +import org.junit.jupiter.api.Test; + +import java.net.HttpURLConnection; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; + + class PrintEventsTests extends APITests { + public static final String REQUEST_IDS_FIELD = "requestIds"; + public static final String REQUESTER_ID_FIELD = "requesterId"; + public static final String REQUESTER_NAME_FIELD = "requesterName"; + public static final String PRINT_DATE_FIELD = "printEventDate"; + + @Test + void postPrintEventsTest() { + JsonObject printRequest = getPrintEvent(); + Response response = printEventsClient.attemptCreate(printRequest); + assertThat(response.getStatusCode(), Is.is(HttpURLConnection.HTTP_CREATED)); + } + private JsonObject getPrintEvent() { + List requestIds = List.of("request1", "request2"); + return new JsonObject() + .put(REQUEST_IDS_FIELD, requestIds) + .put(REQUESTER_ID_FIELD, "sample") + .put(REQUESTER_NAME_FIELD, "Sample Requester") + .put(PRINT_DATE_FIELD, "2024-06-25T14:30:00Z"); + } +} diff --git a/src/test/java/api/support/APITests.java b/src/test/java/api/support/APITests.java index b872ce7165..6935d04e64 100644 --- a/src/test/java/api/support/APITests.java +++ b/src/test/java/api/support/APITests.java @@ -197,6 +197,9 @@ public abstract class APITests { protected final ResourceClient circulationSettingsClient = ResourceClient.forCirculationSettings(); + protected final ResourceClient printEventsClient = + ResourceClient.forPrintEvents(); + protected final ServicePointsFixture servicePointsFixture = new ServicePointsFixture(servicePointsClient); diff --git a/src/test/java/api/support/fakes/FakeOkapi.java b/src/test/java/api/support/fakes/FakeOkapi.java index 5cbecb8542..1275386fa4 100644 --- a/src/test/java/api/support/fakes/FakeOkapi.java +++ b/src/test/java/api/support/fakes/FakeOkapi.java @@ -422,6 +422,11 @@ public void start(Promise startFuture) throws IOException { .withChangeMetadata() .create().register(router); + new FakeStorageModuleBuilder() + .withRootPath("/print-events-storage/print-events") + .withChangeMetadata() + .create().register(router); + new FakeFeeFineOperationsModule().register(router); server.requestHandler(router) diff --git a/src/test/java/api/support/http/InterfaceUrls.java b/src/test/java/api/support/http/InterfaceUrls.java index 59d35de534..f85577d3a6 100644 --- a/src/test/java/api/support/http/InterfaceUrls.java +++ b/src/test/java/api/support/http/InterfaceUrls.java @@ -337,4 +337,8 @@ public static URL settingsStorageUrl() { public static URL circulationSettingsUrl(String subPath) { return circulationModuleUrl("/circulation/settings" + subPath); } + + public static URL printEventsUrl(String subPath) { + return circulationModuleUrl("/circulation/print-events" + subPath); + } } diff --git a/src/test/java/api/support/http/ResourceClient.java b/src/test/java/api/support/http/ResourceClient.java index 8e9b7d63be..73893b3b33 100644 --- a/src/test/java/api/support/http/ResourceClient.java +++ b/src/test/java/api/support/http/ResourceClient.java @@ -276,6 +276,10 @@ public static ResourceClient forCirculationSettings() { return new ResourceClient(InterfaceUrls::circulationSettingsUrl, "circulationSettings"); } + public static ResourceClient forPrintEvents() { + return new ResourceClient(InterfaceUrls::printEventsUrl, " "); + } + private ResourceClient(UrlMaker urlMaker, String collectionArrayPropertyName) { this.urlMaker = urlMaker; this.collectionArrayPropertyName = collectionArrayPropertyName; From ee8f0815ebb4b9d92053b8c6930bb845ddb298b4 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Wed, 3 Jul 2024 13:57:52 +0530 Subject: [PATCH 02/16] CIRC-2099 POST Api implementation --- .../resources/PrintEventsResource.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java index e284206b0d..c18fbe6f96 100644 --- a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java +++ b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java @@ -11,10 +11,14 @@ import org.folio.circulation.support.RouteRegistration; import org.folio.circulation.support.http.server.JsonHttpResponse; import org.folio.circulation.support.http.server.WebContext; +import org.folio.circulation.support.results.Result; import java.lang.invoke.MethodHandles; +import java.util.function.Function; +import static org.folio.circulation.support.ValidationErrorFailure.singleValidationError; import static org.folio.circulation.support.results.Result.ofAsync; +import static org.folio.circulation.support.results.Result.succeeded; public class PrintEventsResource extends Resource { private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); @@ -40,8 +44,18 @@ void create(RoutingContext routingContext) { log.info("create:: Creating print event: {}", () -> printEventRequest); ofAsync(printEventRequest) + .thenApply(refuseWhenPrintEventRequestIsInvalid()) .thenCompose(r -> r.after(printEventsRepository::create)) - .thenApply(r -> r.map(response -> JsonHttpResponse.created(null, null))) + .thenApply(r -> r.map(response -> { + assert printEventRequest != null; + return JsonHttpResponse.created(printEventRequest.getRepresentation(), null); + })) .thenAccept(context::writeResultToHttpResponse); } + + private static Function, Result> + refuseWhenPrintEventRequestIsInvalid() { + return r -> r.failWhen(printEventRequest -> succeeded(printEventRequest == null), + circulationSetting -> singleValidationError("Print Event Request JSON is invalid", "", "")); + } } From 57b915678b19e540cce82ba4172254e5c6c02135 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Wed, 3 Jul 2024 17:45:07 +0530 Subject: [PATCH 03/16] CIRC-2099 POST Api implementation --- .../org/folio/circulation/resources/PrintEventsResource.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java index c18fbe6f96..22f9e64626 100644 --- a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java +++ b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java @@ -37,11 +37,10 @@ void create(RoutingContext routingContext) { final var context = new WebContext(routingContext); final var clients = Clients.create(context, client); final var printEventsRepository = new PrintEventsRepository(clients); - final var incomingRepresentation = routingContext.body().asJsonObject(); final var printEventRequest = PrintEventRequest.from(incomingRepresentation); - log.info("create:: Creating print event: {}", () -> printEventRequest); + log.info("create:: Creating print event: {}", printEventRequest); ofAsync(printEventRequest) .thenApply(refuseWhenPrintEventRequestIsInvalid()) From 6fd68143a7ad3576eaab39d0026456d2a480865c Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Thu, 4 Jul 2024 14:25:44 +0530 Subject: [PATCH 04/16] CIRC-2099 Modified TestClass --- .../api/printEvents/PrintEventsTests.java | 52 ++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/src/test/java/api/printEvents/PrintEventsTests.java b/src/test/java/api/printEvents/PrintEventsTests.java index 744f36db5c..95e70421df 100644 --- a/src/test/java/api/printEvents/PrintEventsTests.java +++ b/src/test/java/api/printEvents/PrintEventsTests.java @@ -3,32 +3,60 @@ import api.support.APITests; import io.vertx.core.json.JsonObject; import org.folio.circulation.support.http.client.Response; -import org.hamcrest.core.Is; import org.junit.jupiter.api.Test; -import java.net.HttpURLConnection; import java.util.List; +import static api.support.matchers.ResponseStatusCodeMatcher.hasStatus; +import static org.folio.HttpStatus.HTTP_CREATED; +import static org.folio.HttpStatus.HTTP_UNPROCESSABLE_ENTITY; import static org.hamcrest.MatcherAssert.assertThat; - class PrintEventsTests extends APITests { +class PrintEventsTests extends APITests { public static final String REQUEST_IDS_FIELD = "requestIds"; public static final String REQUESTER_ID_FIELD = "requesterId"; public static final String REQUESTER_NAME_FIELD = "requesterName"; public static final String PRINT_DATE_FIELD = "printEventDate"; + public static final String INVALID_FIELD = "invalidField"; @Test void postPrintEventsTest() { JsonObject printRequest = getPrintEvent(); Response response = printEventsClient.attemptCreate(printRequest); - assertThat(response.getStatusCode(), Is.is(HttpURLConnection.HTTP_CREATED)); + assertThat(response, hasStatus(HTTP_CREATED)); + } + + @Test + void postPrintEventsWithInvalidField() { + JsonObject printRequest = getPrintEvent(); + printRequest.put(INVALID_FIELD, "invalid"); + Response response = printEventsClient.attemptCreate(printRequest); + assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); + } + + @Test + void postPrintEventsWithInvalidField_EmptyRequestIdsList() { + JsonObject printRequest = getPrintEvent(); + List requestIds = List.of(); + printRequest.put(REQUEST_IDS_FIELD, requestIds); + Response response = printEventsClient.attemptCreate(printRequest); + assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); + } + + @Test + void postPrintEventsWithInvalidField_NullFeild() { + JsonObject printRequest = getPrintEvent(); + printRequest.put(REQUESTER_ID_FIELD, null); + Response response = printEventsClient.attemptCreate(printRequest); + assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); + } + + private JsonObject getPrintEvent() { + List requestIds = List.of("request1", "request2"); + return new JsonObject() + .put(REQUEST_IDS_FIELD, requestIds) + .put(REQUESTER_ID_FIELD, "sreeja") + .put(REQUESTER_NAME_FIELD, "Sample Requester") + .put(PRINT_DATE_FIELD, "2024-06-25T14:30:00Z"); } - private JsonObject getPrintEvent() { - List requestIds = List.of("request1", "request2"); - return new JsonObject() - .put(REQUEST_IDS_FIELD, requestIds) - .put(REQUESTER_ID_FIELD, "sample") - .put(REQUESTER_NAME_FIELD, "Sample Requester") - .put(PRINT_DATE_FIELD, "2024-06-25T14:30:00Z"); - } } From e342831ce780febd90a4e5d3e5db28c78f9854e3 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Thu, 4 Jul 2024 14:50:16 +0530 Subject: [PATCH 05/16] CIRC-2099 removed raml files --- ramls/print-events-request.json | 38 -------------------------- ramls/print-events.raml | 47 --------------------------------- 2 files changed, 85 deletions(-) delete mode 100644 ramls/print-events-request.json delete mode 100644 ramls/print-events.raml diff --git a/ramls/print-events-request.json b/ramls/print-events-request.json deleted file mode 100644 index 12d0d6d389..0000000000 --- a/ramls/print-events-request.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Print Events Request", - "type": "object", - "properties": { - "requestIds": { - "description": "List of request IDs", - "type": "array", - "minItems": 1, - "items": { - "type": "string" - } - }, - "requesterId": { - "description": "ID of the requester", - "type": "string", - "minLength": 1, - "pattern": "^(?!\\s*$).+" - }, - "requesterName": { - "description": "Name of the requester", - "type": "string", - "minLength": 1, - "pattern": "^(?!\\s*$).+" - }, - "printEventDate": { - "description": "Date and time when the print command is executed", - "type": "string", - "format": "date-time" - } - }, - "required": [ - "requestIds", - "requesterId", - "requesterName", - "printEventDate" - ] -} diff --git a/ramls/print-events.raml b/ramls/print-events.raml deleted file mode 100644 index cf8b36c63f..0000000000 --- a/ramls/print-events.raml +++ /dev/null @@ -1,47 +0,0 @@ -#%RAML 1.0 -title: Print Events -version: v1.0 -protocols: [ HTTP, HTTPS ] -baseUri: http://localhost:9130 - -documentation: - - title: Print Events API - content: Api for print events - -types: - print-events-request: !include print-events-request.json - errors: !include raml-util/schemas/errors.schema - -traits: - validate: !include raml-util/traits/validation.raml - -/circulation/print-events: - post: - is: [validate] - description: save a print event log - body: - application/json: - type: print-events-request - responses: - 201: - description: "All items have been successfully created or updated" - 409: - description: "Optimistic locking version conflict" - body: - text/plain: - example: "version error" - 413: - description: "Payload Too Large" - body: - text/plain: - example: "Payload Too Large" - 422: - description: "Unprocessable Entity" - body: - application/json: - type: errors - 500: - description: "Internal server error" - body: - text/plain: - example: "Internal server error" From 17e14a97376dfe5f0bf39ac5487084befad05977 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Fri, 5 Jul 2024 17:50:56 +0530 Subject: [PATCH 06/16] CIRC-2099 changes in the root path of print-event-storage --- src/main/java/org/folio/circulation/support/Clients.java | 2 +- src/test/java/api/support/fakes/FakeOkapi.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/folio/circulation/support/Clients.java b/src/main/java/org/folio/circulation/support/Clients.java index 78cd6271dc..4f79e5836a 100644 --- a/src/main/java/org/folio/circulation/support/Clients.java +++ b/src/main/java/org/folio/circulation/support/Clients.java @@ -826,7 +826,7 @@ private CollectionResourceClient createPrintEventsStorageClient( OkapiHttpClient client, WebContext context) throws MalformedURLException { return getCollectionResourceClient(client, context, - "/print-events-storage/print-events"); + "/print-events-storage"); } private GetManyRecordsClient createSettingsStorageClient( diff --git a/src/test/java/api/support/fakes/FakeOkapi.java b/src/test/java/api/support/fakes/FakeOkapi.java index 1275386fa4..f8742a1c36 100644 --- a/src/test/java/api/support/fakes/FakeOkapi.java +++ b/src/test/java/api/support/fakes/FakeOkapi.java @@ -423,7 +423,7 @@ public void start(Promise startFuture) throws IOException { .create().register(router); new FakeStorageModuleBuilder() - .withRootPath("/print-events-storage/print-events") + .withRootPath("/print-events-storage") .withChangeMetadata() .create().register(router); From ff553747170daa8998a4880fbb677021ff24f4d4 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Mon, 8 Jul 2024 11:42:38 +0530 Subject: [PATCH 07/16] CIRC-2099 testing --- src/main/java/org/folio/circulation/support/Clients.java | 2 +- src/test/java/api/printEvents/PrintEventsTests.java | 2 +- src/test/java/api/support/fakes/FakeOkapi.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/folio/circulation/support/Clients.java b/src/main/java/org/folio/circulation/support/Clients.java index 4f79e5836a..78cd6271dc 100644 --- a/src/main/java/org/folio/circulation/support/Clients.java +++ b/src/main/java/org/folio/circulation/support/Clients.java @@ -826,7 +826,7 @@ private CollectionResourceClient createPrintEventsStorageClient( OkapiHttpClient client, WebContext context) throws MalformedURLException { return getCollectionResourceClient(client, context, - "/print-events-storage"); + "/print-events-storage/print-events"); } private GetManyRecordsClient createSettingsStorageClient( diff --git a/src/test/java/api/printEvents/PrintEventsTests.java b/src/test/java/api/printEvents/PrintEventsTests.java index 95e70421df..f27e32fbb3 100644 --- a/src/test/java/api/printEvents/PrintEventsTests.java +++ b/src/test/java/api/printEvents/PrintEventsTests.java @@ -44,7 +44,7 @@ void postPrintEventsWithInvalidField_EmptyRequestIdsList() { } @Test - void postPrintEventsWithInvalidField_NullFeild() { + void postPrintEventsWithInvalidField_NullField() { JsonObject printRequest = getPrintEvent(); printRequest.put(REQUESTER_ID_FIELD, null); Response response = printEventsClient.attemptCreate(printRequest); diff --git a/src/test/java/api/support/fakes/FakeOkapi.java b/src/test/java/api/support/fakes/FakeOkapi.java index f8742a1c36..1275386fa4 100644 --- a/src/test/java/api/support/fakes/FakeOkapi.java +++ b/src/test/java/api/support/fakes/FakeOkapi.java @@ -423,7 +423,7 @@ public void start(Promise startFuture) throws IOException { .create().register(router); new FakeStorageModuleBuilder() - .withRootPath("/print-events-storage") + .withRootPath("/print-events-storage/print-events") .withChangeMetadata() .create().register(router); From d5f82d08be66d104b75ab2eee1ba413e839793c3 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Mon, 8 Jul 2024 14:09:08 +0530 Subject: [PATCH 08/16] CIRC-2099 testing --- ramls/examples/print-events-request.json | 9 +++++++++ src/main/java/org/folio/circulation/support/Clients.java | 2 +- src/test/java/api/support/fakes/FakeOkapi.java | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 ramls/examples/print-events-request.json diff --git a/ramls/examples/print-events-request.json b/ramls/examples/print-events-request.json new file mode 100644 index 0000000000..8505827a64 --- /dev/null +++ b/ramls/examples/print-events-request.json @@ -0,0 +1,9 @@ +{ + "requestIds": [ + "059e54bb-53e5-4039-a2fb-b34358e88b0a", + "e70dcbae-30c6-47ac-94f8-4ffefd44a935" + ], + "requesterId": "d51470ea-5daa-480b-a4aa-09c8c6d9940e", + "requesterName": "requester", + "printEventDate": "2024-06-25T20:00:00+05:30" +} diff --git a/src/main/java/org/folio/circulation/support/Clients.java b/src/main/java/org/folio/circulation/support/Clients.java index 78cd6271dc..4b1ddf594c 100644 --- a/src/main/java/org/folio/circulation/support/Clients.java +++ b/src/main/java/org/folio/circulation/support/Clients.java @@ -826,7 +826,7 @@ private CollectionResourceClient createPrintEventsStorageClient( OkapiHttpClient client, WebContext context) throws MalformedURLException { return getCollectionResourceClient(client, context, - "/print-events-storage/print-events"); + "/print-events-storage/print-events/create-batch"); } private GetManyRecordsClient createSettingsStorageClient( diff --git a/src/test/java/api/support/fakes/FakeOkapi.java b/src/test/java/api/support/fakes/FakeOkapi.java index 1275386fa4..7c3b93fcba 100644 --- a/src/test/java/api/support/fakes/FakeOkapi.java +++ b/src/test/java/api/support/fakes/FakeOkapi.java @@ -423,7 +423,7 @@ public void start(Promise startFuture) throws IOException { .create().register(router); new FakeStorageModuleBuilder() - .withRootPath("/print-events-storage/print-events") + .withRootPath("/print-events-storage/print-events/create-batch") .withChangeMetadata() .create().register(router); From 5c22e90965403685cf4e69356ea8e5341515e8a5 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Mon, 8 Jul 2024 16:45:29 +0530 Subject: [PATCH 09/16] CIRC-2099 path modified to /create-batch --- descriptors/ModuleDescriptor-template.json | 2 +- .../resources/PrintEventsResource.java | 2 +- .../api/printEvents/PrintEventsTests.java | 30 ++++++++----------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 178dca4dd6..cf16161568 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -743,7 +743,7 @@ "handlers": [ { "methods": ["POST"], - "pathPattern": "/circulation/print-events", + "pathPattern": "/circulation/print-events/create-batch", "permissionsRequired": [ "circulation.print-events.post" ], diff --git a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java index 22f9e64626..358e5b0c41 100644 --- a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java +++ b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java @@ -29,7 +29,7 @@ public PrintEventsResource(HttpClient client) { @Override public void register(Router router) { - new RouteRegistration("/circulation/print-events", router) + new RouteRegistration("/circulation/print-events/create-batch", router) .create(this::create); } diff --git a/src/test/java/api/printEvents/PrintEventsTests.java b/src/test/java/api/printEvents/PrintEventsTests.java index f27e32fbb3..0d23cbda85 100644 --- a/src/test/java/api/printEvents/PrintEventsTests.java +++ b/src/test/java/api/printEvents/PrintEventsTests.java @@ -7,30 +7,26 @@ import java.util.List; +import static api.support.http.InterfaceUrls.printEventsUrl; import static api.support.matchers.ResponseStatusCodeMatcher.hasStatus; import static org.folio.HttpStatus.HTTP_CREATED; import static org.folio.HttpStatus.HTTP_UNPROCESSABLE_ENTITY; import static org.hamcrest.MatcherAssert.assertThat; class PrintEventsTests extends APITests { - public static final String REQUEST_IDS_FIELD = "requestIds"; - public static final String REQUESTER_ID_FIELD = "requesterId"; - public static final String REQUESTER_NAME_FIELD = "requesterName"; - public static final String PRINT_DATE_FIELD = "printEventDate"; - public static final String INVALID_FIELD = "invalidField"; @Test void postPrintEventsTest() { JsonObject printRequest = getPrintEvent(); - Response response = printEventsClient.attemptCreate(printRequest); + Response response = restAssuredClient.post(printRequest, printEventsUrl("/create-batch"), "post-print-event"); assertThat(response, hasStatus(HTTP_CREATED)); } @Test void postPrintEventsWithInvalidField() { JsonObject printRequest = getPrintEvent(); - printRequest.put(INVALID_FIELD, "invalid"); - Response response = printEventsClient.attemptCreate(printRequest); + printRequest.put("invalidField", "invalid"); + Response response = restAssuredClient.post(printRequest, printEventsUrl("/create-batch"), "post-print-event"); assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); } @@ -38,25 +34,25 @@ void postPrintEventsWithInvalidField() { void postPrintEventsWithInvalidField_EmptyRequestIdsList() { JsonObject printRequest = getPrintEvent(); List requestIds = List.of(); - printRequest.put(REQUEST_IDS_FIELD, requestIds); - Response response = printEventsClient.attemptCreate(printRequest); + printRequest.put("requestIds", requestIds); + Response response = restAssuredClient.post(printRequest, printEventsUrl("/create-batch"), "post-print-event"); assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); } @Test void postPrintEventsWithInvalidField_NullField() { JsonObject printRequest = getPrintEvent(); - printRequest.put(REQUESTER_ID_FIELD, null); - Response response = printEventsClient.attemptCreate(printRequest); + printRequest.put("requesterId", null); + Response response = restAssuredClient.post(printRequest, printEventsUrl("/create-batch"), "post-print-event"); assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); } private JsonObject getPrintEvent() { - List requestIds = List.of("request1", "request2"); + List requestIds = List.of("5f5751b4-e352-4121-adca-204b0c2aec43", "5f5751b4-e352-4121-adca-204b0c2aec44"); return new JsonObject() - .put(REQUEST_IDS_FIELD, requestIds) - .put(REQUESTER_ID_FIELD, "sreeja") - .put(REQUESTER_NAME_FIELD, "Sample Requester") - .put(PRINT_DATE_FIELD, "2024-06-25T14:30:00Z"); + .put("requestIds", requestIds) + .put("requesterId", "5f5751b4-e352-4121-adca-204b0c2aec43") + .put("requesterName", "requester") + .put("printEventDate", "2024-06-25T14:30:00Z"); } } From 14f4e4eaf42ff113c39424fc0fca603899446656 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Wed, 10 Jul 2024 12:52:21 +0530 Subject: [PATCH 10/16] CIRC-2099 path modified to /circulation/print-events-entry --- descriptors/ModuleDescriptor-template.json | 2 +- .../folio/circulation/resources/PrintEventsResource.java | 2 +- src/main/java/org/folio/circulation/support/Clients.java | 2 +- src/test/java/api/printEvents/PrintEventsTests.java | 8 ++++---- src/test/java/api/support/fakes/FakeOkapi.java | 2 +- src/test/java/api/support/http/InterfaceUrls.java | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index cf16161568..d038e74136 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -743,7 +743,7 @@ "handlers": [ { "methods": ["POST"], - "pathPattern": "/circulation/print-events/create-batch", + "pathPattern": "/circulation/print-events-entry", "permissionsRequired": [ "circulation.print-events.post" ], diff --git a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java index 358e5b0c41..aa10754e8f 100644 --- a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java +++ b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java @@ -29,7 +29,7 @@ public PrintEventsResource(HttpClient client) { @Override public void register(Router router) { - new RouteRegistration("/circulation/print-events/create-batch", router) + new RouteRegistration("/circulation/print-events-entry", router) .create(this::create); } diff --git a/src/main/java/org/folio/circulation/support/Clients.java b/src/main/java/org/folio/circulation/support/Clients.java index 4b1ddf594c..99be88183c 100644 --- a/src/main/java/org/folio/circulation/support/Clients.java +++ b/src/main/java/org/folio/circulation/support/Clients.java @@ -826,7 +826,7 @@ private CollectionResourceClient createPrintEventsStorageClient( OkapiHttpClient client, WebContext context) throws MalformedURLException { return getCollectionResourceClient(client, context, - "/print-events-storage/print-events/create-batch"); + "/print-events-storage/print-events-entry"); } private GetManyRecordsClient createSettingsStorageClient( diff --git a/src/test/java/api/printEvents/PrintEventsTests.java b/src/test/java/api/printEvents/PrintEventsTests.java index 0d23cbda85..e2d48ea0a8 100644 --- a/src/test/java/api/printEvents/PrintEventsTests.java +++ b/src/test/java/api/printEvents/PrintEventsTests.java @@ -18,7 +18,7 @@ class PrintEventsTests extends APITests { @Test void postPrintEventsTest() { JsonObject printRequest = getPrintEvent(); - Response response = restAssuredClient.post(printRequest, printEventsUrl("/create-batch"), "post-print-event"); + Response response = restAssuredClient.post(printRequest, printEventsUrl("/print-events-entry"), "post-print-event"); assertThat(response, hasStatus(HTTP_CREATED)); } @@ -26,7 +26,7 @@ void postPrintEventsTest() { void postPrintEventsWithInvalidField() { JsonObject printRequest = getPrintEvent(); printRequest.put("invalidField", "invalid"); - Response response = restAssuredClient.post(printRequest, printEventsUrl("/create-batch"), "post-print-event"); + Response response = restAssuredClient.post(printRequest, printEventsUrl("/print-events-entry"), "post-print-event"); assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); } @@ -35,7 +35,7 @@ void postPrintEventsWithInvalidField_EmptyRequestIdsList() { JsonObject printRequest = getPrintEvent(); List requestIds = List.of(); printRequest.put("requestIds", requestIds); - Response response = restAssuredClient.post(printRequest, printEventsUrl("/create-batch"), "post-print-event"); + Response response = restAssuredClient.post(printRequest, printEventsUrl("/print-events-entry"), "post-print-event"); assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); } @@ -43,7 +43,7 @@ void postPrintEventsWithInvalidField_EmptyRequestIdsList() { void postPrintEventsWithInvalidField_NullField() { JsonObject printRequest = getPrintEvent(); printRequest.put("requesterId", null); - Response response = restAssuredClient.post(printRequest, printEventsUrl("/create-batch"), "post-print-event"); + Response response = restAssuredClient.post(printRequest, printEventsUrl("/print-events-entry"), "post-print-event"); assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); } diff --git a/src/test/java/api/support/fakes/FakeOkapi.java b/src/test/java/api/support/fakes/FakeOkapi.java index 7c3b93fcba..1ed5b8f5b0 100644 --- a/src/test/java/api/support/fakes/FakeOkapi.java +++ b/src/test/java/api/support/fakes/FakeOkapi.java @@ -423,7 +423,7 @@ public void start(Promise startFuture) throws IOException { .create().register(router); new FakeStorageModuleBuilder() - .withRootPath("/print-events-storage/print-events/create-batch") + .withRootPath("/print-events-storage/print-events-entry") .withChangeMetadata() .create().register(router); diff --git a/src/test/java/api/support/http/InterfaceUrls.java b/src/test/java/api/support/http/InterfaceUrls.java index f85577d3a6..23c5878ecc 100644 --- a/src/test/java/api/support/http/InterfaceUrls.java +++ b/src/test/java/api/support/http/InterfaceUrls.java @@ -339,6 +339,6 @@ public static URL circulationSettingsUrl(String subPath) { } public static URL printEventsUrl(String subPath) { - return circulationModuleUrl("/circulation/print-events" + subPath); + return circulationModuleUrl("/circulation" + subPath); } } From 949689a85b9392e476052ecd4ecf3c91d41d7fc0 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Mon, 15 Jul 2024 16:17:25 +0530 Subject: [PATCH 11/16] CIRC-2099 added validations for print event flag and request --- descriptors/ModuleDescriptor-template.json | 6 +- .../resources/PrintEventsResource.java | 64 ++++++++++++++++ .../api/printEvents/PrintEventsTests.java | 76 ++++++++++++++++++- 3 files changed, 141 insertions(+), 5 deletions(-) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index d038e74136..3f260fbef6 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -745,10 +745,10 @@ "methods": ["POST"], "pathPattern": "/circulation/print-events-entry", "permissionsRequired": [ - "circulation.print-events.post" + "circulation.print-events-entry.item.post" ], "modulePermissions": [ - "circulation-storage.print-events.post" + "print-events-storage.print-events-entry.item.post" ] } ] @@ -1348,7 +1348,7 @@ ], "permissionSets": [ { - "permissionName": "circulation.print-events.post", + "permissionName": "circulation.print-events-entry.item.post", "displayName": "circulation - create print events", "description": "create print event logs" }, diff --git a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java index aa10754e8f..0975a1fd36 100644 --- a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java +++ b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java @@ -5,8 +5,12 @@ import io.vertx.ext.web.RoutingContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.folio.circulation.domain.CirculationSetting; +import org.folio.circulation.domain.MultipleRecords; import org.folio.circulation.domain.PrintEventRequest; +import org.folio.circulation.infrastructure.storage.CirculationSettingsRepository; import org.folio.circulation.infrastructure.storage.PrintEventsRepository; +import org.folio.circulation.infrastructure.storage.requests.RequestRepository; import org.folio.circulation.support.Clients; import org.folio.circulation.support.RouteRegistration; import org.folio.circulation.support.http.server.JsonHttpResponse; @@ -14,14 +18,24 @@ import org.folio.circulation.support.results.Result; import java.lang.invoke.MethodHandles; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import static org.folio.circulation.support.ValidationErrorFailure.singleValidationError; +import static org.folio.circulation.support.json.JsonPropertyFetcher.getProperty; import static org.folio.circulation.support.results.Result.ofAsync; import static org.folio.circulation.support.results.Result.succeeded; public class PrintEventsResource extends Resource { private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + private static final String PRINT_EVENT_FLAG_QUERY = "query=name=Enable print event log"; + private static final String PRINT_EVENT_FEATURE_DISABLED_ERROR = "print event feature is disabled for this tenant"; + private static final String NO_CONFIG_FOUND_ERROR = "No configuration found for print event feature"; + private static final String MULTIPLE_CONFIGS_ERROR = "Multiple configurations found for print event feature"; + private static final String PRINT_EVENT_FLAG_PROPERTY_NAME = "Enable Print Event"; public PrintEventsResource(HttpClient client) { super(client); @@ -37,6 +51,8 @@ void create(RoutingContext routingContext) { final var context = new WebContext(routingContext); final var clients = Clients.create(context, client); final var printEventsRepository = new PrintEventsRepository(clients); + final var circulationSettingsRepository = new CirculationSettingsRepository(clients); + final var requestRepository = new RequestRepository(clients); final var incomingRepresentation = routingContext.body().asJsonObject(); final var printEventRequest = PrintEventRequest.from(incomingRepresentation); @@ -44,6 +60,8 @@ void create(RoutingContext routingContext) { ofAsync(printEventRequest) .thenApply(refuseWhenPrintEventRequestIsInvalid()) + .thenCompose(r -> r.after(validatePrintEventFeatureFlag(circulationSettingsRepository))) + .thenCompose(r -> r.after(validateRequests(requestRepository))) .thenCompose(r -> r.after(printEventsRepository::create)) .thenApply(r -> r.map(response -> { assert printEventRequest != null; @@ -52,9 +70,55 @@ void create(RoutingContext routingContext) { .thenAccept(context::writeResultToHttpResponse); } + private static Function>> + validateRequests(RequestRepository requestRepository) { + return printRequest -> requestRepository.fetchRequests(printRequest.getRequestIds()) + .thenApply(printRequestList -> printRequestList.map(Collection::size)).thenApply(size -> { + if (size.value() != printRequest.getRequestIds().size()) { + return Result.failed(singleValidationError("invalid request found", "", "")); + } + return succeeded(printRequest); + }); + } + private static Function, Result> refuseWhenPrintEventRequestIsInvalid() { return r -> r.failWhen(printEventRequest -> succeeded(printEventRequest == null), circulationSetting -> singleValidationError("Print Event Request JSON is invalid", "", "")); } + + private static Function>> validatePrintEventFeatureFlag( + CirculationSettingsRepository circulationSettingsRepository) { + return printEventRequest -> circulationSettingsRepository.findBy(formatString(PRINT_EVENT_FLAG_QUERY)) + .thenApply(result -> handleCirculationSettingResult(result.map(MultipleRecords::getRecords), printEventRequest)); + } + + private static Result handleCirculationSettingResult(Result> result, + PrintEventRequest printEventRequest) { + + int size = result.value().size(); + if (size == 0) { + return Result.failed(singleValidationError(NO_CONFIG_FOUND_ERROR, "", "")); + } else if (size > 1) { + return Result.failed(singleValidationError(MULTIPLE_CONFIGS_ERROR, "", "")); + } + boolean isEnabled = result.value().stream() + .map(x -> Boolean.valueOf(getProperty(x.getValue(), PRINT_EVENT_FLAG_PROPERTY_NAME))).findFirst().orElse(true); + + if (!isEnabled) { + return Result.failed(singleValidationError(PRINT_EVENT_FEATURE_DISABLED_ERROR, "", "")); + } + return succeeded(printEventRequest); + } + + public static String formatString(String input) { + String[] parts = input.split("=", 2); + if (parts.length != 2) { + return input; + } + String encodedKey = URLEncoder.encode(parts[0], StandardCharsets.UTF_8); + String encodedValue = URLEncoder.encode(parts[1], StandardCharsets.UTF_8); + return encodedKey + "=" + encodedValue.replace("+", "%20"); + } + } diff --git a/src/test/java/api/printEvents/PrintEventsTests.java b/src/test/java/api/printEvents/PrintEventsTests.java index e2d48ea0a8..691b972266 100644 --- a/src/test/java/api/printEvents/PrintEventsTests.java +++ b/src/test/java/api/printEvents/PrintEventsTests.java @@ -1,11 +1,16 @@ package api.printEvents; import api.support.APITests; +import api.support.builders.CirculationSettingBuilder; +import api.support.builders.RequestBuilder; import io.vertx.core.json.JsonObject; import org.folio.circulation.support.http.client.Response; import org.junit.jupiter.api.Test; import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import static api.support.http.InterfaceUrls.printEventsUrl; import static api.support.matchers.ResponseStatusCodeMatcher.hasStatus; @@ -15,13 +20,68 @@ class PrintEventsTests extends APITests { + @Test void postPrintEventsTest() { + circulationSettingsClient.create(new CirculationSettingBuilder() + .withName("Enable print event log") + .withValue(new JsonObject().put("Enable Print Event", true))); JsonObject printRequest = getPrintEvent(); + printRequest.put("requestIds", createOneHundredRequests()); + System.out.println(printRequest.getString("requestIds")); Response response = restAssuredClient.post(printRequest, printEventsUrl("/print-events-entry"), "post-print-event"); assertThat(response, hasStatus(HTTP_CREATED)); } + @Test + void postPrintEventsWhenCirculationSettingIsNotPresentTest() { + JsonObject printRequest = getPrintEvent(); + printRequest.put("requestIds", List.of(UUID.randomUUID())); + Response response = restAssuredClient.post(printRequest, printEventsUrl("/print-events-entry"), "post-print-event"); + assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); + } + + @Test + void postPrintEventsWhenDuplicateCirculationSettingFound() { + circulationSettingsClient.create(new CirculationSettingBuilder() + .withName("Enable print event log") + .withValue(new JsonObject().put("Enable Print Event", true))); + circulationSettingsClient.create(new CirculationSettingBuilder() + .withName("Enable print event log") + .withValue(new JsonObject().put("Enable-Print-Event", false))); + + JsonObject printRequest = getPrintEvent(); + printRequest.put("requestIds", List.of(UUID.randomUUID())); + Response response = restAssuredClient.post(printRequest, printEventsUrl("/print-events-entry"), "post-print-event"); + assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); + } + + @Test + void postPrintEventsWhenPrintEventSettingIsDisable() { + circulationSettingsClient.create(new CirculationSettingBuilder() + .withName("Enable print event log") + .withValue(new JsonObject().put("Enable Print Event", false))); + + JsonObject printRequest = getPrintEvent(); + printRequest.put("requestIds", List.of(UUID.randomUUID())); + Response response = restAssuredClient.post(printRequest, printEventsUrl("/print-events-entry"), "post-print-event"); + assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); + } + + @Test + void postPrintEventsWithInvalidRequestId() { + circulationSettingsClient.create(new CirculationSettingBuilder() + .withName("Enable print event log") + .withValue(new JsonObject().put("Enable Print Event", true))); + JsonObject printRequest = getPrintEvent(); + List RequestIds = createOneHundredRequests(); + RequestIds.add(UUID.randomUUID()); + printRequest.put("requestIds", RequestIds); + Response response = restAssuredClient.post(printRequest, printEventsUrl("/print-events-entry"), "post-print-event"); + assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); + } + + @Test void postPrintEventsWithInvalidField() { JsonObject printRequest = getPrintEvent(); @@ -48,11 +108,23 @@ void postPrintEventsWithInvalidField_NullField() { } private JsonObject getPrintEvent() { - List requestIds = List.of("5f5751b4-e352-4121-adca-204b0c2aec43", "5f5751b4-e352-4121-adca-204b0c2aec44"); return new JsonObject() - .put("requestIds", requestIds) .put("requesterId", "5f5751b4-e352-4121-adca-204b0c2aec43") .put("requesterName", "requester") .put("printEventDate", "2024-06-25T14:30:00Z"); } + + private List createOneHundredRequests() { + final UUID pickupServicePointId = servicePointsFixture.cd1().getId(); + + return IntStream.range(0, 100).mapToObj(notUsed -> requestsFixture.place( + new RequestBuilder() + .open() + .page() + .forItem(itemsFixture.basedUponSmallAngryPlanet()) + .by(usersFixture.charlotte()) + .fulfillToHoldShelf() + .withPickupServicePointId(pickupServicePointId)).getId()) + .collect(Collectors.toList()); + } } From a11ae9945229ed93fdbaec49ca358a404286f135 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Tue, 16 Jul 2024 19:18:48 +0530 Subject: [PATCH 12/16] CIRC-2099 added loggers --- .../org/folio/circulation/domain/CirculationSetting.java | 1 + .../storage/CirculationSettingsRepository.java | 9 +++++++-- .../folio/circulation/resources/PrintEventsResource.java | 6 +++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/folio/circulation/domain/CirculationSetting.java b/src/main/java/org/folio/circulation/domain/CirculationSetting.java index 03f254f1ed..4f5c67342c 100644 --- a/src/main/java/org/folio/circulation/domain/CirculationSetting.java +++ b/src/main/java/org/folio/circulation/domain/CirculationSetting.java @@ -39,6 +39,7 @@ public class CirculationSetting { private final JsonObject value; public static CirculationSetting from(JsonObject representation) { + log.info("from {}", representation); final var id = getProperty(representation, ID_FIELD); final var name = getProperty(representation, NAME_FIELD); final var value = getObjectProperty(representation, VALUE_FIELD); diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/CirculationSettingsRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/CirculationSettingsRepository.java index 8125f1761b..809c71adb2 100644 --- a/src/main/java/org/folio/circulation/infrastructure/storage/CirculationSettingsRepository.java +++ b/src/main/java/org/folio/circulation/infrastructure/storage/CirculationSettingsRepository.java @@ -40,8 +40,13 @@ public CompletableFuture> getById(String id) { public CompletableFuture>> findBy(String query) { return circulationSettingsStorageClient.getManyWithRawQueryStringParameters(query) - .thenApply(flatMapResult(response -> - MultipleRecords.from(response, CirculationSetting::from, RECORDS_PROPERTY_NAME))); + .thenApply(flatMapResult(response -> { + log.info("************* {}", response); + log.info(MultipleRecords.from(response, CirculationSetting::from, RECORDS_PROPERTY_NAME).value().getRecords()); + return MultipleRecords.from(response, CirculationSetting::from, RECORDS_PROPERTY_NAME); + } + )); + } public CompletableFuture> create( diff --git a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java index 0975a1fd36..e4b78916ec 100644 --- a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java +++ b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java @@ -90,7 +90,11 @@ void create(RoutingContext routingContext) { private static Function>> validatePrintEventFeatureFlag( CirculationSettingsRepository circulationSettingsRepository) { return printEventRequest -> circulationSettingsRepository.findBy(formatString(PRINT_EVENT_FLAG_QUERY)) - .thenApply(result -> handleCirculationSettingResult(result.map(MultipleRecords::getRecords), printEventRequest)); + .thenApply(result -> { + log.info("validatePrintEventFeatureFlag:: result value {}", result.value()); + log.info("validatePrintEventFeatureFlag:: result value {}", result.value().getRecords()); + return handleCirculationSettingResult(result.map(MultipleRecords::getRecords), printEventRequest); + }); } private static Result handleCirculationSettingResult(Result> result, From 2e7c959e90c90de620056e10760c622617ca78fd Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Wed, 17 Jul 2024 14:03:24 +0530 Subject: [PATCH 13/16] CIRC-2099 added circulation setting module permission --- descriptors/ModuleDescriptor-template.json | 3 ++- src/test/java/api/printEvents/PrintEventsTests.java | 10 ++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 3f260fbef6..4223f70edf 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -748,7 +748,8 @@ "circulation.print-events-entry.item.post" ], "modulePermissions": [ - "print-events-storage.print-events-entry.item.post" + "print-events-storage.print-events-entry.item.post", + "circulation-storage.circulation-settings.collection.get" ] } ] diff --git a/src/test/java/api/printEvents/PrintEventsTests.java b/src/test/java/api/printEvents/PrintEventsTests.java index 691b972266..5ba3e6b859 100644 --- a/src/test/java/api/printEvents/PrintEventsTests.java +++ b/src/test/java/api/printEvents/PrintEventsTests.java @@ -9,7 +9,6 @@ import java.util.List; import java.util.UUID; -import java.util.stream.Collectors; import java.util.stream.IntStream; import static api.support.http.InterfaceUrls.printEventsUrl; @@ -74,9 +73,9 @@ void postPrintEventsWithInvalidRequestId() { .withName("Enable print event log") .withValue(new JsonObject().put("Enable Print Event", true))); JsonObject printRequest = getPrintEvent(); - List RequestIds = createOneHundredRequests(); - RequestIds.add(UUID.randomUUID()); - printRequest.put("requestIds", RequestIds); + List requestIds = createOneHundredRequests(); + requestIds.add(UUID.randomUUID()); + printRequest.put("requestIds", requestIds); Response response = restAssuredClient.post(printRequest, printEventsUrl("/print-events-entry"), "post-print-event"); assertThat(response, hasStatus(HTTP_UNPROCESSABLE_ENTITY)); } @@ -124,7 +123,6 @@ private List createOneHundredRequests() { .forItem(itemsFixture.basedUponSmallAngryPlanet()) .by(usersFixture.charlotte()) .fulfillToHoldShelf() - .withPickupServicePointId(pickupServicePointId)).getId()) - .collect(Collectors.toList()); + .withPickupServicePointId(pickupServicePointId)).getId()).toList(); } } From bf22a755ca1613440618846bf8b8e5e1600be0f0 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Thu, 18 Jul 2024 11:00:28 +0530 Subject: [PATCH 14/16] CIRC-2099 changed circulation setting name and flag name in request --- .../resources/PrintEventsResource.java | 24 +++---------- .../api/printEvents/PrintEventsTests.java | 35 ++++++++++--------- 2 files changed, 22 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java index e4b78916ec..57a69fd9cf 100644 --- a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java +++ b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java @@ -18,8 +18,6 @@ import org.folio.circulation.support.results.Result; import java.lang.invoke.MethodHandles; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.concurrent.CompletableFuture; import java.util.function.Function; @@ -31,11 +29,11 @@ public class PrintEventsResource extends Resource { private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); - private static final String PRINT_EVENT_FLAG_QUERY = "query=name=Enable print event log"; + private static final String PRINT_EVENT_FLAG_QUERY = "query=name=printEventLogFeature"; private static final String PRINT_EVENT_FEATURE_DISABLED_ERROR = "print event feature is disabled for this tenant"; private static final String NO_CONFIG_FOUND_ERROR = "No configuration found for print event feature"; private static final String MULTIPLE_CONFIGS_ERROR = "Multiple configurations found for print event feature"; - private static final String PRINT_EVENT_FLAG_PROPERTY_NAME = "Enable Print Event"; + private static final String PRINT_EVENT_FLAG_PROPERTY_NAME = "enablePrintLog"; public PrintEventsResource(HttpClient client) { super(client); @@ -63,10 +61,7 @@ void create(RoutingContext routingContext) { .thenCompose(r -> r.after(validatePrintEventFeatureFlag(circulationSettingsRepository))) .thenCompose(r -> r.after(validateRequests(requestRepository))) .thenCompose(r -> r.after(printEventsRepository::create)) - .thenApply(r -> r.map(response -> { - assert printEventRequest != null; - return JsonHttpResponse.created(printEventRequest.getRepresentation(), null); - })) + .thenApply(r -> r.map(response -> JsonHttpResponse.created(null, null))) .thenAccept(context::writeResultToHttpResponse); } @@ -89,9 +84,8 @@ void create(RoutingContext routingContext) { private static Function>> validatePrintEventFeatureFlag( CirculationSettingsRepository circulationSettingsRepository) { - return printEventRequest -> circulationSettingsRepository.findBy(formatString(PRINT_EVENT_FLAG_QUERY)) + return printEventRequest -> circulationSettingsRepository.findBy(PRINT_EVENT_FLAG_QUERY) .thenApply(result -> { - log.info("validatePrintEventFeatureFlag:: result value {}", result.value()); log.info("validatePrintEventFeatureFlag:: result value {}", result.value().getRecords()); return handleCirculationSettingResult(result.map(MultipleRecords::getRecords), printEventRequest); }); @@ -115,14 +109,4 @@ private static Result handleCirculationSettingResult(Result requestIds = createOneHundredRequests(); + List requestIds = new ArrayList<>(createOneHundredRequests()); requestIds.add(UUID.randomUUID()); printRequest.put("requestIds", requestIds); Response response = restAssuredClient.post(printRequest, printEventsUrl("/print-events-entry"), "post-print-event"); @@ -117,12 +118,12 @@ private List createOneHundredRequests() { final UUID pickupServicePointId = servicePointsFixture.cd1().getId(); return IntStream.range(0, 100).mapToObj(notUsed -> requestsFixture.place( - new RequestBuilder() - .open() - .page() - .forItem(itemsFixture.basedUponSmallAngryPlanet()) - .by(usersFixture.charlotte()) - .fulfillToHoldShelf() - .withPickupServicePointId(pickupServicePointId)).getId()).toList(); + new RequestBuilder() + .open() + .page() + .forItem(itemsFixture.basedUponSmallAngryPlanet()) + .by(usersFixture.charlotte()) + .fulfillToHoldShelf() + .withPickupServicePointId(pickupServicePointId)).getId()).toList(); } } From 391c507dab80941874bf5c09619a2c105d2490cd Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Fri, 26 Jul 2024 11:53:17 +0530 Subject: [PATCH 15/16] CIRC-2099 removed loggers --- .../org/folio/circulation/domain/CirculationSetting.java | 1 - .../storage/CirculationSettingsRepository.java | 9 ++------- .../folio/circulation/resources/PrintEventsResource.java | 7 +++---- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/folio/circulation/domain/CirculationSetting.java b/src/main/java/org/folio/circulation/domain/CirculationSetting.java index 4f5c67342c..03f254f1ed 100644 --- a/src/main/java/org/folio/circulation/domain/CirculationSetting.java +++ b/src/main/java/org/folio/circulation/domain/CirculationSetting.java @@ -39,7 +39,6 @@ public class CirculationSetting { private final JsonObject value; public static CirculationSetting from(JsonObject representation) { - log.info("from {}", representation); final var id = getProperty(representation, ID_FIELD); final var name = getProperty(representation, NAME_FIELD); final var value = getObjectProperty(representation, VALUE_FIELD); diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/CirculationSettingsRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/CirculationSettingsRepository.java index 809c71adb2..39f2ec1bff 100644 --- a/src/main/java/org/folio/circulation/infrastructure/storage/CirculationSettingsRepository.java +++ b/src/main/java/org/folio/circulation/infrastructure/storage/CirculationSettingsRepository.java @@ -40,13 +40,8 @@ public CompletableFuture> getById(String id) { public CompletableFuture>> findBy(String query) { return circulationSettingsStorageClient.getManyWithRawQueryStringParameters(query) - .thenApply(flatMapResult(response -> { - log.info("************* {}", response); - log.info(MultipleRecords.from(response, CirculationSetting::from, RECORDS_PROPERTY_NAME).value().getRecords()); - return MultipleRecords.from(response, CirculationSetting::from, RECORDS_PROPERTY_NAME); - } - )); - + .thenApply(flatMapResult(response -> + MultipleRecords.from(response, CirculationSetting::from, RECORDS_PROPERTY_NAME))); } public CompletableFuture> create( diff --git a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java index 57a69fd9cf..72ba78fcd1 100644 --- a/src/main/java/org/folio/circulation/resources/PrintEventsResource.java +++ b/src/main/java/org/folio/circulation/resources/PrintEventsResource.java @@ -85,10 +85,9 @@ void create(RoutingContext routingContext) { private static Function>> validatePrintEventFeatureFlag( CirculationSettingsRepository circulationSettingsRepository) { return printEventRequest -> circulationSettingsRepository.findBy(PRINT_EVENT_FLAG_QUERY) - .thenApply(result -> { - log.info("validatePrintEventFeatureFlag:: result value {}", result.value().getRecords()); - return handleCirculationSettingResult(result.map(MultipleRecords::getRecords), printEventRequest); - }); + .thenApply(result -> + handleCirculationSettingResult(result.map(MultipleRecords::getRecords), printEventRequest) + ); } private static Result handleCirculationSettingResult(Result> result, From c7d947e8f5037676964b4212856d5d9fe0347075 Mon Sep 17 00:00:00 2001 From: SreejaMangarapu Date: Fri, 26 Jul 2024 11:58:19 +0530 Subject: [PATCH 16/16] CIRC-2099 removed loggers --- .../infrastructure/storage/CirculationSettingsRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/CirculationSettingsRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/CirculationSettingsRepository.java index 39f2ec1bff..8125f1761b 100644 --- a/src/main/java/org/folio/circulation/infrastructure/storage/CirculationSettingsRepository.java +++ b/src/main/java/org/folio/circulation/infrastructure/storage/CirculationSettingsRepository.java @@ -41,7 +41,7 @@ public CompletableFuture> getById(String id) { public CompletableFuture>> findBy(String query) { return circulationSettingsStorageClient.getManyWithRawQueryStringParameters(query) .thenApply(flatMapResult(response -> - MultipleRecords.from(response, CirculationSetting::from, RECORDS_PROPERTY_NAME))); + MultipleRecords.from(response, CirculationSetting::from, RECORDS_PROPERTY_NAME))); } public CompletableFuture> create(