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

[MODPATRON-166]-Added GET api for external patron. #141

Merged
merged 7 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
8 changes: 8 additions & 0 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@
"users.item.get"
]
},
{
"methods": ["GET"],
"pathPattern": "/patron/account/by-email/{email}",
"permissionsRequired": ["patron.account.item.get"],
"modulePermissions": [
"users.item.get"
]
},
{
"methods": ["POST"],
"pathPattern": "/patron/account/{accountId}/item/{itemId}/renew",
Expand Down
42 changes: 42 additions & 0 deletions ramls/patron.raml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,48 @@ traits:
body:
text/plain:
example: internal server error, contact administrator
/by-email/{emailId}:
displayName: Get Accounts By email
description: Service endpoints that manage accounts by an existing email
uriParameters:
emailId:
description: The email of external patron
type: string
get:
description: Return account details for the specified external patron email
responses:
200:
description: Returns the external patron account info
body:
application/json:
type: external_patron
example: !include examples/external_patron.json
400:
description: Bad request
body:
text/plain:
example: unable to process request -- constraint violation
401:
description: Not authorized to perform requested action
body:
text/plain:
example: unable to get account -- unauthorized
404:
description: Item with a given EMAIL not found
body:
text/plain:
example: account not found
403:
description: Access Denied
body:
text/plain:
example: Access Denied
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
20 changes: 20 additions & 0 deletions src/main/java/org/folio/patron/rest/models/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ public void setAddresses(List<Address> addresses) {

private String preferredContactTypeId;

public List<Address> getAddresses() {
return addresses;
}

@Data
public static class Address {

Expand All @@ -206,6 +210,22 @@ public String getId() {
return id;
}

public String getCountryId() {
return countryId;
}

public String getAddressLine2() {
return addressLine2;
}

public String getRegion() {
return region;
}

public String getPostalCode() {
return postalCode;
}

public void setId(String id) {
this.id = id;
}
Expand Down
58 changes: 58 additions & 0 deletions src/main/java/org/folio/patron/rest/utils/PatronUtils.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
package org.folio.patron.rest.utils;

import org.folio.patron.rest.models.User;
import org.folio.rest.jaxrs.model.Address0;
import org.folio.rest.jaxrs.model.Address1;
import org.folio.rest.jaxrs.model.ContactInfo;
import org.folio.rest.jaxrs.model.ExternalPatron;
import org.folio.rest.jaxrs.model.GeneralInfo;
import org.folio.rest.jaxrs.model.PreferredEmailCommunication;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

public class PatronUtils {
Expand All @@ -16,6 +23,57 @@ private PatronUtils() {

private static final String USER_TYPE = "patron";

public static ExternalPatron mapToExternalPatron(User user) {
if (user == null) {
return null;
}
ExternalPatron externalPatron = new ExternalPatron();

GeneralInfo generalInfo = new GeneralInfo();
generalInfo.setExternalSystemId(user.getExternalSystemId());
generalInfo.setFirstName(user.getPersonal().getFirstName());
generalInfo.setLastName(user.getPersonal().getLastName());
generalInfo.setMiddleName(user.getPersonal().getMiddleName());
generalInfo.setPreferredFirstName(user.getPersonal().getPreferredFirstName());
externalPatron.setGeneralInfo(generalInfo);

ContactInfo contactInfo = new ContactInfo();
contactInfo.setEmail(user.getPersonal().getEmail());
contactInfo.setPhone(user.getPersonal().getPhone());
contactInfo.setMobilePhone(user.getPersonal().getMobilePhone());
externalPatron.setContactInfo(contactInfo);

Set<PreferredEmailCommunication> preferredEmailCommunication = new LinkedHashSet<>(user.getPreferredEmailCommunication());
externalPatron.setPreferredEmailCommunication(preferredEmailCommunication);

List<User.Personal.Address> userAddresses = user.getPersonal().getAddresses();
if (userAddresses != null) {
for (int i = 0; i < userAddresses.size(); i++) {
User.Personal.Address userAddress = userAddresses.get(i);
if (i == 0) {
Address0 address0 = new Address0();
address0.setCountry(userAddress.getCountryId());
address0.setAddressLine0(userAddress.getAddressLine1());
address0.setAddressLine1(userAddress.getAddressLine2());
address0.setCity(userAddress.getCity());
address0.setProvince(userAddress.getRegion());
address0.setZip(userAddress.getPostalCode());
externalPatron.setAddress0(address0);
} else if (i == 1) {
Address1 address1 = new Address1();
address1.setCountry(userAddress.getCountryId());
address1.setAddressLine0(userAddress.getAddressLine1());
address1.setAddressLine1(userAddress.getAddressLine2());
address1.setCity(userAddress.getCity());
address1.setProvince(userAddress.getRegion());
address1.setZip(userAddress.getPostalCode());
externalPatron.setAddress1(address1);
}
}
}
return externalPatron;
}

public static User mapToUser(ExternalPatron externalPatron, String remotePatronGroupId, String homeAddressTypeId, String workAddressTypeId) {
if (externalPatron == null) {
return null;
Expand Down
45 changes: 43 additions & 2 deletions src/main/java/org/folio/rest/impl/PatronServicesResourceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static io.vertx.core.Future.succeededFuture;
import static java.lang.String.format;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static org.folio.patron.rest.utils.PatronUtils.mapToExternalPatron;
import static org.folio.rest.impl.Constants.JSON_FIELD_CONTRIBUTORS;
import static org.folio.rest.impl.Constants.JSON_FIELD_CONTRIBUTOR_NAMES;
import static org.folio.rest.impl.Constants.JSON_FIELD_HOLDINGS_RECORD_ID;
Expand Down Expand Up @@ -30,6 +31,7 @@
import static org.folio.rest.jaxrs.resource.Patron.PostPatronAccountItemHoldByIdAndItemIdResponse.respond422WithApplicationJson;
import static org.folio.rest.jaxrs.resource.Patron.PostPatronAccountItemHoldByIdAndItemIdResponse.respond500WithTextPlain;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
Expand All @@ -47,6 +49,8 @@

import javax.ws.rs.core.Response;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.folio.integration.http.HttpClientFactory;
import org.folio.integration.http.ResponseInterpreter;
Expand Down Expand Up @@ -96,7 +100,7 @@ public class PatronServicesResourceImpl implements Patron {
private static final String PATRON_GROUP = "patronGroup";
private static final String ADDRESS_TYPES = "addressTypes";
private static final String USER_GROUPS = "usergroups";
private static final String REMOTE_GROUP = "Remote Non-circulating";
private static final String REMOTE_GROUP = "staff";
private static final String HOME = "home";
private static final String WORK = "work";
private static final String ADDRESS_TYPE = "addressType";
Expand All @@ -108,7 +112,6 @@ public void postPatronAccount(ExternalPatron entity, Map<String, String> okapiHe
var httpClient = HttpClientFactory.getHttpClient(vertxContext.owner());
final var userRepository = new UserRepository(httpClient);
String patronEmail = entity.getContactInfo().getEmail();

getUserByEmail(patronEmail, okapiHeaders, userRepository)
.thenCompose(userResponse -> handleUserResponse(userResponse, entity, okapiHeaders, userRepository))
.thenAccept(response -> asyncResultHandler.handle(Future.succeededFuture(response)))
Expand All @@ -118,6 +121,44 @@ public void postPatronAccount(ExternalPatron entity, Map<String, String> okapiHe
});
}

@Override
public void getPatronAccountByEmailByEmailId(String email, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
var httpClient = HttpClientFactory.getHttpClient(vertxContext.owner());
final var userRepository = new UserRepository(httpClient);

getUserByEmail(email, okapiHeaders, userRepository)
.thenAccept(userResponse -> handleGetUserResponse(userResponse, asyncResultHandler))
.exceptionally(throwable -> {
asyncResultHandler.handle(Future.failedFuture(throwable));
return null;
});
}

private void handleGetUserResponse(JsonObject userResponse, Handler<AsyncResult<Response>> asyncResultHandler) {
int totalRecords = userResponse.getInteger(TOTAL_RECORDS);

if (totalRecords > 1) {
asyncResultHandler.handle(Future.succeededFuture(GetPatronAccountByEmailByEmailIdResponse.respond400WithTextPlain("Multiple users found with the same email")));
} else if (totalRecords == 1) {
JsonObject userJson = userResponse.getJsonArray(USERS).getJsonObject(0);
User user = convertJsonToUser(userJson);
ExternalPatron externalPatron = mapToExternalPatron(user);
asyncResultHandler.handle(Future.succeededFuture(GetPatronAccountByEmailByEmailIdResponse.respond200WithApplicationJson(externalPatron)));
} else {
asyncResultHandler.handle(Future.succeededFuture(GetPatronAccountByEmailByEmailIdResponse.respond404WithTextPlain("User not found")));
}
}

private User convertJsonToUser(JsonObject userJson) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
try {
return mapper.readValue(userJson.encode(), User.class);
} catch (IOException e) {
return null;
}
}

private CompletableFuture<JsonObject> getUserByEmail(String email, Map<String, String> okapiHeaders, UserRepository userRepository) {
return userRepository.getUserByEmail(email, okapiHeaders);
}
Expand Down
16 changes: 16 additions & 0 deletions src/test/java/org/folio/rest/impl/PatronResourceImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public class PatronResourceImplTest {
private final String mockDataFolder = "PatronServicesResourceImpl";
private final String accountPath = "/patron/account/{accountId}";
private final String remotePatronAccountPath = "/patron/account";
private final String remotePatronAccountPathByEmail = "/patron/account/by-email";
private final String itemPath = "/item/{itemId}";
private final String instancePath = "/instance/{instanceId}";
private final String holdPath = "/hold";
Expand Down Expand Up @@ -973,6 +974,21 @@ public final void testGetPatronAccountByIdNoLists() {
logger.info("Test done");
}

@Test
final void testGetPatronAccountByEmail() {
given()
.header(tenantHeader)
.header(urlHeader)
.header(contentTypeHeader)
.when()
.get(remotePatronAccountPathByEmail + "/adsfg")
.then()
.log().all()
.contentType(ContentType.JSON)
.statusCode(200)
.extract().response();
}

@Test
public final void testGetPatronAccountByIdNoListsWhenLimitZero() {
logger.info("Testing for successful patron services account retrieval by id without item lists");
Expand Down
Loading