Skip to content

Commit

Permalink
EDGPATRON-149 changing emailId from path param to query param and for…
Browse files Browse the repository at this point in the history
…matting error response
  • Loading branch information
Vignesh-kalyanasundaram committed Oct 17, 2024
1 parent 364129b commit 72cb196
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 36 deletions.
9 changes: 4 additions & 5 deletions ramls/edge-patron.raml
Original file line number Diff line number Diff line change
Expand Up @@ -623,17 +623,16 @@ types:
text/plain:
example: internal server error, contact administrator
/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
emailId:
description: The email ID of the patron.
type: string
required: true
responses:
200:
description: patron information retrieved successfully
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
2 changes: 1 addition & 1 deletion src/main/java/org/folio/edge/patron/MainVerticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public Router defineRoutes() {
router.route(HttpMethod.POST, "/patron/account/:patronId/hold/:holdId/cancel")
.handler(patronHandler::handleCancelHold);

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

return router;
Expand Down
39 changes: 27 additions & 12 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 Down Expand Up @@ -253,11 +254,19 @@ public void handleGetAllowedServicePoints(RoutingContext ctx) {
}

public void handleGetPatronRegistrationStatus(RoutingContext ctx) {
logger.info("handleGetPatronRegistrationStatus:: EMAIL_ID {}", ctx.request().getParam(PARAM_EMAIL_ID));
super.handleCommon(ctx, new String[]{PARAM_EMAIL_ID}, new String[]{}, (client, params) -> {
String emailId = ctx.request().getParam(PARAM_EMAIL_ID);
logger.debug("handleGetPatronRegistrationStatus:: Fetching patron details by emailId {}", emailId);
if(StringUtils.isNullOrEmpty(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(params.get(PARAM_EMAIL_ID),
patronClient.getPatronRegistrationStatus(emailId,
resp -> handleRegistrationStatusResponse(ctx, resp),
t -> handleProxyException(ctx, t));
});
Expand Down Expand Up @@ -464,27 +473,33 @@ private String get422ErrorMsg(int statusCode, String respBody){
return errorMessage;
}

private String getFormattedErrorMsg(int statusCode, String respBody){
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);
Map<String, String> errorMap = new HashMap<>();
errorMap.put("message", error.getMessage());
errorMap.put("code", error.getCode());
errorMessage = Mappers.jsonMapper.writeValueAsString(errorMap);
} else {
errorMessage = getStructuredErrorMessage(statusCode, "No error message found in response");
return getErrorMsg(error.getCode(), error.getMessage());
}
} catch(Exception ex) {
} catch (Exception ex) {
logger.warn(ex.getMessage());
errorMessage = getStructuredErrorMessage(statusCode, "A problem encountered when extracting error message");
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
11 changes: 0 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/registration-status/%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
106 changes: 101 additions & 5 deletions src/test/java/org/folio/edge/patron/MainVerticleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,110 @@ public void testGetAccountPatronNotFound(TestContext context) throws Exception {
}

@Test
public void testGetAccountByEmail(TestContext context) {
logger.info("=== Test request for getting external_patron by email ===");
public void testGetPatronRegistrationStatusWithoutEmail(TestContext context) {

RestAssured
.get(String.format("/patron/account/%s/by-email/%s?apikey=%s", extPatronId, "fgh@mail", apiKey))
var response = RestAssured
.get(String.format("/patron/registration-status?apikey=%s", apiKey))
.then()
.statusCode(400)
.header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.extract()
.response();

var jsonResponse = new JsonObject(response.body().asString());
assertEquals("EMAIL_NOT_PROVIDED", jsonResponse.getString("code"));
assertEquals("emailId is missing in the request", jsonResponse.getString("errorMessage"));

response = RestAssured
.get(String.format("/patron/registration-status?emailId=%s&apikey=%s", "", apiKey))
.then()
.statusCode(400)
.header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.extract()
.response();

jsonResponse = new JsonObject(response.body().asString());
assertEquals("EMAIL_NOT_PROVIDED", jsonResponse.getString("code"));
assertEquals("emailId is missing in the request", jsonResponse.getString("errorMessage"));
}

@Test
public void testGetPatronRegistrationStatusWithActiveEmail(TestContext context) {

final var response = RestAssured
.get(String.format("/patron/registration-status?emailId=%s&apikey=%s", "[email protected]", apiKey))
.then()
.statusCode(200)
.header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON);
.header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.extract()
.response();

var expected = new JsonObject(readMockFile(
"/user_active.json"));
var actual = new JsonObject(response.body().asString());
assertEquals(expected, actual);
}

@Test
public void testGetPatronRegistrationStatusWithInvalidEmail() {

final var response = RestAssured
.get(String.format("/patron/registration-status?emailId=%s&apikey=%s", "[email protected]", apiKey))
.then()
.statusCode(404)
.header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.extract()
.response();

var jsonResponse = new JsonObject(response.body().asString());
assertEquals("USER_NOT_FOUND", jsonResponse.getString("code"));
assertEquals("User does not exist", jsonResponse.getString("errorMessage"));
}

@Test
public void testGetPatronRegistrationStatusWithMultipleUserEmail() {

final var response = RestAssured
.get(String.format("/patron/registration-status?emailId=%s&apikey=%s", "[email protected]", apiKey))
.then()
.statusCode(400)
.header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.extract()
.response();

var jsonResponse = new JsonObject(response.body().asString());
assertEquals("MULTIPLE_USER_WITH_EMAIL", jsonResponse.getString("code"));
assertEquals("Multiple users found with the same email", jsonResponse.getString("errorMessage"));
}

@Test
public void testGetPatronRegistrationStatusWithInvalidScenarios() {

// when we are getting 404, we converted it to Errors.class. But there are cases where we get text/plain errors.
// In that case, code will return the error as it is.
var response = RestAssured
.get(String.format("/patron/registration-status?emailId=%s&apikey=%s", "[email protected]", apiKey))
.then()
.statusCode(404)
.header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.extract()
.response();

var jsonResponse = new JsonObject(response.body().asString());
assertEquals("404", jsonResponse.getString("code"));
assertEquals("Resource not found", jsonResponse.getString("errorMessage"));

response = RestAssured
.get(String.format("/patron/registration-status?emailId=%s&apikey=%s", "[email protected]", apiKey))
.then()
.statusCode(500)
.header(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.extract()
.response();

jsonResponse = new JsonObject(response.body().asString());
assertEquals("500", jsonResponse.getString("code"));
assertEquals("unable to retrieve user details", jsonResponse.getString("errorMessage"));
}

@Test
Expand Down
44 changes: 43 additions & 1 deletion src/test/java/org/folio/edge/patron/utils/PatronMockOkapi.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static org.folio.edge.core.Constants.DAY_IN_MILLIS;
import static org.folio.edge.core.Constants.TEXT_PLAIN;
import static org.folio.edge.core.Constants.X_OKAPI_TOKEN;
import static org.folio.edge.patron.Constants.PARAM_EMAIL_ID;
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 @@ -22,10 +23,13 @@
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Currency;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -166,6 +170,9 @@ public Router defineRoutes() {
router.route(HttpMethod.GET, "/circulation/requests/:requestId")
.handler(this::getRequestHandler);

router.route(HttpMethod.GET, "/patron/registration-status/:emailId")
.handler(this::getRegistrationStatusHandler);

return router;
}

Expand Down Expand Up @@ -255,6 +262,42 @@ public void getExtPatronAccountHandler(RoutingContext ctx) {
}
}

public void getRegistrationStatusHandler(RoutingContext ctx) {
String token = ctx.request().getHeader(X_OKAPI_TOKEN);
String emailId = ctx.request().getParam(PARAM_EMAIL_ID);
if (token == null || !token.equals(MOCK_TOKEN)) {
ctx.response()
.setStatusCode(403)
.putHeader(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN)
.end("Access requires permission: patron.account.get");
} else if(emailId.equals("[email protected]")) {
ctx.response()
.setStatusCode(200)
.putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.end(readMockFile("/user_active.json"));
} else if(emailId.equals("[email protected]")) {
ctx.response()
.setStatusCode(400)
.putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.end(readMockFile("/multiple_user_error.json"));
} else if(emailId.equals("[email protected]")) {
ctx.response()
.setStatusCode(404)
.putHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.end(readMockFile("/user_not_found_error.json"));
} else if(emailId.equals("[email protected]")) {
ctx.response()
.setStatusCode(404)
.putHeader(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN)
.end("Resource not found");
} else {
ctx.response()
.setStatusCode(500)
.putHeader(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN)
.end("unable to retrieve user details");
}
}

public void putExtPatronAccountHandler(RoutingContext ctx) {
String token = ctx.request().getHeader(X_OKAPI_TOKEN);
if (token == null || !token.equals(MOCK_TOKEN)) {
Expand Down Expand Up @@ -667,7 +710,6 @@ public static Patron getPatron() {
.preferredEmailCommunication(new ArrayList<>())
.build();
}

public static Charge getCharge(String itemId) {
return Charge.builder()
.item(getItem(itemId_overdue))
Expand Down
9 changes: 9 additions & 0 deletions src/test/resources/multiple_user_error.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"errors": [
{
"message": "Multiple users found with the same email",
"code": "MULTIPLE_USER_WITH_EMAIL",
"parameters": []
}
]
}
46 changes: 46 additions & 0 deletions src/test/resources/user_active.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"id": "cacc29d8-cade-4312-a5f2-4eeac55d8697",
"externalSystemId": "[email protected]",
"active": true,
"type": "patron",
"patronGroup": "63f8065f-df84-4e76-a36b-3ba32dbdc9e5",
"departments": [],
"proxyFor": [],
"personal": {
"lastName": "active",
"firstName": "folio",
"middleName": "",
"preferredFirstName": "new",
"email": "[email protected]",
"phone": "555-123456",
"mobilePhone": "555-5678",
"addresses": [
{
"id": "ec8c23d5-c301-4bba-8ade-39cc409e5d7e",
"countryId": "US",
"addressLine1": "123 Main St",
"addressLine2": "Apt 8",
"city": "Metropolis",
"region": "NY",
"postalCode": "12345",
"addressTypeId": "93d3d88d-499b-45d0-9bc7-ac73c3a19880",
"primaryAddress": true
}
],
"preferredContactTypeId": "002"
},
"enrollmentDate": "2024-08-29T13:29:39.248+00:00",
"expirationDate": "2026-08-29T00:00:00.000+00:00",
"createdDate": "2024-08-29T13:29:39.256+00:00",
"updatedDate": "2024-08-29T13:29:39.256+00:00",
"metadata": {
"createdDate": "2024-08-29T13:29:39.250+00:00",
"createdByUserId": "21457ab5-4635-4e56-906a-908f05e9233b",
"updatedDate": "2024-08-29T13:29:39.250+00:00",
"updatedByUserId": "21457ab5-4635-4e56-906a-908f05e9233b"
},
"preferredEmailCommunication": [
"Support",
"Programs"
]
}
9 changes: 9 additions & 0 deletions src/test/resources/user_not_found_error.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"errors": [
{
"message": "User does not exist",
"code": "USER_NOT_FOUND",
"parameters": []
}
]
}

0 comments on commit 72cb196

Please sign in to comment.