diff --git a/pom.xml b/pom.xml index 7e8627c9eeb73329ba9122be087751bbd9542278..78278a19baf22aafc5e3622f40a6eba065a8e209 100644 --- a/pom.xml +++ b/pom.xml @@ -143,6 +143,12 @@ io.micrometer micrometer-registry-prometheus + + + org.apache.commons + commons-lang3 + 3.8.1 + diff --git a/src/main/java/biz/nynja/account/components/PhoneNumberNormalizer.java b/src/main/java/biz/nynja/account/components/PhoneNumberNormalizer.java new file mode 100644 index 0000000000000000000000000000000000000000..afac39dd5510c26f24319e8bbee08469b70399d7 --- /dev/null +++ b/src/main/java/biz/nynja/account/components/PhoneNumberNormalizer.java @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.components; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import biz.nynja.account.grpc.AddContactInfoRequest; +import biz.nynja.account.grpc.ContactDetails; +import biz.nynja.account.grpc.DeleteContactInfoRequest; + +@Service +public class PhoneNumberNormalizer { + + @Autowired + private Validator validator; + + public AddContactInfoRequest normalizePhoneNumber(AddContactInfoRequest request) { + // Get the normalized phone number from libphone + ContactDetails newContactDetails = ContactDetails.newBuilder().setType(request.getContactInfo().getType()) + .setValue(validator.getNormalizedPhoneNumber(request.getContactInfo().getValue())) + .setLabel(request.getContactInfo().getLabel()).build(); + AddContactInfoRequest newRequest = AddContactInfoRequest.newBuilder().setAccountId(request.getAccountId()) + .setContactInfo(newContactDetails).build(); + request = newRequest; + return request; + } + + public DeleteContactInfoRequest normalizePhoneNumber(DeleteContactInfoRequest request) { + // Get the normalized phone number from libphone + ContactDetails newContactDetails = ContactDetails.newBuilder().setType(request.getContactInfo().getType()) + .setValue(validator.getNormalizedPhoneNumber(request.getContactInfo().getValue())) + .setLabel(request.getContactInfo().getLabel()).build(); + DeleteContactInfoRequest newRequest = DeleteContactInfoRequest.newBuilder().setAccountId(request.getAccountId()) + .setContactInfo(newContactDetails).build(); + request = newRequest; + return request; + } +} diff --git a/src/main/java/biz/nynja/account/components/Validator.java b/src/main/java/biz/nynja/account/components/Validator.java index 2da72ad4f34203cac4f70fd11a34076806631933..0703c1d2198c6614e0aab9d4655d7c17235e1495 100644 --- a/src/main/java/biz/nynja/account/components/Validator.java +++ b/src/main/java/biz/nynja/account/components/Validator.java @@ -8,11 +8,13 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.PostConstruct; +import org.apache.commons.lang3.tuple.ImmutablePair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ClassPathResource; @@ -26,9 +28,11 @@ import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; import biz.nynja.account.grpc.AuthProviderDetails; import biz.nynja.account.grpc.AuthenticationType; import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; +import biz.nynja.account.grpc.ContactDetails; import biz.nynja.account.grpc.ContactType; import biz.nynja.account.grpc.CreatePendingAccountRequest; import biz.nynja.account.grpc.DeleteAuthenticationProviderRequest; +import biz.nynja.account.grpc.DeleteContactInfoRequest; import biz.nynja.account.grpc.ErrorResponse.Cause; import biz.nynja.account.grpc.UpdateAccountRequest; import biz.nynja.account.grpc.UpdateProfileRequest; @@ -238,32 +242,32 @@ public class Validator { return null; } - public Cause validateContactInfo(ContactType type, String contactInfoValue) { + public Optional> validateContactInfo(ContactType type, String contactInfoValue) { if (contactInfoValue == null || contactInfoValue.trim().isEmpty()) { - return Cause.MISSING_CONTACT_INFO_IDENTIFIER; + return Optional.of(new ImmutablePair<>(Cause.MISSING_CONTACT_INFO_IDENTIFIER, "Missing contact info identifier")); } switch (type) { case MISSING_CONTACT_TYPE: - return Cause.MISSING_CONTACT_INFO_TYPE; + return Optional.of(new ImmutablePair<>(Cause.MISSING_CONTACT_INFO_TYPE, "Missing contact info type")); case PHONE_CONTACT: // We expect to receive phone number in the following format : ":" String[] provider = contactInfoValue.split(":"); if (provider == null || provider.length != 2) { - return Cause.PHONE_NUMBER_INVALID; + return Optional.of(new ImmutablePair<>(Cause.PHONE_NUMBER_INVALID, "Invalid phone number")); } if (!isPhoneNumberValid(provider[1], provider[0])) { - return Cause.PHONE_NUMBER_INVALID; + return Optional.of(new ImmutablePair<>(Cause.PHONE_NUMBER_INVALID, "Invalid phone number")); } break; case EMAIL_CONTACT: if (!isEmailValid(contactInfoValue)) { - return Cause.EMAIL_INVALID; + return Optional.of(new ImmutablePair<>(Cause.EMAIL_INVALID, "Invalid email")); } break; default: break; } - return null; + return Optional.empty(); } public Cause validateCreatePendingAccountRequest(CreatePendingAccountRequest request) { @@ -340,20 +344,21 @@ public class Validator { request.getAuthenticationProvider().getAuthenticationProvider()); } - public Cause checkAddContactInfoRequest(AddContactInfoRequest request) { - if ((request.getAccountId() == null) || (request.getAccountId().isEmpty())) { - return Cause.MISSING_ACCOUNT_ID; + public Optional> validateContactInfoRequest(String accountId, ContactDetails contactDetails) { + if ((accountId == null) || (accountId.isEmpty())) { + return Optional.of(new ImmutablePair<>(Cause.MISSING_ACCOUNT_ID, "Missing account id")); } - if (request.getContactInfo().getTypeValue() == 0) { - return Cause.MISSING_CONTACT_INFO_TYPE; + if (contactDetails.getTypeValue() == 0) { + return Optional.of(new ImmutablePair<>(Cause.MISSING_CONTACT_INFO_TYPE, "Missing contact info type")); + } - if (request.getContactInfo().getValue() == null || request.getContactInfo().getValue().isEmpty()) { - return Cause.MISSING_CONTACT_INFO_IDENTIFIER; + if (contactDetails.getValue() == null || contactDetails.getValue().isEmpty()) { + return Optional.of(new ImmutablePair<>(Cause.MISSING_CONTACT_INFO_IDENTIFIER, "Missing contact info identifier")); } - if (!isValidUuid(request.getAccountId())) { - return Cause.INVALID_ACCOUNT_ID; + if (!isValidUuid(accountId)) { + return Optional.of(new ImmutablePair<>(Cause.INVALID_ACCOUNT_ID, "Invalid account id")); } - return validateContactInfo(request.getContactInfo().getType(), request.getContactInfo().getValue()); + return validateContactInfo(contactDetails.getType(), contactDetails.getValue()); } public Cause validateDeleteAuthenticationProviderRequest(DeleteAuthenticationProviderRequest request) { diff --git a/src/main/java/biz/nynja/account/models/ContactInfo.java b/src/main/java/biz/nynja/account/models/ContactInfo.java index 982681d9f49ff756f339203d77bd7defcd9d9d1c..9fffa48da04b0e8905defe0ab83842efd6c983c5 100644 --- a/src/main/java/biz/nynja/account/models/ContactInfo.java +++ b/src/main/java/biz/nynja/account/models/ContactInfo.java @@ -99,7 +99,6 @@ public class ContactInfo { if (contactType != null) { builder.setType(contactType); } - if (getValue() != null) { builder.setValue(getValue()); } diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java index 382adca051ce6bd76ddf501efc52eccaa5da2695..876f037bb6fb55ca60b9a3236a1470c96da16381 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditional.java @@ -40,4 +40,6 @@ public interface AccountRepositoryAdditional { boolean deleteAuthenticationProvider(Profile profile, AuthenticationProvider authProvider); boolean addContactInfo(UUID accountId, ContactInfo contactInfo); + + boolean deleteContactInfo(UUID accountId, ContactInfo contactInfo); } diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index 8aa3bfc41f8eaedf010ed0cd7943c7e363ff92ce..d1051637087dcefe91d2293d757a89a737000952 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -544,6 +544,33 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio return false; } + @Override + public boolean deleteContactInfo(UUID accountId, ContactInfo contactInfo) { + Account accountToUpdate = accountRepository.findByAccountId(accountId); + if (accountToUpdate == null) { + logger.error("Existing account with the provided id {} was not found.", accountId); + return false; + } + Set contactsInfoSet = accountToUpdate.getContactsInfo(); + if (contactsInfoSet == null) { + logger.error("No existing contact info found for account {}.", accountId); + return false; + } + if (!contactsInfoSet.remove(contactInfo)) { + logger.error("Error removing contact info from account {}. Existing contact info: {} was not found.", + accountId, contactInfo.toString()); + return false; + } + accountToUpdate.setContactsInfo(contactsInfoSet); + Long timeUpdated = Instant.now().toEpochMilli(); + accountToUpdate.setLastUpdateTimestamp(timeUpdated); + WriteResult wr = cassandraTemplate.update(accountToUpdate, UpdateOptions.builder().ifExists(true).build()); + if (wr != null && wr.wasApplied()) { + return true; + } + return false; + } + @Override public boolean deleteAuthenticationProvider(Profile profile, AuthenticationProvider authProvider) { diff --git a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java index fe0bea37ad83d8bff728a08297d483fc9249c0bd..19fbaddc9904fc89ce51fbe5aff2d39fc517d56d 100644 --- a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java +++ b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java @@ -7,13 +7,16 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Optional; import java.util.UUID; +import org.apache.commons.lang3.tuple.ImmutablePair; import org.lognet.springboot.grpc.GRpcService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import biz.nynja.account.components.AccountServiceHelper; +import biz.nynja.account.components.PhoneNumberNormalizer; import biz.nynja.account.components.Validator; import biz.nynja.account.grpc.AccountByAccountIdRequest; import biz.nynja.account.grpc.AccountByAuthenticationProviderRequest; @@ -35,6 +38,7 @@ import biz.nynja.account.grpc.CreatePendingAccountRequest; import biz.nynja.account.grpc.CreatePendingAccountResponse; import biz.nynja.account.grpc.DeleteAccountRequest; import biz.nynja.account.grpc.DeleteAuthenticationProviderRequest; +import biz.nynja.account.grpc.DeleteContactInfoRequest; import biz.nynja.account.grpc.StatusResponse; import biz.nynja.account.grpc.DeleteProfileRequest; import biz.nynja.account.grpc.ErrorResponse; @@ -101,6 +105,9 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas @Autowired private AccountServiceHelper accountServiceHelper; + @Autowired + private PhoneNumberNormalizer phoneNumberNormalizer; + @Override public void createAccount(CreateAccountRequest request, StreamObserver responseObserver) { @@ -560,19 +567,16 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas StreamObserver responseObserver) { logger.info("Adding contact info to account requested."); logger.debug("Adding contact info to account requested: {}", request); - - Cause cause = validator.checkAddContactInfoRequest(request); - if (cause != null) { - responseObserver - .onNext(StatusResponse.newBuilder().setError(ErrorResponse.newBuilder().setCause(cause)).build()); - responseObserver.onCompleted(); + Optional> validationResult = validator + .validateContactInfoRequest(request.getAccountId(), request.getContactInfo()); + if (validationResult.isPresent()) { + prepareErrorStatusResponse(responseObserver, validationResult.get().getKey(), + validationResult.get().getValue()); return; } - if (request.getContactInfo().getType() == ContactType.PHONE_CONTACT) { - request = normalizePhoneNumber(request); + request = phoneNumberNormalizer.normalizePhoneNumber(request); } - boolean result = accountRepositoryAdditional.addContactInfo(UUID.fromString(request.getAccountId()), ContactInfo.createContactInfoFromProto(request.getContactInfo())); if (result) { @@ -584,21 +588,38 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas } logger.error("Contact info {}:{} was not added to account {}.", request.getContactInfo().getType().name(), request.getContactInfo().getValue(), request.getAccountId()); - responseObserver.onNext(StatusResponse.newBuilder() - .setError(ErrorResponse.newBuilder().setCause(Cause.ERROR_ADDING_CONTACT_INFO)).build()); - responseObserver.onCompleted(); + prepareErrorStatusResponse(responseObserver, Cause.ERROR_ADDING_CONTACT_INFO, "Error adding contact info"); return; } - private AddContactInfoRequest normalizePhoneNumber(AddContactInfoRequest request) { - // Get the normalized phone number from libphone - ContactDetails newContactDetails = ContactDetails.newBuilder().setType(request.getContactInfo().getType()) - .setValue(validator.getNormalizedPhoneNumber(request.getContactInfo().getValue())) - .setLabel(request.getContactInfo().getLabel()).build(); - AddContactInfoRequest newRequest = AddContactInfoRequest.newBuilder().setAccountId(request.getAccountId()) - .setContactInfo(newContactDetails).build(); - request = newRequest; - return request; + @Override + public void deleteContactInfoFromAccount(DeleteContactInfoRequest request, + StreamObserver responseObserver) { + logger.info("Removing contact info from account requested."); + logger.debug("Removing contact info from account requested: {}", request); + Optional> validationResult = validator + .validateContactInfoRequest(request.getAccountId(), request.getContactInfo()); + if (validationResult.isPresent()) { + prepareErrorStatusResponse(responseObserver, validationResult.get().getKey(), + validationResult.get().getValue()); + return; + } + if (request.getContactInfo().getType() == ContactType.PHONE_CONTACT) { + request = phoneNumberNormalizer.normalizePhoneNumber(request); + } + boolean result = accountRepositoryAdditional.deleteContactInfo(UUID.fromString(request.getAccountId()), + ContactInfo.createContactInfoFromProto(request.getContactInfo())); + if (result) { + logger.info("Contact info {}:{} was removed from account {}.", request.getContactInfo().getType().name(), + request.getContactInfo().getValue(), request.getAccountId()); + responseObserver.onNext(StatusResponse.newBuilder().setStatus("SUCCESS").build()); + responseObserver.onCompleted(); + return; + } + logger.error("Contact info {}:{} was not removed from account {}.", request.getContactInfo().getType().name(), + request.getContactInfo().getValue(), request.getAccountId()); + prepareErrorStatusResponse(responseObserver, Cause.ERROR_REMOVING_CONTACT_INFO, "Error removing contact info"); + return; } @Override @@ -677,4 +698,11 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas responseObserver.onCompleted(); return; } -} \ No newline at end of file + + public void prepareErrorStatusResponse(StreamObserver responseObserver, Cause error, + String message) { + responseObserver.onNext(StatusResponse.newBuilder() + .setError(ErrorResponse.newBuilder().setCause(error).setMessage(message)).build()); + responseObserver.onCompleted(); + } +}