diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index f6c0761bf7f8c3571d648fa3052b48506fad7b24..760f2c1a380b3195230ef4a081556609ba8100c9 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -11,6 +11,7 @@ import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.cassandra.core.CassandraBatchOperations; import org.springframework.data.cassandra.core.CassandraTemplate; import org.springframework.data.cassandra.core.WriteResult; @@ -32,7 +33,9 @@ import biz.nynja.account.models.ProfileByAuthenticationProvider; public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditional { private static final Logger logger = LoggerFactory.getLogger(AccountRepositoryAdditionalImpl.class); - private static final int COMPLETE_PENDING_ACCOUNT_TIMEOUT = 30 * 60 * 1000; + + @Value("${complete.pending.account.timeout-minutes}") + private int COMPLETE_PENDING_ACCOUNT_TIMEOUT_IN_MINUTES; @Autowired private CassandraTemplate cassandraTemplate; @@ -60,7 +63,7 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio } Long timeCreated = new Date().getTime(); Long checkMinutes = timeCreated - pendingAccount.getCreationTimestamp(); - if (checkMinutes > COMPLETE_PENDING_ACCOUNT_TIMEOUT) { + if (checkMinutes > COMPLETE_PENDING_ACCOUNT_TIMEOUT_IN_MINUTES * 60 * 1000) { logger.info("Account creation timeout expired."); return null; } diff --git a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java index 511cf5ddb01365dccfcc283df288a22a885864bb..b72c3a495b730edae13f30009d8fa7601a061801 100644 --- a/src/main/java/biz/nynja/account/services/AccountServiceImpl.java +++ b/src/main/java/biz/nynja/account/services/AccountServiceImpl.java @@ -263,6 +263,12 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas logger.info("Updating profile..."); logger.debug("Updating profile...: {}", request); + if ((request.getProfileId() == null) || (request.getProfileId().isEmpty())) { + responseObserver.onNext(UpdateProfileResponse.newBuilder() + .setError(ErrorResponse.newBuilder().setCause(Cause.MISSING_PROFILE_ID)).build()); + responseObserver.onCompleted(); + return; + } if (request.getAuthProvidersList().size() < MIN_NUMBER_OF_AUTH_PROVIDERS_IN_PROFILE) { logger.info("Error updating profile. Check the number of authentication providers."); logger.debug("Error updating profile. Check the number of authentication providers: {}", request); @@ -295,6 +301,12 @@ public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBas logger.info("Updating account..."); logger.debug("Updating account...: {}", request); + if ((request.getAccountId() == null) || (request.getAccountId().isEmpty())) { + responseObserver.onNext(AccountResponse.newBuilder() + .setError(ErrorResponse.newBuilder().setCause(Cause.MISSING_ACCOUNT_ID)).build()); + responseObserver.onCompleted(); + return; + } Cause validationCause = validator.validateUpdateAccountRequest(request); if (validationCause != null) { responseObserver.onNext(AccountResponse.newBuilder() diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 5e70478f6158735260780bbe4ad5fe0d15e8c7cf..c033fac051b7da8fb13fd52379a4fecd455786fa 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -8,4 +8,9 @@ spring: cassandra: keyspace-name: account contact-points: localhost - port: 9042 \ No newline at end of file + port: 9042 + +complete: + pending: + account: + timeout-minutes: 30 \ No newline at end of file diff --git a/src/main/resources/application-production.yml b/src/main/resources/application-production.yml index 89f53d3fb6e9d70f6ef431e68c32992750d36517..30cd816d5ad1b6de3a26015f798d9381dbe7994b 100644 --- a/src/main/resources/application-production.yml +++ b/src/main/resources/application-production.yml @@ -8,4 +8,9 @@ spring: cassandra: keyspace-name: ${CASSANDRA_KEYSPACE:account} contact-points: ${CASSANDRA_CONTACT_POINTS:localhost} - port: ${CASSANDRA_PORT:9042} \ No newline at end of file + port: ${CASSANDRA_PORT:9042} + +complete: + pending: + account: + timeout-minutes: ${COMPLETE_PENDING_ACCOUNT_TIMEOUT_MINUTES:30} \ No newline at end of file diff --git a/src/test/java/biz/nynja/account/services/AccountServiceTests.java b/src/test/java/biz/nynja/account/services/AccountServiceTests.java index 91c6697ce89dba6b9719d363415952327b65d8aa..74240f4311c19c8c05133fa2e8bfa229eb98dbe9 100644 --- a/src/test/java/biz/nynja/account/services/AccountServiceTests.java +++ b/src/test/java/biz/nynja/account/services/AccountServiceTests.java @@ -31,14 +31,20 @@ import biz.nynja.account.grpc.AccountByAccountIdRequest; import biz.nynja.account.grpc.AccountServiceGrpc; import biz.nynja.account.grpc.AccountsByProfileIdRequest; import biz.nynja.account.grpc.AccountsResponse; +import biz.nynja.account.grpc.AuthProviderDetails; import biz.nynja.account.grpc.AuthenticationType; import biz.nynja.account.grpc.ErrorResponse.Cause; +import biz.nynja.account.grpc.UpdateAccountRequest; +import biz.nynja.account.grpc.UpdateProfileRequest; +import biz.nynja.account.grpc.UpdateProfileResponse; import biz.nynja.account.models.Account; import biz.nynja.account.models.AccountByAuthenticationProvider; import biz.nynja.account.repositories.AccountByAuthenticationProviderRepository; import biz.nynja.account.models.AccountByProfileId; +import biz.nynja.account.models.Profile; import biz.nynja.account.repositories.AccountByProfileIdRepository; import biz.nynja.account.repositories.AccountRepository; +import biz.nynja.account.repositories.AccountRepositoryAdditional; import biz.nynja.account.utils.GrpcServerTestBase; import biz.nynja.account.utils.Util; @@ -78,6 +84,14 @@ public class AccountServiceTests extends GrpcServerTestBase { @Qualifier("savedAccount") private Account savedAccount; + @Autowired + @Qualifier("updatedAccount") + private Account updatedAccount; + + @Autowired + @Qualifier("updatedProfile") + private Profile updatedProfile; + @Autowired @Qualifier("savedAccountByProfileId") private AccountByProfileId savedAccountByProfileId; @@ -88,6 +102,9 @@ public class AccountServiceTests extends GrpcServerTestBase { @MockBean private AccountByProfileIdRepository accountByProffileIdRepository; + @MockBean + private AccountRepositoryAdditional accountRepositoryAdditional; + @Test public void testGetAccountByAccountId() throws ExecutionException, InterruptedException { final AccountByAccountIdRequest request = AccountByAccountIdRequest.newBuilder() @@ -278,4 +295,155 @@ public class AccountServiceTests extends GrpcServerTestBase { assertTrue(String.format("Reply should contain cause '%s'", Cause.ACCOUNT_NOT_FOUND), reply.getError().getCause().equals(Cause.ACCOUNT_NOT_FOUND)); } + + @Test + public void testUpdateAccount() throws ExecutionException, InterruptedException { + final UpdateAccountRequest request = UpdateAccountRequest.newBuilder().setAccountId(Util.ACCOUNT_ID.toString()) + .setAccountMark(Util.UPDATED_ACCOUNT_MARK).setFirstName(Util.FIRST_NAME).build(); + + given(accountRepositoryAdditional.updateAccount(request)).willReturn(updatedAccount); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountResponse reply = accountServiceBlockingStub.updateAccount(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain account mark '%s'", Util.UPDATED_ACCOUNT_MARK), + reply.getAccountDetails().getAccountMark().equals(Util.UPDATED_ACCOUNT_MARK)); + + } + + @Test + public void testUpdateAccountMissingFirstName() { + final UpdateAccountRequest request = UpdateAccountRequest.newBuilder().setAccountId(Util.ACCOUNT_ID.toString()) + .setAccountMark(Util.UPDATED_ACCOUNT_MARK).build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountResponse reply = accountServiceBlockingStub.updateAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_FIRST_NAME), + reply.getError().getCause().equals(Cause.MISSING_FIRST_NAME)); + } + + @Test + public void testUpdateAccountMissingAccountId() { + final UpdateAccountRequest request = UpdateAccountRequest.newBuilder().setFirstName(Util.FIRST_NAME) + .setAccountMark(Util.UPDATED_ACCOUNT_MARK).build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountResponse reply = accountServiceBlockingStub.updateAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_ACCOUNT_ID), + reply.getError().getCause().equals(Cause.MISSING_ACCOUNT_ID)); + } + + @Test + public void testUpdateAccountAccountIdNotFound() { + final UpdateAccountRequest request = UpdateAccountRequest.newBuilder() + .setAccountId(Util.ACCOUNT_ID_NOT_FOUND.toString()).setFirstName(Util.FIRST_NAME) + .setAccountMark(Util.UPDATED_ACCOUNT_MARK).build(); + + given(accountRepositoryAdditional.updateAccount(request)).willReturn(null); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountResponse reply = accountServiceBlockingStub.updateAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ERROR_UPDATING_ACCOUNT), + reply.getError().getCause().equals(Cause.ERROR_UPDATING_ACCOUNT)); + } + + @Test + public void testUpdateAccountUsernameAlreadyUsed() { + final UpdateAccountRequest request = UpdateAccountRequest.newBuilder().setAccountId(Util.ACCOUNT_ID.toString()) + .setAccountMark(Util.UPDATED_ACCOUNT_MARK).setFirstName(Util.FIRST_NAME).setUsername(Util.USERNAME) + .build(); + + given(accountRepositoryAdditional.foundExistingNotOwnUsername(UUID.fromString(request.getAccountId()), + request.getUsername())).willReturn(true); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final AccountResponse reply = accountServiceBlockingStub.updateAccount(request); + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.USERNAME_ALREADY_USED), + reply.getError().getCause().equals(Cause.USERNAME_ALREADY_USED)); + } + + @Test + public void testUpdateProfileAddBackupAuthProvider() throws ExecutionException, InterruptedException { + + final UpdateProfileRequest request = UpdateProfileRequest.newBuilder().setProfileId(Util.PROFILE_ID.toString()) + .addAuthProviders(AuthProviderDetails.newBuilder().setAuthenticationProvider(Util.PHONE_NUMBER) + .setAuthenticationType(AuthenticationType.PHONE).build()) + .setBackupAuthProvider(AuthProviderDetails.newBuilder().setAuthenticationProvider(Util.PHONE_NUMBER) + .setAuthenticationType(AuthenticationType.PHONE).build()) + .build(); + + given(accountRepositoryAdditional.updateProfile(request)).willReturn(updatedProfile); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final UpdateProfileResponse reply = accountServiceBlockingStub.updateProfile(request); + + assertNotNull("Reply should not be null", reply); + assertTrue( + String.format("Reply should contain backup auth provider type '%s'", + request.getBackupAuthProvider().getAuthenticationType().name()), + reply.getProfileResponse().getBackupAuthProvider().getAuthenticationType().toString() + .equals(AuthenticationType.PHONE.name())); + assertTrue( + String.format("Reply should contain backup auth provider '%s'", + request.getBackupAuthProvider().getAuthenticationProvider()), + reply.getProfileResponse().getBackupAuthProvider().getAuthenticationProvider() + .equals(Util.PHONE_NUMBER)); + } + + @Test + public void testUpdateProfileMissingProfileId() throws ExecutionException, InterruptedException { + + final UpdateProfileRequest request = UpdateProfileRequest.newBuilder() + .addAuthProviders(AuthProviderDetails.newBuilder().setAuthenticationProvider(Util.PHONE_NUMBER) + .setAuthenticationType(AuthenticationType.PHONE).build()) + .setBackupAuthProvider(AuthProviderDetails.newBuilder().setAuthenticationProvider(Util.PHONE_NUMBER) + .setAuthenticationType(AuthenticationType.PHONE).build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final UpdateProfileResponse reply = accountServiceBlockingStub.updateProfile(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_PROFILE_ID), + reply.getError().getCause().equals(Cause.MISSING_PROFILE_ID)); + + } + + @Test + public void testUpdateProfileNoAuthProviders() throws ExecutionException, InterruptedException { + + final UpdateProfileRequest request = UpdateProfileRequest.newBuilder().setProfileId(Util.PROFILE_ID.toString()) + .setBackupAuthProvider(AuthProviderDetails.newBuilder().setAuthenticationProvider(Util.PHONE_NUMBER) + .setAuthenticationType(AuthenticationType.PHONE).build()) + .build(); + + final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final UpdateProfileResponse reply = accountServiceBlockingStub.updateProfile(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.ERROR_UPDATING_PROFILE), + reply.getError().getCause().equals(Cause.ERROR_UPDATING_PROFILE)); + + } } diff --git a/src/test/java/biz/nynja/account/utils/Util.java b/src/test/java/biz/nynja/account/utils/Util.java index 6c976e0c9a40b2cfa1bbf0b63147d1256e517a5d..f4e58fe4860ff19f900309bd1b3fa38665e6f06c 100644 --- a/src/test/java/biz/nynja/account/utils/Util.java +++ b/src/test/java/biz/nynja/account/utils/Util.java @@ -17,6 +17,7 @@ import biz.nynja.account.models.Account; import biz.nynja.account.models.AccountByAuthenticationProvider; import biz.nynja.account.models.AccountByProfileId; import biz.nynja.account.models.AuthenticationProvider; +import biz.nynja.account.models.Profile; /** * Unit tests variables, beans and help methods. @@ -27,7 +28,9 @@ public class Util { public static final UUID PROFILE_ID = UUID.fromString("12352345-e89b-43d3-d156-456732452200"); public static final UUID ACCOUNT_ID = UUID.fromString("44532732-12b3-132d-e156-223732152200"); + public static final UUID ACCOUNT_ID_NOT_FOUND = UUID.fromString("44532732-12b3-132d-e156-223732152202"); public static final String ACCOUNT_MARK = "AccountMark"; + public static final String UPDATED_ACCOUNT_MARK = "PRIVATE"; public static final String AUTHENTICATION_PROVIDER = "Provider"; public static final String AUTHENTICATION_PROVIDER_TYPE = "ProviderType"; public static final ByteBuffer AVATAR = ByteBuffer.allocate(42); @@ -97,6 +100,29 @@ public class Util { return account; } + @Bean + public Account updatedAccount() { + Account account = new Account(); + account.setAccountId(ACCOUNT_ID); + account.setProfileId(PROFILE_ID); + account.setUsername(USERNAME); + account.setFirstName(FIRST_NAME); + account.setLastName(LAST_NAME); + account.setAccountMark(UPDATED_ACCOUNT_MARK); + return account; + } + + @Bean + public Profile updatedProfile() { + Profile profile = new Profile(); + profile.setProfileId(PROFILE_ID); + Set authProviders = new HashSet(); + authProviders.add(AuthenticationProvider.createAuthenticationProviderFromStrings(PHONE_TYPE, PHONE_NUMBER)); + profile.setAuthenticationProviders(authProviders); + profile.setBackupAuthenticationProvider(AuthenticationProvider.createAuthenticationProviderFromStrings(PHONE_TYPE, PHONE_NUMBER)); + return profile; + } + @Bean public AccountByAuthenticationProvider accountByPhone() { AccountByAuthenticationProvider accountByPhone = new AccountByAuthenticationProvider();