Skip to content

Commit

Permalink
EDGPATRON-149 Adding new GET endpoint to fetch patron details based o…
Browse files Browse the repository at this point in the history
…n emailId (#132)

* EDGPATRON-149 Adding new endpoint to fetch user details based on emailId

* EDGPATRON-149 Formatting error response

* EDGPATRON-149 changing emailId from path param to query param and formatting error response

* EDGPATRON-149 Fix sonar issues and remove unused imports

* EDGPATRON-149 Fix contracts
  • Loading branch information
Vignesh-kalyanasundaram authored Oct 17, 2024
1 parent d6ff358 commit 7659857
Show file tree
Hide file tree
Showing 13 changed files with 361 additions and 101 deletions.
91 changes: 45 additions & 46 deletions ramls/edge-patron.raml
Original file line number Diff line number Diff line change
Expand Up @@ -622,50 +622,49 @@ types:
body:
text/plain:
example: internal server error, contact administrator
/patron-registration-status/{emailId}:
uriParameters:
emailId:
description: The email ID of the patron.
type: string
required: true
get:
description: Get the patron details by email ID
queryParameters:
apikey:
description: "API Key"
type: string
responses:
200:
description: patron information retrieved successfully
body:
application/json:
type: user
example: !include examples/user.json
400:
description: Validation error
body:
application/json:
type: user_error_400
example: !include examples/user_error.json
401:
description: Not authorized to perform requested action
body:
text/plain:
example: unable to get account -- unauthorized
403:
description: Access Denied
body:
text/plain:
example: Access Denied
404:
description: Validation error
body:
application/json:
type: user_error_404
example: !include examples/user_error.json
500:
description: Internal server error, e.g. due to misconfiguration
body:
text/plain:
example: internal server error, contact administrator
/registration-status/{emailId}:
get:
description: Get the patron details by email ID
queryParameters:
apikey:
description: "API Key"
type: string
emailId:
description: The email ID of the patron.
type: string
required: true
responses:
200:
description: patron information retrieved successfully
body:
application/json:
type: user
example: !include examples/user.json
400:
description: Validation error
body:
application/json:
type: user_error_400
example: !include examples/user_error.json
401:
description: Not authorized to perform requested action
body:
text/plain:
example: unable to get account -- unauthorized
403:
description: Access Denied
body:
text/plain:
example: Access Denied
404:
description: Validation error
body:
application/json:
type: user_error_404
example: !include examples/user_error.json
500:
description: Internal server error, e.g. due to misconfiguration
body:
text/plain:
example: internal server error, contact administrator

4 changes: 2 additions & 2 deletions ramls/examples/user_error.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"code": 404,
"errorMessage": "USER_ACCOUNT_INACTIVE"
"code": "USER_ACCOUNT_INACTIVE",
"errorMessage": "User account is not active"
}
20 changes: 11 additions & 9 deletions ramls/schemas/user_error_400.schema
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "external_patron_error.schema",
"description": "An external_patron user error",
"id": "user_error.schema",
"description": "user errors",
"type": "object",
"properties": {
"code": {
"type": "integer",
"description": "Error code"
"type": "string",
"description": "Error code",
"examples": [
"MULTIPLE_USER_WITH_EMAIL",
"EMAIL_NOT_PROVIDED"
]
},
"errorMessage": {
"type": "string",
"description": "Error message text",
"description": "Error code description",
"examples": [
{
"value": "MULTIPLE_USER_EXISTS",
"description": "Multiple users found with the same email"
}
"Multiple users found with the same email",
"emailId is missing in the request"
]
}
},
Expand Down
24 changes: 11 additions & 13 deletions ramls/schemas/user_error_404.schema
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "external_patron_error.schema",
"description": "An external_patron user error",
"id": "user_error.schema",
"description": "user errors",
"type": "object",
"properties": {
"code": {
"type": "integer",
"description": "Error code"
"type": "string",
"description": "Error code",
"examples": [
"USER_ACCOUNT_INACTIVE",
"USER_NOT_FOUND"
]
},
"errorMessage": {
"type": "string",
"description": "Error message text",
"description": "Error code description",
"examples": [
{
"value": "USER_ACCOUNT_INACTIVE",
"description": "User is not active"
},
{
"value": "USER_NOT_FOUND",
"description": "User does not exist"
}
"User account is not active",
"User does not exist"
]
}
},
Expand Down
2 changes: 1 addition & 1 deletion ramls/staging_user.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "object",
"properties": {
"isEmailVerified": {
"description": "A boolean flag that indicates whether the patron has completed email verification. If this value is not provided when creating a new record, it will default to false. However, for Kiosk user registrations, this value should be sent true.",
"description": "A boolean flag that indicates whether the patron has completed email verification. If this value is not provided when creating a new record, it will default to false. However, for Kiosk user registrations, this value should be sent false.",
"type": "boolean"
},
"status": {
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/folio/edge/patron/MainVerticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,6 @@ public Router defineRoutes() {
router.route(HttpMethod.GET, "/patron/account/:patronId/external-patrons")
.handler(patronHandler::handleGetExtPatronsAccounts);

router.route(HttpMethod.GET, "/patron/account/:patronId/by-email/:emailId")
.handler(patronHandler::handleGetExtPatronAccountByEmail);

router.route(HttpMethod.PUT, "/patron/account/:patronId/by-email/:emailId")
.handler(patronHandler::handlePutExtPatronAccountByEmail);

Expand All @@ -84,6 +81,9 @@ public Router defineRoutes() {
router.route(HttpMethod.POST, "/patron/account/:patronId/hold/:holdId/cancel")
.handler(patronHandler::handleCancelHold);

router.route(HttpMethod.GET, "/patron/registration-status")
.handler(patronHandler::handleGetPatronRegistrationStatus);

return router;
}
}
86 changes: 76 additions & 10 deletions src/main/java/org/folio/edge/patron/PatronHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static org.folio.edge.patron.Constants.PARAM_SORT_BY;
import static org.folio.edge.patron.model.HoldCancellationValidator.validateCancelHoldRequest;

import com.amazonaws.util.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpHeaders;
Expand All @@ -33,6 +34,7 @@
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -42,6 +44,7 @@
import org.apache.logging.log4j.Logger;
import org.folio.edge.core.Handler;
import org.folio.edge.core.security.SecureStore;
import org.folio.edge.core.utils.Mappers;
import org.folio.edge.core.utils.OkapiClient;
import org.folio.edge.core.utils.OkapiClientFactory;
import org.folio.edge.patron.model.error.Error;
Expand Down Expand Up @@ -139,16 +142,6 @@ public void handleRenew(RoutingContext ctx) {

}

public void handleGetExtPatronAccountByEmail(RoutingContext ctx) {
handleCommon(ctx,
new String[] { PARAM_PATRON_ID, PARAM_EMAIL_ID },
new String[] {},
(client, params) -> ((PatronOkapiClient) client).getExtPatronAccountByEmail(
params.get(PARAM_EMAIL_ID),
resp -> handleProxyResponse(ctx, resp),
t -> handleProxyException(ctx, t)));
}

public void handlePutExtPatronAccountByEmail(RoutingContext ctx) {
if (ctx.body().asJsonObject() == null) {
badRequest(ctx, MSG_EXTERNAL_NOBODY);
Expand Down Expand Up @@ -260,6 +253,26 @@ public void handleGetAllowedServicePoints(RoutingContext ctx) {
t -> handleProxyException(ctx, t)));
}

public void handleGetPatronRegistrationStatus(RoutingContext ctx) {
logger.debug("handleGetPatronRegistrationStatus:: Fetching patron registration");
String emailId = ctx.request().getParam(PARAM_EMAIL_ID);
if(StringUtils.isNullOrEmpty(emailId)) {
logger.warn("handleGetPatronRegistrationStatus:: Missing or empty emailId");
ctx.response()
.setStatusCode(400)
.putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.end(getErrorMsg("EMAIL_NOT_PROVIDED", "emailId is missing in the request"));
return;
}
super.handleCommon(ctx, new String[]{}, new String[]{}, (client, params) -> {
String alternateTenantId = ctx.request().getParam("alternateTenantId", client.tenant);
final PatronOkapiClient patronClient = new PatronOkapiClient(client, alternateTenantId);
patronClient.getPatronRegistrationStatus(emailId,
resp -> handleRegistrationStatusResponse(ctx, resp),
t -> handleProxyException(ctx, t));
});
}

@Override
protected void invalidApiKey(RoutingContext ctx, String msg) {
accessDenied(ctx, msg);
Expand Down Expand Up @@ -332,6 +345,32 @@ protected void handleProxyResponse(RoutingContext ctx, HttpResponse<Buffer> resp
}
}

protected void handleRegistrationStatusResponse(RoutingContext ctx, HttpResponse<Buffer> resp) {
HttpServerResponse serverResponse = ctx.response();

int statusCode = resp.statusCode();
serverResponse.setStatusCode(statusCode);

String respBody = resp.bodyAsString();
if (logger.isDebugEnabled() ) {
logger.debug("handleRegistrationStatusResponse:: response {} ", respBody);
}

String contentType = resp.getHeader(HttpHeaders.CONTENT_TYPE.toString());

if (resp.statusCode() < 400 && Objects.nonNull(respBody)){
setContentType(serverResponse, contentType);
serverResponse.end(respBody); //not an error case, pass on the response body as received
}
else {
String errorMsg = (statusCode == 404 || statusCode == 400)
? getFormattedErrorMsg(statusCode, respBody)
: getStructuredErrorMessage(statusCode, respBody);
setContentType(serverResponse, APPLICATION_JSON);
serverResponse.end(errorMsg);
}
}

@Override
protected void handleProxyException(RoutingContext ctx, Throwable t) {
logger.error("Exception retrieving data from mod-patron:", t);
Expand Down Expand Up @@ -435,6 +474,33 @@ private String get422ErrorMsg(int statusCode, String respBody){
return errorMessage;
}

private String getFormattedErrorMsg(int statusCode, String respBody) {
logger.debug("getFormattedErrorMsg:: respBody {}", respBody);
String errorMessage = "";
try {
var errors = Json.decodeValue(respBody, Errors.class).getErrors();
if (errors != null && !errors.isEmpty()) {
var error = errors.get(0);
return getErrorMsg(error.getCode(), error.getMessage());
}
} catch (Exception ex) {
logger.warn(ex.getMessage());
errorMessage = getStructuredErrorMessage(statusCode, respBody);
}
return errorMessage;
}

private String getErrorMsg(String code, String errorMessage) {
Map<String, String> errorMap = new HashMap<>();
errorMap.put("errorMessage", errorMessage);
errorMap.put("code", code);
try {
return Mappers.jsonMapper.writeValueAsString(errorMap);
} catch (JsonProcessingException e) {
return getStructuredErrorMessage(500, "A problem encountered when extracting error message");
}
}

private String getErrorMessage(int statusCode, String respBody){

if (statusCode == 422)
Expand Down
18 changes: 7 additions & 11 deletions src/main/java/org/folio/edge/patron/utils/PatronOkapiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,17 +95,6 @@ public void getAccount(String patronId, boolean includeLoans, boolean includeCha
exceptionHandler);
}

public void getExtPatronAccountByEmail(String email, Handler<HttpResponse<Buffer>> responseHandler,
Handler<Throwable> exceptionHandler) {
String url = String.format("%s/patron/account/by-email/%s", okapiURL, email);
get(
url,
tenant,
null,
responseHandler,
exceptionHandler);
}

public void getExtPatronAccounts(boolean expired, Handler<HttpResponse<Buffer>> responseHandler,
Handler<Throwable> exceptionHandler) {
String url = String.format("%s/patron/account?expired=%s", okapiURL, expired);
Expand Down Expand Up @@ -219,6 +208,13 @@ public void placeInstanceHold(String patronId, String instanceId, String request
exceptionHandler);
}

public void getPatronRegistrationStatus(String emailId,
Handler<HttpResponse<Buffer>> responseHandler, Handler<Throwable> exceptionHandler) {

get(String.format("%s/patron/registration-status/%s", okapiURL,
emailId), tenant, null, responseHandler, exceptionHandler);
}

private Hold createCancellationHoldRequest(JsonObject cancellationRequest, JsonObject baseRequest, String patronId) {
return Hold.builder()
.cancellationReasonId(cancellationRequest.getString(FIELD_CANCELLATION_REASON_ID))
Expand Down
Loading

0 comments on commit 7659857

Please sign in to comment.