Java tutorial
/******************************************************************************* * Copyright (c) 2015, 2017 Stichting Yona Foundation This Source Code Form is subject to the terms of the Mozilla Public License, * v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. *******************************************************************************/ package nu.yona.server.subscriptions.service; import java.io.UnsupportedEncodingException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.function.BiFunction; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.mail.internet.InternetAddress; import org.apache.commons.lang.NotImplementedException; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import nu.yona.server.Translator; import nu.yona.server.crypto.seckey.CryptoSession; import nu.yona.server.email.EmailService; import nu.yona.server.exceptions.EmailException; import nu.yona.server.messaging.entities.BuddyMessage.BuddyInfoParameters; import nu.yona.server.messaging.entities.Message; import nu.yona.server.messaging.entities.MessageDestination; import nu.yona.server.messaging.service.BuddyMessageDto; import nu.yona.server.messaging.service.MessageActionDto; import nu.yona.server.messaging.service.MessageDestinationDto; import nu.yona.server.messaging.service.MessageService; import nu.yona.server.properties.YonaProperties; import nu.yona.server.sms.SmsService; import nu.yona.server.sms.SmsTemplate; import nu.yona.server.subscriptions.entities.Buddy; import nu.yona.server.subscriptions.entities.BuddyAnonymized; import nu.yona.server.subscriptions.entities.BuddyAnonymized.Status; import nu.yona.server.subscriptions.entities.BuddyAnonymizedRepository; import nu.yona.server.subscriptions.entities.BuddyConnectRequestMessage; import nu.yona.server.subscriptions.entities.BuddyConnectResponseMessage; import nu.yona.server.subscriptions.entities.BuddyDisconnectMessage; import nu.yona.server.subscriptions.entities.BuddyInfoChangeMessage; import nu.yona.server.subscriptions.entities.User; import nu.yona.server.subscriptions.entities.UserAnonymized; import nu.yona.server.subscriptions.service.UserService.UserPurpose; import nu.yona.server.util.TransactionHelper; @Service @Transactional public class BuddyService { private static final Logger logger = LoggerFactory.getLogger(BuddyService.class); @Autowired private UserService userService; @Autowired private MessageService messageService; @Autowired private EmailService emailService; @Autowired private SmsService smsService; @Autowired private Translator translator; @Autowired private YonaProperties properties; @Autowired private UserAnonymizedService userAnonymizedService; @Autowired(required = false) private BuddyAnonymizedRepository buddyAnonymizedRepository; @Autowired private BuddyConnectResponseMessageDto.Manager connectResponseMessageHandler; @Autowired private TransactionHelper transactionHelper; public enum DropBuddyReason { USER_ACCOUNT_DELETED, USER_REMOVED_BUDDY } public BuddyDto getBuddy(UUID buddyId) { return getBuddy(getEntityById(buddyId)); } private BuddyDto getBuddy(Buddy buddyEntity) { BuddyDto result = BuddyDto.createInstance(buddyEntity); if (canIncludePrivateData(buddyEntity)) { UUID buddyUserAnonymizedId = getUserAnonymizedIdForBuddy(buddyEntity); result.setGoals(userAnonymizedService.getUserAnonymized(buddyUserAnonymizedId).getGoals().stream() .collect(Collectors.toSet())); } return result; } static boolean canIncludePrivateData(Buddy buddyEntity) { return (buddyEntity.getReceivingStatus() == Status.ACCEPTED) || (buddyEntity.getSendingStatus() == Status.ACCEPTED); } public Set<BuddyDto> getBuddiesOfUser(UUID forUserId) { UserDto user = userService.getPrivateUser(forUserId); return user.getPrivateData().getBuddies(); } public Set<BuddyDto> getBuddiesOfUserThatAcceptedSending(UUID forUserId) { return getBuddiesOfUser(forUserId).stream().filter(b -> b.getSendingStatus() == Status.ACCEPTED) .collect(Collectors.toSet()); } @Transactional(propagation = Propagation.NEVER) // It explicitly creates transactions when needed. public BuddyDto addBuddyToRequestingUser(UUID idOfRequestingUser, BuddyDto buddy, BiFunction<UUID, String, String> inviteUrlGetter) { UserDto requestingUser = userService.getPrivateUser(idOfRequestingUser); assertMobileNumberOfRequestingUserConfirmed(idOfRequestingUser); assertValidBuddy(requestingUser, buddy); boolean buddyUserExists = buddyUserExists(buddy); if (!buddyUserExists) { createAndInviteBuddyUser(idOfRequestingUser, buddy, inviteUrlGetter); } BuddyDto savedBuddy = transactionHelper .executeInNewTransaction(() -> handleBuddyRequestForExistingUser(idOfRequestingUser, buddy)); logger.info( "User with mobile number '{}' and ID '{}' sent buddy connect message to {} user with mobile number '{}' and ID '{}' as buddy", requestingUser.getMobileNumber(), requestingUser.getId(), (buddyUserExists) ? "new" : "existing", buddy.getUser().getMobileNumber(), buddy.getUser().getId()); return savedBuddy; } private void createAndInviteBuddyUser(UUID idOfRequestingUser, BuddyDto buddy, BiFunction<UUID, String, String> inviteUrlGetter) { String tempPassword = getTempPassword(); // To ensure the data of the new user is encrypted with the temp password, the user is created in a separate transaction // and crypto session. UUID savedUserId; try (CryptoSession cryptoSession = CryptoSession.start(tempPassword)) { savedUserId = transactionHelper.executeInNewTransaction( () -> userService.addUserCreatedOnBuddyRequest(buddy.getUser()).getId()); } String inviteUrl = inviteUrlGetter.apply(savedUserId, tempPassword); UserDto requestingUser = userService.getPrivateUser(idOfRequestingUser); sendInvitationMessage(requestingUser, buddy, inviteUrl); } private void assertMobileNumberOfRequestingUserConfirmed(UUID idOfRequestingUser) { UserDto requestingUser = userService.getPrivateUser(idOfRequestingUser); requestingUser.assertMobileNumberConfirmed(); } private void assertValidBuddy(UserDto requestingUser, BuddyDto buddy) { userService.assertValidUserFields(buddy.getUser(), UserPurpose.BUDDY); if (buddy.getSendingStatus() != Status.REQUESTED || buddy.getReceivingStatus() != Status.REQUESTED) { throw BuddyServiceException.onlyTwoWayBuddiesAllowed(); } String buddyMobileNumber = buddy.getUser().getMobileNumber(); if (requestingUser.getMobileNumber().equals(buddyMobileNumber)) { throw BuddyServiceException.cannotInviteSelf(); } if (requestingUser.getPrivateData().getBuddies().stream().map(b -> b.getUser().getMobileNumber()) .anyMatch(m -> m.equals(buddyMobileNumber))) { throw BuddyServiceException.cannotInviteExistingBuddy(); } } @Transactional public BuddyDto addBuddyToAcceptingUser(UserDto acceptingUser, UUID buddyUserId, String buddyNickName, UUID buddyUserAnonymizedId, boolean isRequestingSending, boolean isRequestingReceiving) { if (acceptingUser == null) { throw BuddyServiceException.acceptingUserIsNull(); } acceptingUser.assertMobileNumberConfirmed(); Buddy buddy = Buddy.createInstance(buddyUserId, buddyNickName, isRequestingSending ? Status.ACCEPTED : Status.NOT_REQUESTED, isRequestingReceiving ? Status.ACCEPTED : Status.NOT_REQUESTED); buddy.setUserAnonymizedId(buddyUserAnonymizedId); BuddyDto buddyDto = BuddyDto.createInstance(Buddy.getRepository().save(buddy)); userService.addBuddy(acceptingUser, buddyDto); return buddyDto; } @Transactional public void removeBuddyAfterConnectRejection(UUID idOfRequestingUser, UUID buddyId) { User user = userService.getValidatedUserbyId(idOfRequestingUser); Buddy buddy = Buddy.getRepository().findOne(buddyId); if (buddy != null) { removeBuddy(user, buddy); } // else: buddy already removed, probably in response to removing the user } private void removeBuddy(User user, Buddy buddy) { user.removeBuddy(buddy); User.getRepository().save(user); Buddy.getRepository().delete(buddy); UserAnonymized userAnonymizedEntity = user.getAnonymized(); userAnonymizedEntity.removeBuddyAnonymized(buddy.getBuddyAnonymized()); userAnonymizedService.updateUserAnonymized(userAnonymizedEntity); } @Transactional public void removeBuddy(UUID idOfRequestingUser, UUID buddyId, Optional<String> message) { User user = userService.getValidatedUserbyId(idOfRequestingUser); Buddy buddy = getEntityById(buddyId); if (buddy.getSendingStatus() == Status.REQUESTED || buddy.getReceivingStatus() == Status.REQUESTED) { // The buddy might already have responded while the response wasn't processed yet processPossiblePendingBuddyResponseMessage(user, buddy); } removeMessagesSentByBuddy(user, buddy); removeBuddyInfoForBuddy(user, buddy, message, DropBuddyReason.USER_REMOVED_BUDDY); removeBuddy(user, buddy); User buddyUser = buddy.getUser(); if (buddyUser == null) { logger.info("User with mobile number '{}' and ID '{}' removed buddy whose account is already removed", user.getMobileNumber(), user.getId()); } else { logger.info( "User with mobile number '{}' and ID '{}' removed buddy with mobile number '{}' and ID '{}' as buddy", user.getMobileNumber(), user.getId(), buddyUser.getMobileNumber(), buddyUser.getId()); } } private void processPossiblePendingBuddyResponseMessage(User userEntity, Buddy buddy) { int page = 0; final int pageSize = 50; Page<Message> messagePage; boolean messageFound = false; UserDto user = userService.createUserDtoWithPrivateData(userEntity); do { messagePage = messageService.getReceivedMessageEntitiesSinceDate(user.getId(), buddy.getLastStatusChangeTime(), new PageRequest(page++, pageSize)); messageFound = processPossiblePendingBuddyResponseMessage(user, buddy, messagePage); } while (!messageFound && messagePage.getNumberOfElements() == pageSize); } private boolean processPossiblePendingBuddyResponseMessage(UserDto user, Buddy buddy, Page<Message> messagePage) { Stream<BuddyConnectResponseMessage> buddyConnectResponseMessages = messagePage.getContent().stream() .filter(m -> m instanceof BuddyConnectResponseMessage).map(m -> (BuddyConnectResponseMessage) m); Stream<BuddyConnectResponseMessage> messagesFromBuddy = buddyConnectResponseMessages .filter(m -> buddy.getUserId().equals(getUserId(m).orElse(null))); Optional<BuddyConnectResponseMessage> messageToBeProcessed = messagesFromBuddy.filter(m -> !m.isProcessed()) .findFirst(); messageToBeProcessed.ifPresent(m -> connectResponseMessageHandler.handleAction_Process(user, m, new MessageActionDto(Collections.emptyMap()))); return messageToBeProcessed.isPresent(); } public void processPossiblePendingBuddyResponseMessages(User userEntity) { getBuddyEntitiesOfUser(userEntity.getId()).stream() .forEach(b -> processPossiblePendingBuddyResponseMessage(userEntity, b)); } private Optional<UUID> getUserId(BuddyConnectResponseMessage message) { return message.getSenderUser().map(User::getId); } @Transactional void removeBuddyInfoForBuddy(User requestingUser, Buddy requestingUserBuddy, Optional<String> message, DropBuddyReason reason) { if (requestingUserBuddy == null) { throw BuddyServiceException.requestingUserBuddyIsNull(); } if (requestingUserBuddy.getUser() == null) { // buddy account was removed in the meantime; nothing to do return; } removeNamedMessagesSentByUser(requestingUserBuddy.getUser(), requestingUser.getUserAnonymizedId()); if (requestingUserBuddy.getSendingStatus() == Status.ACCEPTED || requestingUserBuddy.getReceivingStatus() == Status.ACCEPTED) { UUID buddyUserAnonymizedId = getUserAnonymizedIdForBuddy(requestingUserBuddy); UserAnonymizedDto buddyUserAnonymized = userAnonymizedService.getUserAnonymized(buddyUserAnonymizedId); disconnectBuddyIfConnected(buddyUserAnonymized, requestingUser.getUserAnonymizedId()); removeAnonymousMessagesSentByUser(buddyUserAnonymized, requestingUser.getUserAnonymizedId()); sendDropBuddyMessage(requestingUser, requestingUserBuddy, message, reason); } } @Transactional void removeBuddyInfoForRemovedUser(User user, Buddy buddy) { if (buddy.getSendingStatus() == Status.ACCEPTED || buddy.getReceivingStatus() == Status.ACCEPTED) { // Buddy request was accepted removeMessagesSentByBuddy(user, buddy); removeMessagesSentByUserToBuddy(user, buddy); // Send message to "self", to notify the user about the removed buddy user UUID buddyUserAnonymizedId = getUserAnonymizedIdForBuddy(buddy); sendDropBuddyMessage(BuddyInfoParameters.createInstance(buddy, buddyUserAnonymizedId), Optional.empty(), DropBuddyReason.USER_ACCOUNT_DELETED, user.getNamedMessageDestination()); } else if (buddy.getSendingStatus() != Status.REJECTED && buddy.getReceivingStatus() != Status.REJECTED) { // Buddy request was not accepted or rejected yet // Send message to "self", as if the requested user declined the buddy request UUID buddyUserAnonymizedId = buddy.getUserAnonymizedId().orElse(null); // sendBuddyConnectResponseMessage(BuddyInfoParameters.createInstance(buddy, buddyUserAnonymizedId), user.getUserAnonymizedId(), buddy.getId(), Status.REJECTED, getDropBuddyMessage(DropBuddyReason.USER_ACCOUNT_DELETED, Optional.empty())); } removeBuddy(user, buddy); } private UUID getUserAnonymizedIdForBuddy(Buddy buddy) { return buddy.getUserAnonymizedId().orElseThrow(() -> new IllegalStateException( "Should have user anonymized ID when buddy relationship is established")); } @Transactional public void removeBuddyAfterBuddyRemovedConnection(UUID idOfRequestingUser, UUID relatedUserId) { User user = userService.getValidatedUserbyId(idOfRequestingUser); user.getBuddies().stream().filter(b -> b.getUserId().equals(relatedUserId)).findFirst() .ifPresent(b -> removeBuddy(user, b)); } public void setBuddyAcceptedWithSecretUserInfo(UUID actingUserAnonymizedId, UUID buddyId, UUID userAnonymizedId, String nickname) { Buddy buddy = Buddy.getRepository().findOne(buddyId); if (buddy == null) { throw BuddyNotFoundException.notFound(buddyId); } if (buddy.getSendingStatus() == Status.REQUESTED) { buddy.setSendingStatus(Status.ACCEPTED); } if (buddy.getReceivingStatus() == Status.REQUESTED) { buddy.setReceivingStatus(Status.ACCEPTED); } buddy.setUserAnonymizedId(userAnonymizedId); buddy.setNickName(nickname); Buddy.getRepository().save(buddy); userAnonymizedService .updateUserAnonymized(userAnonymizedService.getUserAnonymizedEntity(actingUserAnonymizedId)); } Set<BuddyDto> getBuddyDtos(Set<Buddy> buddyEntities) { loadAllBuddiesAnonymizedAtOnce(buddyEntities); loadAllUsersAnonymizedAtOnce(buddyEntities); return buddyEntities.stream().map(this::getBuddy).collect(Collectors.toSet()); } private void loadAllUsersAnonymizedAtOnce(Set<Buddy> buddyEntities) { UserAnonymized.getRepository().findAll(buddyEntities.stream().map(Buddy::getUserAnonymizedId) .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList())); } private void loadAllBuddiesAnonymizedAtOnce(Set<Buddy> buddyEntities) { buddyAnonymizedRepository .findAll(buddyEntities.stream().map(Buddy::getBuddyAnonymizedId).collect(Collectors.toList())); } public Set<MessageDestinationDto> getBuddyDestinations(UserAnonymizedDto user) { return user.getBuddiesAnonymized().stream().filter(ba -> ba.getSendingStatus() == Status.ACCEPTED) .map(BuddyAnonymizedDto::getUserAnonymizedId).filter(Optional::isPresent).map(Optional::get) .map(buaid -> userAnonymizedService.getUserAnonymized(buaid).getAnonymousDestination()) .collect(Collectors.toSet()).stream().collect(Collectors.toSet()); } public Set<MessageDestination> getBuddyDestinations(UserAnonymized user) { return user.getBuddiesAnonymized().stream().filter(ba -> ba.getSendingStatus() == Status.ACCEPTED) .map(BuddyAnonymized::getUserAnonymized).map(UserAnonymized::getAnonymousDestination) .collect(Collectors.toSet()); } private Set<Buddy> getBuddyEntitiesOfUser(UUID forUserId) { UserDto user = userService.getPrivateUser(forUserId); return getBuddyEntities(user.getPrivateData().getBuddyIds()); } private Set<Buddy> getBuddyEntities(Set<UUID> buddyIds) { return buddyIds.stream().map(this::getEntityById).collect(Collectors.toSet()); } private void removeMessagesSentByBuddy(User user, Buddy buddy) { Optional<UUID> buddyUserAnonymizedId = buddy.getUserAnonymizedId(); if (!buddyUserAnonymizedId.isPresent()) { return; } removeNamedMessagesSentByUser(user, buddyUserAnonymizedId.get()); UserAnonymizedDto userAnonymized = userAnonymizedService.getUserAnonymized(user.getUserAnonymizedId()); removeAnonymousMessagesSentByUser(userAnonymized, buddyUserAnonymizedId.get()); } private void removeMessagesSentByUserToBuddy(User user, Buddy buddy) { UUID buddyUserAnonymizedId = getUserAnonymizedIdForBuddy(buddy); UserAnonymizedDto buddyUserAnonymized = userAnonymizedService.getUserAnonymized(buddyUserAnonymizedId); removeAnonymousMessagesSentByUser(buddyUserAnonymized, user.getUserAnonymizedId()); // We are not removing the named messages because we don't have User entity anymore // (this method is being called from removeBuddyInfoForRemovedUser) and thus we don't know the named destination. // Given that the user and the named destination are both removed, this is not causing any issues. } private void sendDropBuddyMessage(User requestingUser, Buddy requestingUserBuddy, Optional<String> message, DropBuddyReason reason) { sendDropBuddyMessage(BuddyInfoParameters.createInstance(requestingUser), message, reason, requestingUserBuddy.getUser().getNamedMessageDestination()); } private void sendDropBuddyMessage(BuddyInfoParameters buddyInfoParameters, Optional<String> message, DropBuddyReason reason, MessageDestination messageDestination) { MessageDestinationDto messageDestinationDto = MessageDestinationDto.createInstance(messageDestination); messageService.sendMessageAndFlushToDatabase(BuddyDisconnectMessage.createInstance(buddyInfoParameters, getDropBuddyMessage(reason, message), reason), messageDestinationDto); } void sendBuddyConnectResponseMessage(BuddyInfoParameters buddyInfoParameters, UUID receiverUserAnonymizedId, UUID buddyId, Status status, String responseMessage) { MessageDestinationDto messageDestination = userAnonymizedService.getUserAnonymized(receiverUserAnonymizedId) .getAnonymousDestination(); assert messageDestination != null; messageService.sendMessageAndFlushToDatabase( BuddyConnectResponseMessage.createInstance(buddyInfoParameters, responseMessage, buddyId, status), messageDestination); } private void disconnectBuddyIfConnected(UserAnonymizedDto buddyUserAnonymized, UUID userAnonymizedId) { Optional<BuddyAnonymizedDto> buddyAnonymized = buddyUserAnonymized.getBuddyAnonymized(userAnonymizedId); buddyAnonymized.map(ba -> buddyAnonymizedRepository.findOne(ba.getId())).ifPresent(bae -> { bae.setDisconnected(); userAnonymizedService.updateUserAnonymized(buddyUserAnonymized.getId()); // Notice: last status change time will not be set, as we are not able to reach the Buddy entity from here // Buddy will be removed anyway the first time the other user logs in }); // Else: user who requested buddy relationship didn't process the accept message yet } private void removeNamedMessagesSentByUser(User receivingUser, UUID sentByUserAnonymizedId) { MessageDestination namedMessageDestination = receivingUser.getNamedMessageDestination(); messageService.removeMessagesFromUser(MessageDestinationDto.createInstance(namedMessageDestination), sentByUserAnonymizedId); } private void removeAnonymousMessagesSentByUser(UserAnonymizedDto receivingUserAnonymized, UUID sentByUserAnonymizedId) { MessageDestinationDto anonymousMessageDestination = receivingUserAnonymized.getAnonymousDestination(); messageService.removeMessagesFromUser(anonymousMessageDestination, sentByUserAnonymizedId); } private String getDropBuddyMessage(DropBuddyReason reason, Optional<String> message) { if (message.isPresent()) { return message.get(); } switch (reason) { case USER_ACCOUNT_DELETED: return translator.getLocalizedMessage("message.user.account.deleted"); case USER_REMOVED_BUDDY: return translator.getLocalizedMessage("message.user.removed.buddy"); default: throw new NotImplementedException(); } } private void sendInvitationMessage(UserDto requestingUser, BuddyDto buddy, String inviteUrl) { try { String subjectTemplateName = "buddy-invitation-subject"; String bodyTemplateName = "buddy-invitation-body"; String requestingUserName = StringUtils .join(new Object[] { requestingUser.getFirstName(), requestingUser.getLastName() }, " "); String requestingUserMobileNumber = requestingUser.getMobileNumber(); String requestingUserNickname = requestingUser.getPrivateData().getNickname(); String buddyName = StringUtils .join(new Object[] { buddy.getUser().getFirstName(), buddy.getUser().getLastName() }, " "); String buddyEmailAddress = buddy.getUser().getEmailAddress(); String personalInvitationMessage = buddy.getPersonalInvitationMessage(); String buddyMobileNumber = buddy.getUser().getMobileNumber(); Map<String, Object> templateParams = new HashMap<>(); templateParams.put("inviteUrl", inviteUrl); templateParams.put("requestingUserFirstName", requestingUser.getFirstName()); templateParams.put("requestingUserLastName", requestingUser.getLastName()); templateParams.put("requestingUserMobileNumber", requestingUserMobileNumber); templateParams.put("requestingUserNickname", requestingUserNickname); templateParams.put("buddyFirstName", buddy.getUser().getFirstName()); templateParams.put("buddyLastName", buddy.getUser().getLastName()); templateParams.put("personalInvitationMessage", personalInvitationMessage); templateParams.put("emailAddress", buddyEmailAddress); emailService.sendEmail(requestingUserName, new InternetAddress(buddyEmailAddress, buddyName), subjectTemplateName, bodyTemplateName, templateParams); smsService.send(buddyMobileNumber, SmsTemplate.BUDDY_INVITE, templateParams); } catch (UnsupportedEncodingException e) { throw EmailException.emailSendingFailed(e); } } private String getTempPassword() { return (properties.getEmail().isEnabled()) ? userService.generatePassword() : "abcd"; } private BuddyDto handleBuddyRequestForExistingUser(UUID idOfRequestingUser, BuddyDto buddy) { UserDto requestingUser = userService.getPrivateUser(idOfRequestingUser); User buddyUserEntity = UserService.findUserByMobileNumber(buddy.getUser().getMobileNumber()); buddy.getUser().setUserId(buddyUserEntity.getId()); Buddy buddyEntity = buddy.createBuddyEntity(translator); Buddy savedBuddyEntity = Buddy.getRepository().save(buddyEntity); BuddyDto savedBuddy = BuddyDto.createInstance(savedBuddyEntity); userService.addBuddy(requestingUser, savedBuddy); boolean isRequestingSending = buddy.getReceivingStatus() == Status.REQUESTED; boolean isRequestingReceiving = buddy.getSendingStatus() == Status.REQUESTED; MessageDestination messageDestination = buddyUserEntity.getNamedMessageDestination(); messageService.sendMessageAndFlushToDatabase(BuddyConnectRequestMessage.createInstance( BuddyMessageDto.createBuddyInfoParametersInstance(requestingUser), buddy.getPersonalInvitationMessage(), savedBuddyEntity.getId(), isRequestingSending, isRequestingReceiving), MessageDestinationDto.createInstance(messageDestination)); return savedBuddy; } private Buddy getEntityById(UUID id) { Buddy entity = Buddy.getRepository().findOne(id); if (entity == null) { throw BuddyNotFoundException.notFound(id); } return entity; } private boolean buddyUserExists(BuddyDto buddy) { try { if (buddy.getUser() == null) { throw UserServiceException.missingOrNullUser(); } UserService.findUserByMobileNumber(buddy.getUser().getMobileNumber()); return true; } catch (UserServiceException e) { return false; } } public Optional<BuddyDto> getBuddyOfUserByUserAnonymizedId(UserPrivateDto user, UUID userAnonymizedId) { Set<BuddyDto> buddies = user.getBuddies(); for (BuddyDto buddy : buddies) { if (buddy.getUserAnonymizedId().filter(id -> id.equals(userAnonymizedId)).isPresent()) { return Optional.of(buddy); } } return Optional.empty(); } @Transactional void broadcastUserInfoChangeToBuddies(User updatedUserEntity, UserDto originalUser) { messageService.broadcastMessageToBuddies( UserAnonymizedDto.createInstance(updatedUserEntity.getAnonymized()), () -> BuddyInfoChangeMessage.createInstance( BuddyInfoParameters.createInstance(updatedUserEntity, originalUser.getPrivateData().getNickname()), getUserInfoChangeMessage(), updatedUserEntity.getNickname(), updatedUserEntity.getUserPhotoId())); } private String getUserInfoChangeMessage() { return translator.getLocalizedMessage("message.buddy.user.info.changed"); } @Transactional public void updateBuddyUserInfo(UUID idOfRequestingUser, UUID relatedUserAnonymizedId, String buddyNickname, Optional<UUID> buddyUserPhotoId) { User user = userService.getValidatedUserbyId(idOfRequestingUser); Buddy buddy = user.getBuddyByUserAnonymizedId(relatedUserAnonymizedId); buddy.setNickName(buddyNickname); buddy.setUserPhotoId(buddyUserPhotoId); Buddy.getRepository().save(buddy); } }