diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2023-05-15 19:49:00 -0500 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-06-01 20:02:59 +0000 |
commit | 46effb33d74b5b37f097288d6bac6343a2d06a8c (patch) | |
tree | d97ed78ac7d0413214353667dcf2424101f79646 /server/sonar-server-common | |
parent | 9bc77e5b117af186e37aff6e22a0ed6da96d5ae5 (diff) | |
download | sonarqube-46effb33d74b5b37f097288d6bac6343a2d06a8c.tar.gz sonarqube-46effb33d74b5b37f097288d6bac6343a2d06a8c.zip |
SONAR-18856 Refactor favorites and properties
Diffstat (limited to 'server/sonar-server-common')
5 files changed, 25 insertions, 234 deletions
diff --git a/server/sonar-server-common/src/it/java/org/sonar/server/favorite/FavoriteUpdaterIT.java b/server/sonar-server-common/src/it/java/org/sonar/server/favorite/FavoriteUpdaterIT.java index 82a496a830c..2a69e111bf7 100644 --- a/server/sonar-server-common/src/it/java/org/sonar/server/favorite/FavoriteUpdaterIT.java +++ b/server/sonar-server-common/src/it/java/org/sonar/server/favorite/FavoriteUpdaterIT.java @@ -26,6 +26,8 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; +import org.sonar.db.entity.EntityDto; +import org.sonar.db.project.ProjectDto; import org.sonar.db.property.PropertyQuery; import org.sonar.db.user.UserDto; @@ -44,7 +46,7 @@ public class FavoriteUpdaterIT { @Test public void put_favorite() { - ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto project = db.components().insertPrivateProject().getProjectDto(); UserDto user = db.users().insertUser(); assertNoFavorite(project, user); @@ -55,23 +57,23 @@ public class FavoriteUpdaterIT { @Test public void do_nothing_when_no_user() { - ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto project = db.components().insertPrivateProject().getProjectDto(); underTest.add(dbSession, project, null, null,true); assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder() - .setComponentUuid(project.uuid()) + .setComponentUuid(project.getUuid()) .build(), dbSession)).isEmpty(); } @Test public void do_not_add_favorite_when_already_100_favorite_projects() { UserDto user = db.users().insertUser(); - IntStream.rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getMainBranchComponent(), user.getUuid(), user.getName())); + IntStream.rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getProjectDto(), user.getUuid(), user.getName())); assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder() .setUserUuid(user.getUuid()) .build(), dbSession)).hasSize(100); - ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto project = db.components().insertPrivateProject().getProjectDto(); underTest.add(dbSession, project, user.getUuid(), user.getLogin(), false); @@ -83,12 +85,12 @@ public class FavoriteUpdaterIT { @Test public void do_not_add_favorite_when_already_100_favorite_portfolios() { UserDto user = db.users().insertUser(); - IntStream.rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getMainBranchComponent(), + IntStream.rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getProjectDto(), user.getUuid(), user.getLogin())); assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder() .setUserUuid(user.getUuid()) .build(), dbSession)).hasSize(100); - ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto project = db.components().insertPrivateProject().getProjectDto(); underTest.add(dbSession, project, user.getUuid(), user.getLogin(), false); @@ -100,9 +102,9 @@ public class FavoriteUpdaterIT { @Test public void fail_when_more_than_100_projects_favorites() { UserDto user = db.users().insertUser(); - IntStream.rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getMainBranchComponent(), + IntStream.rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getProjectDto(), user.getUuid(), user.getLogin())); - ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto project = db.components().insertPrivateProject().getProjectDto(); assertThatThrownBy(() -> underTest.add(dbSession, project, user.getUuid(), user.getLogin(), true)) .isInstanceOf(IllegalArgumentException.class) @@ -111,27 +113,27 @@ public class FavoriteUpdaterIT { @Test public void fail_when_adding_existing_favorite() { - ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto project = db.components().insertPrivateProject().getProjectDto(); UserDto user = db.users().insertUser(); underTest.add(dbSession, project, user.getUuid(), user.getLogin(), true); assertFavorite(project, user); assertThatThrownBy(() -> underTest.add(dbSession, project, user.getUuid(), user.getLogin(), true)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage(String.format("Component '%s' (uuid: %s) is already a favorite", project.getKey(), project.uuid())); + .hasMessage(String.format("Component '%s' (uuid: %s) is already a favorite", project.getKey(), project.getUuid())); } - private void assertFavorite(ComponentDto project, UserDto user) { + private void assertFavorite(EntityDto entity, UserDto user) { assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder() .setUserUuid(user.getUuid()) - .setComponentUuid(project.uuid()) + .setComponentUuid(entity.getUuid()) .build(), dbSession)).hasSize(1); } - private void assertNoFavorite(ComponentDto project, UserDto user) { + private void assertNoFavorite(EntityDto entity, UserDto user) { assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder() .setUserUuid(user.getUuid()) - .setComponentUuid(project.uuid()) + .setComponentUuid(entity.getUuid()) .build(), dbSession)).isEmpty(); } } diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/favorite/FavoriteUpdater.java b/server/sonar-server-common/src/main/java/org/sonar/server/favorite/FavoriteUpdater.java index 2fd5671d931..54940fc75a1 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/favorite/FavoriteUpdater.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/favorite/FavoriteUpdater.java @@ -24,6 +24,7 @@ import javax.annotation.Nullable; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; +import org.sonar.db.entity.EntityDto; import org.sonar.db.property.PropertyDto; import org.sonar.db.property.PropertyQuery; @@ -41,7 +42,7 @@ public class FavoriteUpdater { /** * Set favorite to the logged in user. If no user, no action is done */ - public void add(DbSession dbSession, ComponentDto componentDto, @Nullable String userUuid, @Nullable String userLogin, boolean failIfTooManyFavorites) { + public void add(DbSession dbSession, EntityDto entity, @Nullable String userUuid, @Nullable String userLogin, boolean failIfTooManyFavorites) { if (userUuid == null) { return; } @@ -49,21 +50,21 @@ public class FavoriteUpdater { List<PropertyDto> existingFavoriteOnComponent = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder() .setKey(PROP_FAVORITE_KEY) .setUserUuid(userUuid) - .setComponentUuid(componentDto.uuid()) + .setComponentUuid(entity.getUuid()) .build(), dbSession); - checkArgument(existingFavoriteOnComponent.isEmpty(), "Component '%s' (uuid: %s) is already a favorite", componentDto.getKey(), componentDto.uuid()); + checkArgument(existingFavoriteOnComponent.isEmpty(), "Component '%s' (uuid: %s) is already a favorite", entity.getKey(), entity.getUuid()); - List<PropertyDto> existingFavorites = dbClient.propertiesDao().selectByKeyAndUserUuidAndComponentQualifier(dbSession, PROP_FAVORITE_KEY, userUuid, componentDto.qualifier()); + List<PropertyDto> existingFavorites = dbClient.propertiesDao().selectByKeyAndUserUuidAndComponentQualifier(dbSession, PROP_FAVORITE_KEY, userUuid, entity.getQualifier()); if (existingFavorites.size() >= 100) { - checkArgument(!failIfTooManyFavorites, "You cannot have more than 100 favorites on components with qualifier '%s'", componentDto.qualifier()); + checkArgument(!failIfTooManyFavorites, "You cannot have more than 100 favorites on components with qualifier '%s'", entity.getQualifier()); return; } dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto() .setKey(PROP_FAVORITE_KEY) - .setComponentUuid(componentDto.uuid()) + .setComponentUuid(entity.getUuid()) .setUserUuid(userUuid), userLogin, - componentDto.getKey(), componentDto.name(), componentDto.qualifier()); + entity.getKey(), entity.getName(), entity.getQualifier()); } /** diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java b/server/sonar-server-common/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java index 8b6800ae195..eed10f265f2 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java @@ -113,69 +113,10 @@ public class DefaultNotificationManager implements NotificationManager { return dbClient.notificationQueueDao().count(); } - /** - * {@inheritDoc} - */ - @Override - public Multimap<String, NotificationChannel> findSubscribedRecipientsForDispatcher(NotificationDispatcher dispatcher, - String projectKey, SubscriberPermissionsOnProject subscriberPermissionsOnProject) { - verifyProjectKey(projectKey); - String dispatcherKey = dispatcher.getKey(); - - Set<SubscriberAndChannel> subscriberAndChannels = Arrays.stream(notificationChannels) - .flatMap(notificationChannel -> toSubscriberAndChannels(dispatcherKey, projectKey, notificationChannel)) - .collect(Collectors.toSet()); - - if (subscriberAndChannels.isEmpty()) { - return ImmutableMultimap.of(); - } - - ImmutableSetMultimap.Builder<String, NotificationChannel> builder = ImmutableSetMultimap.builder(); - try (DbSession dbSession = dbClient.openSession(false)) { - Set<String> authorizedLogins = keepAuthorizedLogins(dbSession, projectKey, subscriberAndChannels, subscriberPermissionsOnProject); - subscriberAndChannels.stream() - .filter(subscriberAndChannel -> authorizedLogins.contains(subscriberAndChannel.subscriber().getLogin())) - .forEach(subscriberAndChannel -> builder.put(subscriberAndChannel.subscriber().getLogin(), subscriberAndChannel.channel())); - } - return builder.build(); - } - private static void verifyProjectKey(String projectKey) { requireNonNull(projectKey, "projectKey is mandatory"); } - private Stream<SubscriberAndChannel> toSubscriberAndChannels(String dispatcherKey, String projectKey, NotificationChannel notificationChannel) { - Set<Subscriber> usersForNotification = dbClient.propertiesDao().findUsersForNotification(dispatcherKey, notificationChannel.getKey(), projectKey); - return usersForNotification - .stream() - .map(login -> new SubscriberAndChannel(login, notificationChannel)); - } - - private Set<String> keepAuthorizedLogins(DbSession dbSession, String projectKey, Set<SubscriberAndChannel> subscriberAndChannels, - SubscriberPermissionsOnProject requiredPermissions) { - if (requiredPermissions.getGlobalSubscribers().equals(requiredPermissions.getProjectSubscribers())) { - return keepAuthorizedLogins(dbSession, projectKey, subscriberAndChannels, null, requiredPermissions.getGlobalSubscribers()); - } else { - return Stream - .concat( - keepAuthorizedLogins(dbSession, projectKey, subscriberAndChannels, true, requiredPermissions.getGlobalSubscribers()).stream(), - keepAuthorizedLogins(dbSession, projectKey, subscriberAndChannels, false, requiredPermissions.getProjectSubscribers()).stream()) - .collect(Collectors.toSet()); - } - } - - private Set<String> keepAuthorizedLogins(DbSession dbSession, String projectKey, Set<SubscriberAndChannel> subscriberAndChannels, - @Nullable Boolean global, String permission) { - Set<String> logins = subscriberAndChannels.stream() - .filter(s -> global == null || s.subscriber().isGlobal() == global) - .map(s -> s.subscriber().getLogin()) - .collect(Collectors.toSet()); - if (logins.isEmpty()) { - return Collections.emptySet(); - } - return dbClient.authorizationDao().keepAuthorizedLoginsOnProject(dbSession, logins, projectKey, permission); - } - @Override public Set<EmailRecipient> findSubscribedEmailRecipients(String dispatcherKey, String projectKey, SubscriberPermissionsOnProject subscriberPermissionsOnProject) { verifyProjectKey(projectKey); @@ -244,23 +185,6 @@ public class DefaultNotificationManager implements NotificationManager { .filter(s -> authorizedLogins.contains(s.getLogin())); } - private record SubscriberAndChannel(Subscriber subscriber, NotificationChannel channel) { - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SubscriberAndChannel that = (SubscriberAndChannel) o; - return Objects.equals(subscriber, that.subscriber) && - Objects.equals(channel, that.channel); - } - - } - @VisibleForTesting protected List<NotificationChannel> getChannels() { return Arrays.asList(notificationChannels); diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/notification/NotificationManager.java b/server/sonar-server-common/src/main/java/org/sonar/server/notification/NotificationManager.java index 6517c4ee9a1..be336191a8f 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/notification/NotificationManager.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/notification/NotificationManager.java @@ -44,23 +44,6 @@ public interface NotificationManager { */ <T extends Notification> void scheduleForSending(T notification); - /** - * <p> - * Returns the list of users who subscribed to the given dispatcher, along with the notification channels (email, twitter, ...) that they choose - * for this dispatcher. - * </p> - * <p> - * The resource ID can be null in case of notifications that have nothing to do with a specific project (like system notifications). - * </p> - * - * @param dispatcher the dispatcher for which this list of users is requested - * @param projectKey key of the project - * @param subscriberPermissionsOnProject the required permission for global and project subscribers - * @return the list of user login along with the subscribed channels - */ - Multimap<String, NotificationChannel> findSubscribedRecipientsForDispatcher(NotificationDispatcher dispatcher, String projectKey, - SubscriberPermissionsOnProject subscriberPermissionsOnProject); - record EmailRecipient(String login, String email) { public EmailRecipient(String login, String email) { this.login = requireNonNull(login, "login can't be null"); diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/notification/DefaultNotificationManagerTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/notification/DefaultNotificationManagerTest.java index d07f5d1567a..310615af703 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/notification/DefaultNotificationManagerTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/notification/DefaultNotificationManagerTest.java @@ -143,125 +143,6 @@ public class DefaultNotificationManagerTest { } @Test - public void shouldFindNoRecipient() { - assertThat(underTest.findSubscribedRecipientsForDispatcher(dispatcher, "uuid_45", new SubscriberPermissionsOnProject(UserRole.USER)).asMap().entrySet()) - .isEmpty(); - } - - @Test - public void shouldFindSubscribedRecipientForGivenResource() { - String projectKey = randomAlphabetic(6); - String otherProjectKey = randomAlphabetic(7); - when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectKey)) - .thenReturn(newHashSet(new Subscriber("user1", false), new Subscriber("user3", false), new Subscriber("user3", true))); - when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", otherProjectKey)) - .thenReturn(newHashSet(new Subscriber("user2", false))); - when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectKey)) - .thenReturn(newHashSet(new Subscriber("user3", true))); - when(propertiesDao.findUsersForNotification("NewAlerts", "Twitter", projectKey)) - .thenReturn(newHashSet(new Subscriber("user4", false))); - - when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user1", "user3"), projectKey, "user")) - .thenReturn(newHashSet("user1", "user3")); - - Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectKey, - ALL_MUST_HAVE_ROLE_USER); - assertThat(multiMap.entries()).hasSize(3); - - Map<String, Collection<NotificationChannel>> map = multiMap.asMap(); - assertThat(map.get("user1")).containsOnly(emailChannel); - assertThat(map.get("user2")).isNull(); - assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel); - assertThat(map.get("user4")).isNull(); - - // code is optimized to perform only 1 SQL requests for all channels - verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), anyString()); - } - - @Test - public void should_apply_distinct_permission_filtering_global_or_project_subscribers() { - String globalPermission = randomAlphanumeric(4); - String projectPermission = randomAlphanumeric(5); - String projectKey = randomAlphabetic(6); - String otherProjectKey = randomAlphabetic(7); - when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectKey)) - .thenReturn(newHashSet(new Subscriber("user1", false), new Subscriber("user3", false), new Subscriber("user3", true))); - when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", otherProjectKey)) - .thenReturn(newHashSet(new Subscriber("user2", false))); - when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectKey)) - .thenReturn(newHashSet(new Subscriber("user3", true))); - when(propertiesDao.findUsersForNotification("NewAlerts", "Twitter", projectKey)) - .thenReturn(newHashSet(new Subscriber("user4", false))); - - when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3", "user4"), projectKey, globalPermission)) - .thenReturn(newHashSet("user3")); - when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user1", "user3"), projectKey, projectPermission)) - .thenReturn(newHashSet("user1", "user3")); - - Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectKey, - new SubscriberPermissionsOnProject(globalPermission, projectPermission)); - assertThat(multiMap.entries()).hasSize(3); - - Map<String, Collection<NotificationChannel>> map = multiMap.asMap(); - assertThat(map.get("user1")).containsOnly(emailChannel); - assertThat(map.get("user2")).isNull(); - assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel); - assertThat(map.get("user4")).isNull(); - - // code is optimized to perform only 2 SQL requests for all channels - verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(globalPermission)); - verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(projectPermission)); - } - - @Test - public void do_not_call_db_for_project_permission_filtering_if_there_is_no_project_subscriber() { - String globalPermission = randomAlphanumeric(4); - String projectPermission = randomAlphanumeric(5); - String projectKey = randomAlphabetic(6); - when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectKey)) - .thenReturn(newHashSet(new Subscriber("user3", true))); - when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectKey)) - .thenReturn(newHashSet(new Subscriber("user3", true))); - - when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3"), projectKey, globalPermission)) - .thenReturn(newHashSet("user3")); - - Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectKey, - new SubscriberPermissionsOnProject(globalPermission, projectPermission)); - assertThat(multiMap.entries()).hasSize(2); - - Map<String, Collection<NotificationChannel>> map = multiMap.asMap(); - assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel); - - verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(globalPermission)); - verify(authorizationDao, times(0)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(projectPermission)); - } - - @Test - public void do_not_call_db_for_project_permission_filtering_if_there_is_no_global_subscriber() { - String globalPermission = randomAlphanumeric(4); - String projectPermission = randomAlphanumeric(5); - String projectKey = randomAlphabetic(6); - when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectKey)) - .thenReturn(newHashSet(new Subscriber("user3", false))); - when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectKey)) - .thenReturn(newHashSet(new Subscriber("user3", false))); - - when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3"), projectKey, projectPermission)) - .thenReturn(newHashSet("user3")); - - Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectKey, - new SubscriberPermissionsOnProject(globalPermission, projectPermission)); - assertThat(multiMap.entries()).hasSize(2); - - Map<String, Collection<NotificationChannel>> map = multiMap.asMap(); - assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel); - - verify(authorizationDao, times(0)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(globalPermission)); - verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(projectPermission)); - } - - @Test public void findSubscribedEmailRecipients_fails_with_NPE_if_projectKey_is_null() { String dispatcherKey = randomAlphabetic(12); |