From 1e30414329411f8936771040c5c3125470782533 Mon Sep 17 00:00:00 2001 From: Adesh_Singh Date: Wed, 5 Jun 2024 15:00:47 +0530 Subject: [PATCH 01/10] [EDGEPATRON-131]-Added POST edge api for LOC patron --- .../org/folio/edge/patron/MainVerticle.java | 3 + .../org/folio/edge/patron/PatronHandler.java | 15 ++ .../org/folio/edge/patron/model/Patron.java | 210 ++++++++++++++++++ .../edge/patron/utils/PatronOkapiClient.java | 11 + .../folio/edge/patron/MainVerticleTest.java | 26 ++- .../edge/patron/utils/PatronMockOkapi.java | 77 ++++++- 6 files changed, 333 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/folio/edge/patron/model/Patron.java diff --git a/src/main/java/org/folio/edge/patron/MainVerticle.java b/src/main/java/org/folio/edge/patron/MainVerticle.java index 9057a90..159d995 100644 --- a/src/main/java/org/folio/edge/patron/MainVerticle.java +++ b/src/main/java/org/folio/edge/patron/MainVerticle.java @@ -63,6 +63,9 @@ public Router defineRoutes() { router.route(HttpMethod.POST, "/patron/account/:patronId/item/:itemId/hold") .handler(patronHandler::handlePlaceItemHold); + router.route(HttpMethod.POST, "/patron/account/:patronId") + .handler(patronHandler::handlePatronRequest); + router.route(HttpMethod.POST, "/patron/account/:patronId/instance/:instanceId/hold") .handler(patronHandler::handlePlaceInstanceHold); diff --git a/src/main/java/org/folio/edge/patron/PatronHandler.java b/src/main/java/org/folio/edge/patron/PatronHandler.java index af3e4df..72684a4 100644 --- a/src/main/java/org/folio/edge/patron/PatronHandler.java +++ b/src/main/java/org/folio/edge/patron/PatronHandler.java @@ -151,6 +151,21 @@ public void handlePlaceItemHold(RoutingContext ctx) { t -> handleProxyException(ctx, t))); } + public void handlePatronRequest(RoutingContext ctx) { + if (ctx.body().asJsonObject() == null) { + badRequest(ctx, MSG_HOLD_NOBODY); + return; + } + final String body = String.valueOf(ctx.body().asJsonObject()); + handleCommon(ctx, + new String[] {}, + new String[] {}, + (client, params) -> ((PatronOkapiClient) client).postPatron( + body, + resp -> handleProxyResponse(ctx, resp), + t -> handleProxyException(ctx, t))); + } + public void handleCancelHold(RoutingContext ctx) { String validationResult = validateCancelHoldRequest(ctx.body().asJsonObject()); if ( validationResult != null) { diff --git a/src/main/java/org/folio/edge/patron/model/Patron.java b/src/main/java/org/folio/edge/patron/model/Patron.java new file mode 100644 index 0000000..b7f7fc3 --- /dev/null +++ b/src/main/java/org/folio/edge/patron/model/Patron.java @@ -0,0 +1,210 @@ +package org.folio.edge.patron.model; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; + +import org.folio.edge.core.utils.Mappers; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JacksonXmlRootElement(localName = "patron") +@JsonDeserialize(builder = Patron.Builder.class) +@JsonPropertyOrder({ + "generalInfo", + "address0", + "address1", + "contactInfo", + "preferredEmailCommunication" +}) +public final class Patron { + public final GeneralInfo generalInfo; + public final Address address0; + public final Address address1; + public final ContactInfo contactInfo; + public final List preferredEmailCommunication; + + private Patron(Builder builder) { + this.generalInfo = builder.generalInfo; + this.address0 = builder.address0; + this.address1 = builder.address1; + this.contactInfo = builder.contactInfo; + this.preferredEmailCommunication = builder.preferredEmailCommunication; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + @JsonProperty("generalInfo") + private GeneralInfo generalInfo; + + @JsonProperty("address0") + private Address address0; + + @JsonProperty("address1") + private Address address1; + + @JsonProperty("contactInfo") + private ContactInfo contactInfo; + + @JsonProperty("preferredEmailCommunication") + private List preferredEmailCommunication; + + public Builder generalInfo(GeneralInfo generalInfo) { + this.generalInfo = generalInfo; + return this; + } + + public Builder address0(Address address0) { + this.address0 = address0; + return this; + } + + public Builder address1(Address address1) { + this.address1 = address1; + return this; + } + + public Builder contactInfo(ContactInfo contactInfo) { + this.contactInfo = contactInfo; + return this; + } + + public Builder preferredEmailCommunication(List preferredEmailCommunication) { + this.preferredEmailCommunication = preferredEmailCommunication; + return this; + } + + public Patron build() { + return new Patron(this); + } + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonPropertyOrder({ + "externalSystemId", + "firstName", + "preferredFirstName", + "middleName", + "lastName" + }) + public static class GeneralInfo { + public final String externalSystemId; + public final String firstName; + public final String preferredFirstName; + public final String middleName; + public final String lastName; + + @JsonCreator + public GeneralInfo( + @JsonProperty("externalSystemId") String externalSystemId, + @JsonProperty("firstName") String firstName, + @JsonProperty("preferredFirstName") String preferredFirstName, + @JsonProperty("middleName") String middleName, + @JsonProperty("lastName") String lastName) { + this.externalSystemId = externalSystemId; + this.firstName = firstName; + this.preferredFirstName = preferredFirstName; + this.middleName = middleName; + this.lastName = lastName; + } + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonPropertyOrder({ + "addressLine0", + "addressLine1", + "city", + "province", + "zip", + "country" + }) + public static class Address { + public final String addressLine0; + public final String addressLine1; + public final String city; + public final String province; + public final String zip; + public final String country; + + @JsonCreator + public Address( + @JsonProperty("addressLine0") String addressLine0, + @JsonProperty("addressLine1") String addressLine1, + @JsonProperty("city") String city, + @JsonProperty("province") String province, + @JsonProperty("zip") String zip, + @JsonProperty("country") String country) { + this.addressLine0 = addressLine0; + this.addressLine1 = addressLine1; + this.city = city; + this.province = province; + this.zip = zip; + this.country = country; + } + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonPropertyOrder({ + "phone", + "mobilePhone", + "email" + }) + public static class ContactInfo { + public final String phone; + public final String mobilePhone; + public final String email; + + @JsonCreator + public ContactInfo( + @JsonProperty("phone") String phone, + @JsonProperty("mobilePhone") String mobilePhone, + @JsonProperty("email") String email) { + this.phone = phone; + this.mobilePhone = mobilePhone; + this.email = email; + } + } + + @Override + public int hashCode() { + return Objects.hash(generalInfo, address0, address1, contactInfo, preferredEmailCommunication); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Patron patron = (Patron) o; + return Objects.equals(generalInfo, patron.generalInfo) && + Objects.equals(address0, patron.address0) && + Objects.equals(address1, patron.address1) && + Objects.equals(contactInfo, patron.contactInfo) && + Objects.equals(preferredEmailCommunication, patron.preferredEmailCommunication); + } + + public String toXml() throws JsonProcessingException { + return Mappers.XML_PROLOG + Mappers.xmlMapper.writeValueAsString(this); + } + + public String toJson() throws JsonProcessingException { + return Mappers.jsonMapper.writeValueAsString(this); + } + + public static Patron fromJson(String json) throws IOException { + return Mappers.jsonMapper.readValue(json, Patron.class); + } + + public static Patron fromXml(String xml) throws IOException { + return Mappers.xmlMapper.readValue(xml, Patron.class); + } +} diff --git a/src/main/java/org/folio/edge/patron/utils/PatronOkapiClient.java b/src/main/java/org/folio/edge/patron/utils/PatronOkapiClient.java index 5840f6a..6174aae 100644 --- a/src/main/java/org/folio/edge/patron/utils/PatronOkapiClient.java +++ b/src/main/java/org/folio/edge/patron/utils/PatronOkapiClient.java @@ -119,6 +119,17 @@ public void placeItemHold(String patronId, String itemId, String requestBody, exceptionHandler); } + public void postPatron(String requestBody, + Handler> responseHandler, Handler exceptionHandler) { + post( + String.format("%s/patron/account", okapiURL), + tenant, + requestBody, + null, + responseHandler, + exceptionHandler); + } + public void cancelHold(String patronId, String holdId, JsonObject holdCancellationRequest, Handler> responseHandler, Handler exceptionHandler) { getRequest(holdId, diff --git a/src/test/java/org/folio/edge/patron/MainVerticleTest.java b/src/test/java/org/folio/edge/patron/MainVerticleTest.java index 15f1080..f5cc0f8 100644 --- a/src/test/java/org/folio/edge/patron/MainVerticleTest.java +++ b/src/test/java/org/folio/edge/patron/MainVerticleTest.java @@ -48,6 +48,7 @@ import org.folio.edge.core.utils.ApiKeyUtils; import org.folio.edge.core.utils.test.TestUtils; import org.folio.edge.patron.model.Account; +import org.folio.edge.patron.model.Patron; import org.folio.edge.patron.model.error.ErrorMessage; import org.folio.edge.patron.model.Hold; import org.folio.edge.patron.model.Loan; @@ -74,7 +75,6 @@ public class MainVerticleTest { private static final Logger logger = LogManager.getLogger(MainVerticleTest.class); - private static final String extPatronId = PatronMockOkapi.extPatronId; private static final String patronId = PatronMockOkapi.patronId; private static final String itemId = UUID.randomUUID().toString(); @@ -701,6 +701,30 @@ public void testPlaceInstanceHoldPatronNotFound(TestContext context) throws Exce assertEquals(expectedStatusCode, msg.httpStatusCode); } + @Test + public void test1(TestContext context) throws Exception { + logger.info("=== Test place instance hold w/ patron not found ==="); + + Patron hold = PatronMockOkapi.getPatron(); + int expectedStatusCode = 404; + + final Response resp = RestAssured + .with() + .body(hold.toJson()) + .contentType(APPLICATION_JSON) + .post( + String.format("/patron/account?apikey=%s", apiKey)) + .then() + .statusCode(expectedStatusCode) + .header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) + .extract() + .response(); + + ErrorMessage msg = ErrorMessage.fromJson(resp.body().asString()); + assertEquals("Unable to find patron " + PatronMockOkapi.extPatronId_notFound, msg.message); + assertEquals(expectedStatusCode, msg.httpStatusCode); + } + @Test public void testPlaceInstanceHoldInstanceNotFound(TestContext context) throws Exception { logger.info("=== Test place instance hold w/ instance not found ==="); diff --git a/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java b/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java index 5d5d89e..6474472 100644 --- a/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java +++ b/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java @@ -31,9 +31,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.folio.edge.core.utils.test.MockOkapi; -import org.folio.edge.patron.model.Account; -import org.folio.edge.patron.model.Charge; -import org.folio.edge.patron.model.Hold; +import org.folio.edge.patron.model.*; import org.folio.edge.patron.model.Hold.Status; import com.fasterxml.jackson.core.JsonProcessingException; @@ -44,10 +42,6 @@ import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; -import org.folio.edge.patron.model.HoldCancellation; -import org.folio.edge.patron.model.Item; -import org.folio.edge.patron.model.Loan; -import org.folio.edge.patron.model.Money; public class PatronMockOkapi extends MockOkapi { @@ -131,7 +125,7 @@ public Router defineRoutes() { router.route(HttpMethod.GET, "/users") .handler(this::getPatronHandler); - router.route(HttpMethod.GET, "/patron/account/:patronId") + router.route(HttpMethod.POST, "/patron/account") .handler(this::getAccountHandler); router.route(HttpMethod.POST, "/patron/account/:patronId/item/:itemId/renew") @@ -140,6 +134,9 @@ public Router defineRoutes() { router.route(HttpMethod.POST, "/patron/account/:patronId/item/:itemId/hold") .handler(this::placeItemHoldHandler); + router.route(HttpMethod.POST, "/patron/account/:patronId") + .handler(this::postP1); + router.route(HttpMethod.POST, "/patron/account/:patronId/instance/:instanceId/hold") .handler(this::placeInstanceHoldHandler); @@ -324,6 +321,49 @@ public void placeItemHoldHandler(RoutingContext ctx) { } } + public void postP1(RoutingContext ctx) { + String patronId = ctx.request().getParam(PARAM_PATRON_ID); + String token = ctx.request().getHeader(X_OKAPI_TOKEN); + + String body = ctx.getBodyAsString(); + + Patron hold; + try { + hold = Patron.fromJson(body); + } catch (IOException e) { + logger.error("Exception parsing request payload", e); + ctx.response() + .setStatusCode(400) + .putHeader(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN) + .end("Bad Request"); + return; + } + + if (token == null || !token.equals(MOCK_TOKEN)) { + ctx.response() + .setStatusCode(403) + .putHeader(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN) + .end("Access requires permission: patron.item.post"); + } else if (patronId.equals(patronId_notFound)) { + // Magic patronId signifying we want to mock a "not found" + // response. + ctx.response() + .setStatusCode(404) + .putHeader(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN) + .end(patronId + " not found"); + } else if (hold == null) { + ctx.response() + .setStatusCode(400) + .putHeader(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN) + .end("Bad Request"); + } else { + ctx.response() + .setStatusCode(201) + .putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) + .end(getPJson(hold)); + } + } + public void getRequestHandler(RoutingContext ctx) { String requestId = ctx.request().getParam(PARAM_REQUEST_ID); String token = ctx.request().getHeader(X_OKAPI_TOKEN); @@ -597,6 +637,16 @@ public static Hold getHold(String itemId, Date holdReqDate) { .build(); } + public static Patron getPatron() { + return Patron.builder() + .address0(new Patron.Address("fdsf","sds", "fsd", "dasd", "123", "sdsd")) + .address1(new Patron.Address("fdsf","sds", "fsd", "dasd", "123", "sdsd")) + .contactInfo(new Patron.ContactInfo("342424","232321","fgh@mail")) + .generalInfo(new Patron.GeneralInfo("1234","sds","a","s", "45")) + .preferredEmailCommunication(new ArrayList<>()) + .build(); + } + public static Charge getCharge(String itemId) { return Charge.builder() .item(getItem(itemId_overdue)) @@ -643,6 +693,17 @@ public static String getPlacedHoldJson(Hold hold) { return ret; } + public static String getPJson(Patron hold) { + + String ret = null; + try { + ret = hold.toJson(); + } catch (JsonProcessingException e) { + logger.warn("Failed to generate Hold JSON", e); + } + return ret; + } + public static String getRemovedHoldJson(String holdReqId) { String ret = null; try { From 06eaaccb58a7d793e1733123726666516491909b Mon Sep 17 00:00:00 2001 From: Adesh_Singh Date: Thu, 6 Jun 2024 11:55:12 +0530 Subject: [PATCH 02/10] [EDGEPATRON-131]-Added POST edge api for LOC patron --- ramls/edge-patron.raml | 56 ++++++++ ramls/examples/external_patron.json | 31 ++++ ramls/external_patron.json | 135 ++++++++++++++++++ .../folio/edge/patron/MainVerticleTest.java | 10 +- .../edge/patron/utils/PatronMockOkapi.java | 34 +---- 5 files changed, 232 insertions(+), 34 deletions(-) create mode 100644 ramls/examples/external_patron.json create mode 100644 ramls/external_patron.json diff --git a/ramls/edge-patron.raml b/ramls/edge-patron.raml index 9304de5..1e99ae8 100644 --- a/ramls/edge-patron.raml +++ b/ramls/edge-patron.raml @@ -17,6 +17,7 @@ types: charge: !include charge.json money: !include money.json item: !include item.json + external_patron: !include external_patron.json allowedServicePoints: !include allowed-service-points-response.json hold-cancellation: !include hold-cancellation.json errors: !include raml-util/schemas/errors.schema @@ -26,6 +27,61 @@ types: description: | Services that allow patron empowerment from 3rd party discovery services /account: + post: + description: | + Creates external patron request + queryParameters: + apikey: + description: "API Key" + type: string + body: + application/json: + type: external_patron + example: !include examples/external_patron.json + responses: + 201: + description: | + Returns data for a new hold request on the selected item + body: + application/json: + type: external_patron + example: !include examples/external_patron.json + 400: + description: Bad request + body: + text/plain: + example: unable to process request + 401: + description: Not authorized to perform requested action + body: + text/plain: + example: unable to create request + 404: + description: Instance with a given ID not found + body: + text/plain: + example: item not found + 403: + description: Access Denied + body: + text/plain: + example: Access Denied + 409: + description: Conflict + body: + text/plain: + example: Optimistic Locking Conflict + 422: + description: Validation error + body: + text/plain: + example: Validation error + 500: + description: | + Internal server error, e.g. due to misconfiguration + body: + text/plain: + example: internal server error, contact administrator /{id}: displayName: Manage Accounts By Id description: Service endpoints that manage accounts by an existing Id diff --git a/ramls/examples/external_patron.json b/ramls/examples/external_patron.json new file mode 100644 index 0000000..fbf90b8 --- /dev/null +++ b/ramls/examples/external_patron.json @@ -0,0 +1,31 @@ +{ + "generalInfo": { + "externalSystemId": "ext-123456", + "firstName": "John", + "preferredFirstName": "Johnny", + "middleName": "M", + "lastName": "Doe" + }, + "address0": { + "addressLine0": "123 Main St", + "addressLine1": "Apt 4B", + "city": "Metropolis", + "province": "NY", + "zip": "12345", + "country": "USA" + }, + "address1": { + "addressLine0": "456 Side St", + "addressLine1": "Suite 500", + "city": "Metropolis", + "province": "NY", + "zip": "12346", + "country": "USA" + }, + "contactInfo": { + "phone": "555-1234", + "mobilePhone": "555-5678", + "email": "john.doe@example.com" + }, + "preferredEmailCommunication": ["Support", "Programs"] +} diff --git a/ramls/external_patron.json b/ramls/external_patron.json new file mode 100644 index 0000000..0c023b5 --- /dev/null +++ b/ramls/external_patron.json @@ -0,0 +1,135 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "User Information Schema", + "description": "Schema for user information including general info, addresses, contact info", + "type": "object", + "properties": { + "generalInfo": { + "type": "object", + "description": "General info of patron", + "properties": { + "externalSystemId": { + "type": "string", + "description": "A unique ID that corresponds to an external authority" + }, + "firstName": { + "type": "string", + "description": "The user's given name" + }, + "preferredFirstName": { + "type": "string", + "description": "The user's preferred name" + }, + "middleName": { + "type": "string", + "description": "The user's middle name (if any)" + }, + "lastName": { + "type": "string", + "description": "The user's surname" + } + }, + "required": ["externalSystemId", "firstName", "lastName"], + "additionalProperties": false + }, + "address0": { + "type": "object", + "description": "Primary address info of patron", + "properties": { + "addressLine0": { + "type": "string", + "description": "Address, Line 0" + }, + "addressLine1": { + "type": "string", + "description": "Address, Line 1" + }, + "city": { + "type": "string", + "description": "City name" + }, + "province": { + "type": "string", + "description": "Province" + }, + "zip": { + "type": "string", + "description": "Zip Code" + }, + "country": { + "type": "string", + "description": "Country" + } + }, + "required": ["addressLine0", "city", "province", "zip", "country"], + "additionalProperties": false + }, + "address1": { + "type": "object", + "description": "Secondary address info of patron", + "properties": { + "addressLine0": { + "type": "string", + "description": "Address, Line 0" + }, + "addressLine1": { + "type": "string", + "description": "Address, Line 1" + }, + "city": { + "type": "string", + "description": "City name" + }, + "province": { + "type": "string", + "description": "Province" + }, + "zip": { + "type": "string", + "description": "Zip Code" + }, + "country": { + "type": "string", + "description": "Country" + } + }, + "required": ["addressLine0", "city", "province", "zip", "country"], + "additionalProperties": false + }, + "contactInfo": { + "type": "object", + "description": "Contact info of patron", + "properties": { + "phone": { + "type": "string", + "description": "The user's primary phone number" + }, + "mobilePhone": { + "type": "string", + "description": "The user's mobile phone number" + }, + "email": { + "type": "string", + "description": "The user's email address", + "format": "email" + } + }, + "required": ["email"], + "additionalProperties": false + }, + "preferredEmailCommunication": { + "type": "array", + "description": "Email communication info of patron", + "items": { + "type": "string", + "enum": ["Support", "Programs", "Service"] + }, + "minItems": 1, + "maxItems": 3, + "uniqueItems": true, + "description": "Preferred email communication types" + } + }, + "required": ["generalInfo", "address0", "contactInfo", "preferredEmailCommunication"], + "additionalProperties": false +} diff --git a/src/test/java/org/folio/edge/patron/MainVerticleTest.java b/src/test/java/org/folio/edge/patron/MainVerticleTest.java index f5cc0f8..152cf6c 100644 --- a/src/test/java/org/folio/edge/patron/MainVerticleTest.java +++ b/src/test/java/org/folio/edge/patron/MainVerticleTest.java @@ -82,7 +82,7 @@ public class MainVerticleTest { private static final String holdId = UUID.randomUUID().toString(); private static final String apiKey = ApiKeyUtils.generateApiKey(10, "diku", "diku"); private static final String badApiKey = apiKey + "0000"; - private static final String unknownTenantApiKey = ApiKeyUtils.generateApiKey(10, "bogus", "diku");; + private static final String unknownTenantApiKey = ApiKeyUtils.generateApiKey(10, "bogus", "diku"); private static final long requestTimeoutMs = 3000L; @@ -706,23 +706,19 @@ public void test1(TestContext context) throws Exception { logger.info("=== Test place instance hold w/ patron not found ==="); Patron hold = PatronMockOkapi.getPatron(); - int expectedStatusCode = 404; + int expectedStatusCode = 201; final Response resp = RestAssured .with() .body(hold.toJson()) .contentType(APPLICATION_JSON) .post( - String.format("/patron/account?apikey=%s", apiKey)) + String.format("/patron/account/%s?apikey=%s", UUID.randomUUID(), apiKey)) .then() .statusCode(expectedStatusCode) .header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) .extract() .response(); - - ErrorMessage msg = ErrorMessage.fromJson(resp.body().asString()); - assertEquals("Unable to find patron " + PatronMockOkapi.extPatronId_notFound, msg.message); - assertEquals(expectedStatusCode, msg.httpStatusCode); } @Test diff --git a/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java b/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java index 6474472..c05f5c7 100644 --- a/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java +++ b/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java @@ -125,7 +125,7 @@ public Router defineRoutes() { router.route(HttpMethod.GET, "/users") .handler(this::getPatronHandler); - router.route(HttpMethod.POST, "/patron/account") + router.route(HttpMethod.GET, "/patron/account") .handler(this::getAccountHandler); router.route(HttpMethod.POST, "/patron/account/:patronId/item/:itemId/renew") @@ -134,7 +134,7 @@ public Router defineRoutes() { router.route(HttpMethod.POST, "/patron/account/:patronId/item/:itemId/hold") .handler(this::placeItemHoldHandler); - router.route(HttpMethod.POST, "/patron/account/:patronId") + router.route(HttpMethod.POST, "/patron/account") .handler(this::postP1); router.route(HttpMethod.POST, "/patron/account/:patronId/instance/:instanceId/hold") @@ -322,7 +322,7 @@ public void placeItemHoldHandler(RoutingContext ctx) { } public void postP1(RoutingContext ctx) { - String patronId = ctx.request().getParam(PARAM_PATRON_ID); + logger.info("Inside postp1"); String token = ctx.request().getHeader(X_OKAPI_TOKEN); String body = ctx.getBodyAsString(); @@ -330,6 +330,10 @@ public void postP1(RoutingContext ctx) { Patron hold; try { hold = Patron.fromJson(body); + ctx.response() + .setStatusCode(201) + .putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) + .end("hiiiiiiiiiii"); } catch (IOException e) { logger.error("Exception parsing request payload", e); ctx.response() @@ -338,30 +342,6 @@ public void postP1(RoutingContext ctx) { .end("Bad Request"); return; } - - if (token == null || !token.equals(MOCK_TOKEN)) { - ctx.response() - .setStatusCode(403) - .putHeader(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN) - .end("Access requires permission: patron.item.post"); - } else if (patronId.equals(patronId_notFound)) { - // Magic patronId signifying we want to mock a "not found" - // response. - ctx.response() - .setStatusCode(404) - .putHeader(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN) - .end(patronId + " not found"); - } else if (hold == null) { - ctx.response() - .setStatusCode(400) - .putHeader(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN) - .end("Bad Request"); - } else { - ctx.response() - .setStatusCode(201) - .putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) - .end(getPJson(hold)); - } } public void getRequestHandler(RoutingContext ctx) { From bd540eb27894c36df78b6d9bc489fc7beb6aeece Mon Sep 17 00:00:00 2001 From: Adesh_Singh Date: Thu, 6 Jun 2024 12:18:46 +0530 Subject: [PATCH 03/10] [EDGEPATRON-131]-Added POST edge api for LOC patron --- src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java b/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java index c05f5c7..3b7eab8 100644 --- a/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java +++ b/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java @@ -125,7 +125,7 @@ public Router defineRoutes() { router.route(HttpMethod.GET, "/users") .handler(this::getPatronHandler); - router.route(HttpMethod.GET, "/patron/account") + router.route(HttpMethod.GET, "/patron/account/:patronId") .handler(this::getAccountHandler); router.route(HttpMethod.POST, "/patron/account/:patronId/item/:itemId/renew") From 97badb6fa869c78fa1322178d3e4a463f812e0be Mon Sep 17 00:00:00 2001 From: Adesh_Singh Date: Thu, 6 Jun 2024 12:36:14 +0530 Subject: [PATCH 04/10] [EDGEPATRON-131]-Added POST edge api for LOC patron --- .../org/folio/edge/patron/model/Patron.java | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/src/main/java/org/folio/edge/patron/model/Patron.java b/src/main/java/org/folio/edge/patron/model/Patron.java index b7f7fc3..c058f40 100644 --- a/src/main/java/org/folio/edge/patron/model/Patron.java +++ b/src/main/java/org/folio/edge/patron/model/Patron.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.util.List; -import java.util.Objects; import org.folio.edge.core.utils.Mappers; @@ -175,27 +174,6 @@ public ContactInfo( } } - @Override - public int hashCode() { - return Objects.hash(generalInfo, address0, address1, contactInfo, preferredEmailCommunication); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Patron patron = (Patron) o; - return Objects.equals(generalInfo, patron.generalInfo) && - Objects.equals(address0, patron.address0) && - Objects.equals(address1, patron.address1) && - Objects.equals(contactInfo, patron.contactInfo) && - Objects.equals(preferredEmailCommunication, patron.preferredEmailCommunication); - } - - public String toXml() throws JsonProcessingException { - return Mappers.XML_PROLOG + Mappers.xmlMapper.writeValueAsString(this); - } - public String toJson() throws JsonProcessingException { return Mappers.jsonMapper.writeValueAsString(this); } @@ -203,8 +181,4 @@ public String toJson() throws JsonProcessingException { public static Patron fromJson(String json) throws IOException { return Mappers.jsonMapper.readValue(json, Patron.class); } - - public static Patron fromXml(String xml) throws IOException { - return Mappers.xmlMapper.readValue(xml, Patron.class); - } } From c888c1123ffc7d17bcbdd793f95d0d2092aaa0f0 Mon Sep 17 00:00:00 2001 From: Adesh_Singh Date: Thu, 6 Jun 2024 12:43:18 +0530 Subject: [PATCH 05/10] [EDGEPATRON-131]-Added POST edge api for LOC patron --- descriptors/ModuleDescriptor-template.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index c54d6ac..20de480 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -5,7 +5,7 @@ "requires": [ { "id": "patron", - "version": "4.2 5.1" + "version": "4.2 5.2" }, { "id": "circulation", From edb3dafc4e26dc4bc1fc8938d9dce981e12562fb Mon Sep 17 00:00:00 2001 From: Adesh_Singh Date: Thu, 6 Jun 2024 12:51:51 +0530 Subject: [PATCH 06/10] [EDGEPATRON-131]-Added POST edge api for LOC patron --- .../org/folio/edge/patron/MainVerticleTest.java | 11 +++++------ .../edge/patron/utils/PatronMockOkapi.java | 17 ++++------------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/test/java/org/folio/edge/patron/MainVerticleTest.java b/src/test/java/org/folio/edge/patron/MainVerticleTest.java index 152cf6c..b96ac9f 100644 --- a/src/test/java/org/folio/edge/patron/MainVerticleTest.java +++ b/src/test/java/org/folio/edge/patron/MainVerticleTest.java @@ -702,15 +702,14 @@ public void testPlaceInstanceHoldPatronNotFound(TestContext context) throws Exce } @Test - public void test1(TestContext context) throws Exception { - logger.info("=== Test place instance hold w/ patron not found ==="); + public void testPostExternalPatron(TestContext context) throws Exception { + logger.info("=== Test post external patron ==="); - Patron hold = PatronMockOkapi.getPatron(); + Patron patron = PatronMockOkapi.getPatron(); int expectedStatusCode = 201; - - final Response resp = RestAssured + RestAssured .with() - .body(hold.toJson()) + .body(patron.toJson()) .contentType(APPLICATION_JSON) .post( String.format("/patron/account/%s?apikey=%s", UUID.randomUUID(), apiKey)) diff --git a/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java b/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java index 3b7eab8..2d5b169 100644 --- a/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java +++ b/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java @@ -135,7 +135,7 @@ public Router defineRoutes() { .handler(this::placeItemHoldHandler); router.route(HttpMethod.POST, "/patron/account") - .handler(this::postP1); + .handler(this::postPatronMock); router.route(HttpMethod.POST, "/patron/account/:patronId/instance/:instanceId/hold") .handler(this::placeInstanceHoldHandler); @@ -321,33 +321,24 @@ public void placeItemHoldHandler(RoutingContext ctx) { } } - public void postP1(RoutingContext ctx) { - logger.info("Inside postp1"); - String token = ctx.request().getHeader(X_OKAPI_TOKEN); - - String body = ctx.getBodyAsString(); - - Patron hold; + public void postPatronMock(RoutingContext ctx) { try { - hold = Patron.fromJson(body); ctx.response() .setStatusCode(201) .putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON) - .end("hiiiiiiiiiii"); - } catch (IOException e) { + .end(); + } catch (Exception e) { logger.error("Exception parsing request payload", e); ctx.response() .setStatusCode(400) .putHeader(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN) .end("Bad Request"); - return; } } public void getRequestHandler(RoutingContext ctx) { String requestId = ctx.request().getParam(PARAM_REQUEST_ID); String token = ctx.request().getHeader(X_OKAPI_TOKEN); - if (token == null || !token.equals(MOCK_TOKEN)) { ctx.response() .setStatusCode(403) From 289074d03d763a905a0b279cc94be2a4b5d7fcf9 Mon Sep 17 00:00:00 2001 From: Adesh_Singh Date: Thu, 6 Jun 2024 20:23:43 +0530 Subject: [PATCH 07/10] [EDGEPATRON-131]-Added POST edge api for LOC patron --- src/test/java/org/folio/edge/patron/MainVerticleTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/folio/edge/patron/MainVerticleTest.java b/src/test/java/org/folio/edge/patron/MainVerticleTest.java index b96ac9f..79a4032 100644 --- a/src/test/java/org/folio/edge/patron/MainVerticleTest.java +++ b/src/test/java/org/folio/edge/patron/MainVerticleTest.java @@ -702,7 +702,7 @@ public void testPlaceInstanceHoldPatronNotFound(TestContext context) throws Exce } @Test - public void testPostExternalPatron(TestContext context) throws Exception { + public void testPostExternalLCPatron(TestContext context) throws Exception { logger.info("=== Test post external patron ==="); Patron patron = PatronMockOkapi.getPatron(); From 33ab6b71fa77c31311b985017d3bb25a59d5fe5c Mon Sep 17 00:00:00 2001 From: Adesh_Singh Date: Thu, 6 Jun 2024 21:09:42 +0530 Subject: [PATCH 08/10] [EDGEPATRON-131]-Added POST edge api for LOC patron --- .../java/org/folio/edge/patron/PatronHandler.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/folio/edge/patron/PatronHandler.java b/src/main/java/org/folio/edge/patron/PatronHandler.java index 254daa7..08a6806 100644 --- a/src/main/java/org/folio/edge/patron/PatronHandler.java +++ b/src/main/java/org/folio/edge/patron/PatronHandler.java @@ -28,11 +28,8 @@ import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.client.HttpResponse; import java.text.SimpleDateFormat; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.TimeZone; +import java.util.*; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.folio.edge.core.Handler; @@ -274,13 +271,13 @@ protected void handleProxyResponse(RoutingContext ctx, HttpResponse resp serverResponse.setStatusCode(statusCode); String respBody = resp.bodyAsString(); - if (logger.isDebugEnabled()) { + if (logger.isDebugEnabled() ) { logger.debug("response: " + respBody); } String contentType = resp.getHeader(HttpHeaders.CONTENT_TYPE.toString()); - if (resp.statusCode() < 400){ + if (resp.statusCode() < 400 && Objects.nonNull(respBody)){ setContentType(serverResponse, contentType); serverResponse.end(respBody); //not an error case, pass on the response body as received } From 681e5295332838923d16ba2bd6342914191bf30b Mon Sep 17 00:00:00 2001 From: Adesh_Singh Date: Mon, 10 Jun 2024 14:54:19 +0530 Subject: [PATCH 09/10] [EDGEPATRON-131]-Added POST edge api for LOC patron --- ramls/edge-patron.raml | 9 ++------- src/main/java/org/folio/edge/patron/PatronHandler.java | 7 ++++++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ramls/edge-patron.raml b/ramls/edge-patron.raml index 1e99ae8..ba860e7 100644 --- a/ramls/edge-patron.raml +++ b/ramls/edge-patron.raml @@ -2,7 +2,7 @@ title: Patron Services baseUri: https://github.com/folio-org/mod-patron protocols: [ HTTPS ] -version: v4.3 +version: v4.4 documentation: - title: Patron Services @@ -41,7 +41,7 @@ types: responses: 201: description: | - Returns data for a new hold request on the selected item + Returns data for a new created external patron body: application/json: type: external_patron @@ -56,11 +56,6 @@ types: body: text/plain: example: unable to create request - 404: - description: Instance with a given ID not found - body: - text/plain: - example: item not found 403: description: Access Denied body: diff --git a/src/main/java/org/folio/edge/patron/PatronHandler.java b/src/main/java/org/folio/edge/patron/PatronHandler.java index 08a6806..c74b4b0 100644 --- a/src/main/java/org/folio/edge/patron/PatronHandler.java +++ b/src/main/java/org/folio/edge/patron/PatronHandler.java @@ -28,7 +28,12 @@ import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.client.HttpResponse; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TimeZone; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; From 479bbfe9cce251aa86a0acadfb5a0b457393926c Mon Sep 17 00:00:00 2001 From: Adesh_Singh Date: Mon, 10 Jun 2024 15:24:16 +0530 Subject: [PATCH 10/10] [EDGEPATRON-131]-Added POST edge api for LOC patron --- src/main/java/org/folio/edge/patron/Constants.java | 1 + src/main/java/org/folio/edge/patron/PatronHandler.java | 5 +++-- .../org/folio/edge/patron/utils/PatronMockOkapi.java | 9 ++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/folio/edge/patron/Constants.java b/src/main/java/org/folio/edge/patron/Constants.java index b565fd4..7009965 100644 --- a/src/main/java/org/folio/edge/patron/Constants.java +++ b/src/main/java/org/folio/edge/patron/Constants.java @@ -29,6 +29,7 @@ public class Constants { public static final String MSG_INTERNAL_SERVER_ERROR = "Internal Server Error"; public static final String MSG_REQUEST_TIMEOUT = "Request to FOLIO timed out"; public static final String MSG_HOLD_NOBODY = "No hold data provided"; + public static final String MSG_EXTERNAL_NOBODY = "No external_patron data provided"; public static final String FIELD_EXPIRATION_DATE = "expirationDate"; public static final String FIELD_REQUEST_DATE = "requestDate"; diff --git a/src/main/java/org/folio/edge/patron/PatronHandler.java b/src/main/java/org/folio/edge/patron/PatronHandler.java index c74b4b0..cdaa019 100644 --- a/src/main/java/org/folio/edge/patron/PatronHandler.java +++ b/src/main/java/org/folio/edge/patron/PatronHandler.java @@ -4,9 +4,10 @@ import static org.folio.edge.patron.Constants.FIELD_EXPIRATION_DATE; import static org.folio.edge.patron.Constants.FIELD_REQUEST_DATE; import static org.folio.edge.patron.Constants.MSG_ACCESS_DENIED; +import static org.folio.edge.patron.Constants.MSG_EXTERNAL_NOBODY; +import static org.folio.edge.patron.Constants.MSG_HOLD_NOBODY; import static org.folio.edge.patron.Constants.MSG_INTERNAL_SERVER_ERROR; import static org.folio.edge.patron.Constants.MSG_REQUEST_TIMEOUT; -import static org.folio.edge.patron.Constants.MSG_HOLD_NOBODY; import static org.folio.edge.patron.Constants.PARAM_HOLD_ID; import static org.folio.edge.patron.Constants.PARAM_INCLUDE_CHARGES; import static org.folio.edge.patron.Constants.PARAM_INCLUDE_HOLDS; @@ -155,7 +156,7 @@ public void handlePlaceItemHold(RoutingContext ctx) { public void handlePatronRequest(RoutingContext ctx) { if (ctx.body().asJsonObject() == null) { - badRequest(ctx, MSG_HOLD_NOBODY); + badRequest(ctx, MSG_EXTERNAL_NOBODY); return; } final String body = String.valueOf(ctx.body().asJsonObject()); diff --git a/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java b/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java index 2d5b169..08d2305 100644 --- a/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java +++ b/src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java @@ -31,7 +31,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.folio.edge.core.utils.test.MockOkapi; -import org.folio.edge.patron.model.*; +import org.folio.edge.patron.model.Account; +import org.folio.edge.patron.model.Charge; +import org.folio.edge.patron.model.Hold; import org.folio.edge.patron.model.Hold.Status; import com.fasterxml.jackson.core.JsonProcessingException; @@ -42,6 +44,11 @@ import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; +import org.folio.edge.patron.model.HoldCancellation; +import org.folio.edge.patron.model.Item; +import org.folio.edge.patron.model.Loan; +import org.folio.edge.patron.model.Money; +import org.folio.edge.patron.model.Patron; public class PatronMockOkapi extends MockOkapi {