Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EDGEPATRON-131]-Added POST edge api for LOC patron #113

Merged
merged 12 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"requires": [
{
"id": "patron",
"version": "4.2 5.1"
"version": "4.2 5.2"
},
{
"id": "circulation",
Expand Down
53 changes: 52 additions & 1 deletion ramls/edge-patron.raml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -26,6 +27,56 @@ 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 created external patron
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
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
Expand Down
31 changes: 31 additions & 0 deletions ramls/examples/external_patron.json
Original file line number Diff line number Diff line change
@@ -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"]
}
135 changes: 135 additions & 0 deletions ramls/external_patron.json
Original file line number Diff line number Diff line change
@@ -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
}
1 change: 1 addition & 0 deletions src/main/java/org/folio/edge/patron/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/org/folio/edge/patron/MainVerticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
24 changes: 21 additions & 3 deletions src/main/java/org/folio/edge/patron/PatronHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,7 +33,9 @@
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;
import org.folio.edge.core.Handler;
Expand Down Expand Up @@ -151,6 +154,21 @@ public void handlePlaceItemHold(RoutingContext ctx) {
t -> handleProxyException(ctx, t)));
}

public void handlePatronRequest(RoutingContext ctx) {
if (ctx.body().asJsonObject() == null) {
badRequest(ctx, MSG_EXTERNAL_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) {
Expand Down Expand Up @@ -259,13 +277,13 @@ protected void handleProxyResponse(RoutingContext ctx, HttpResponse<Buffer> 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)){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the use of new condition here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Vignesh-kalyanasundaram :- just as precaution , There might be a case when we receive an empty or null response from mod-patron. in this case this code will not fail as NullPointer exception .

setContentType(serverResponse, contentType);
serverResponse.end(respBody); //not an error case, pass on the response body as received
}
Expand Down
Loading
Loading