From 1a7ddb192f12c2577ced3f34e79d9a0d110f703b Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Wed, 1 Feb 2017 17:30:43 +0100 Subject: [PATCH] SONAR-8664 add organization to response of api/notifications/list --- .../server/notification/ws/ListAction.java | 54 +++++++++++++------ .../server/notification/ws/list-example.json | 3 ++ .../notification/ws/ListActionTest.java | 32 +++++++---- .../src/main/protobuf/ws-notifications.proto | 1 + 4 files changed, 63 insertions(+), 27 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/notification/ws/ListAction.java b/server/sonar-server/src/main/java/org/sonar/server/notification/ws/ListAction.java index 409323408b4..c1eb9061b7e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/notification/ws/ListAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/notification/ws/ListAction.java @@ -22,8 +22,11 @@ package org.sonar.server.notification.ws; import com.google.common.base.Splitter; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -38,6 +41,7 @@ import org.sonar.core.util.stream.Collectors; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; +import org.sonar.db.organization.OrganizationDto; import org.sonar.db.property.PropertyDto; import org.sonar.db.property.PropertyQuery; import org.sonar.server.notification.NotificationCenter; @@ -45,11 +49,11 @@ import org.sonar.server.user.UserSession; import org.sonarqube.ws.Notifications.ListResponse; import org.sonarqube.ws.Notifications.Notification; +import static com.google.common.base.Preconditions.checkArgument; import static java.util.Comparator.comparing; import static java.util.Comparator.naturalOrder; import static java.util.Comparator.nullsFirst; import static org.sonar.core.util.Protobuf.setNullable; -import static org.sonar.core.util.stream.Collectors.toList; import static org.sonar.core.util.stream.Collectors.toOneElement; import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION; import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION; @@ -112,6 +116,7 @@ public class ListAction implements NotificationsWsAction { return response -> { List properties = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setUserId(userSession.getUserId()).build(), dbSession); Map componentsById = searchProjects(dbSession, properties); + Map organizationsByUuid = getOrganizations(dbSession, componentsById.values()); Predicate isNotification = prop -> prop.getKey().startsWith("notification."); Predicate isComponentInDb = prop -> prop.getResourceId() == null || componentsById.containsKey(prop.getResourceId()); @@ -122,7 +127,7 @@ public class ListAction implements NotificationsWsAction { .filter(isNotification) .filter(channelAndDispatcherAuthorized()) .filter(isComponentInDb) - .map(toWsNotification(notification, componentsById)) + .map(toWsNotification(notification, organizationsByUuid, componentsById)) .sorted(comparing(Notification::getProject, nullsFirst(naturalOrder())) .thenComparing(comparing(Notification::getChannel)) .thenComparing(comparing(Notification::getType))) @@ -146,35 +151,52 @@ public class ListAction implements NotificationsWsAction { } private Map searchProjects(DbSession dbSession, List properties) { - Collection authorizedComponentUuids = dbClient.authorizationDao().selectAuthorizedRootProjectsUuids(dbSession, userSession.getUserId(), UserRole.USER); - return dbClient.componentDao().selectByIds(dbSession, - properties.stream() - .filter(prop -> prop.getResourceId() != null) - .map(PropertyDto::getResourceId) - .distinct() - .collect(toList())) + Set authorizedComponentUuids = new HashSet<>(dbClient.authorizationDao().selectAuthorizedRootProjectsUuids(dbSession, userSession.getUserId(), UserRole.USER)); + Set componentIds = properties.stream() + .map(PropertyDto::getResourceId) + .filter(Objects::nonNull) + .collect(Collectors.toSet(properties.size())); + return dbClient.componentDao().selectByIds(dbSession, componentIds) .stream() .filter(c -> authorizedComponentUuids.contains(c.uuid())) .collect(Collectors.uniqueIndex(ComponentDto::getId)); } - private static Function toWsNotification(Notification.Builder notification, Map projectsById) { + private Map getOrganizations(DbSession dbSession, Collection values) { + Set organizationUuids = values.stream() + .map(ComponentDto::getOrganizationUuid) + .collect(Collectors.toSet()); + return dbClient.organizationDao().selectByUuids(dbSession, organizationUuids) + .stream() + .collect(Collectors.uniqueIndex(OrganizationDto::getUuid)); + } + + private static Function toWsNotification(Notification.Builder notification, + Map organizationsByUuid, Map projectsById) { return property -> { notification.clear(); List propertyKey = Splitter.on(".").splitToList(property.getKey()); notification.setType(propertyKey.get(1)); notification.setChannel(propertyKey.get(2)); - setNullable(property.getResourceId(), componentId -> { - ComponentDto project = projectsById.get(componentId); - notification.setProject(project.getKey()); - notification.setProjectName(project.name()); - return notification; - }); + setNullable(property.getResourceId(), + componentId -> populateProjectFields(notification, componentId, organizationsByUuid, projectsById)); return notification.build(); }; } + private static Notification.Builder populateProjectFields(Notification.Builder notification, Long componentId, + Map organizationsByUuid, Map projectsById) { + ComponentDto project = projectsById.get(componentId); + String organizationUuid = project.getOrganizationUuid(); + OrganizationDto organizationDto = organizationsByUuid.get(organizationUuid); + checkArgument(organizationDto != null, "No organization for uuid '%s'", organizationUuid); + + return notification.setOrganization(organizationDto.getKey()) + .setProject(project.getKey()) + .setProjectName(project.name()); + } + private Consumer checkPermissions() { return request -> userSession.checkLoggedIn(); } diff --git a/server/sonar-server/src/main/resources/org/sonar/server/notification/ws/list-example.json b/server/sonar-server/src/main/resources/org/sonar/server/notification/ws/list-example.json index e794aae16d7..0ebee0c1963 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/notification/ws/list-example.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/notification/ws/list-example.json @@ -15,18 +15,21 @@ { "channel": "EmailChannel", "type": "MyNewIssues", + "organization": "my-org-1", "project": "my_project", "projectName": "My Project" }, { "channel": "EmailChannel", "type": "NewQualityGateStatus", + "organization": "my-org-1", "project": "my_project", "projectName": "My Project" }, { "channel": "TwitterChannel", "type": "MyNewIssues", + "organization": "my-org-1", "project": "my_project", "projectName": "My Project" } diff --git a/server/sonar-server/src/test/java/org/sonar/server/notification/ws/ListActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/ws/ListActionTest.java index a9437914cc1..cde5be0ed7c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/notification/ws/ListActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/notification/ws/ListActionTest.java @@ -33,6 +33,7 @@ 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.organization.OrganizationDto; import org.sonar.db.permission.UserPermissionDto; import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.notification.NotificationCenter; @@ -56,12 +57,14 @@ public class ListActionTest { private static final String NOTIF_MY_NEW_ISSUES = "MyNewIssues"; private static final String NOTIF_NEW_ISSUES = "NewIssues"; private static final String NOTIF_NEW_QUALITY_GATE_STATUS = "NewQualityGateStatus"; + @Rule public ExpectedException expectedException = ExpectedException.none(); @Rule public UserSessionRule userSession = UserSessionRule.standalone().logIn().setUserId(123); @Rule public DbTester db = DbTester.create(); + private DbClient dbClient = db.getDbClient(); private DbSession dbSession = db.getSession(); @@ -156,12 +159,15 @@ public class ListActionTest { ListResponse result = call(); - assertThat(result.getNotificationsList()).extracting(Notification::getType).containsOnly(NOTIF_MY_NEW_ISSUES); + assertThat(result.getNotificationsList()) + .extracting(Notification::getType) + .containsOnly(NOTIF_MY_NEW_ISSUES); } @Test public void order_with_global_then_by_channel_and_dispatcher() { - ComponentDto project = addComponent(newProjectDto(db.organizations().insert()).setKey("K1")); + OrganizationDto organization = db.organizations().insert(); + ComponentDto project = addComponent(newProjectDto(organization).setKey("K1")); notificationUpdater.add(dbSession, twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, null); notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, null); notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_NEW_ISSUES, null); @@ -172,19 +178,21 @@ public class ListActionTest { ListResponse result = call(); - assertThat(result.getNotificationsList()).extracting(Notification::getChannel, Notification::getType, Notification::getProject) + assertThat(result.getNotificationsList()) + .extracting(Notification::getChannel, Notification::getOrganization, Notification::getType, Notification::getProject) .containsExactly( - tuple(emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, ""), - tuple(emailChannel.getKey(), NOTIF_NEW_ISSUES, ""), - tuple(twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, ""), - tuple(emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, "K1"), - tuple(emailChannel.getKey(), NOTIF_NEW_QUALITY_GATE_STATUS, "K1"), - tuple(twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, "K1")); + tuple(emailChannel.getKey(), "", NOTIF_MY_NEW_ISSUES, ""), + tuple(emailChannel.getKey(), "", NOTIF_NEW_ISSUES, ""), + tuple(twitterChannel.getKey(), "", NOTIF_MY_NEW_ISSUES, ""), + tuple(emailChannel.getKey(), organization.getKey(), NOTIF_MY_NEW_ISSUES, "K1"), + tuple(emailChannel.getKey(), organization.getKey(), NOTIF_NEW_QUALITY_GATE_STATUS, "K1"), + tuple(twitterChannel.getKey(), organization.getKey(), NOTIF_MY_NEW_ISSUES, "K1")); } @Test public void json_example() { - ComponentDto project = addComponent(newProjectDto(db.organizations().insert()).setKey(KEY_PROJECT_EXAMPLE_001).setName("My Project")); + OrganizationDto organization = db.organizations().insertForKey("my-org-1"); + ComponentDto project = addComponent(newProjectDto(organization).setKey(KEY_PROJECT_EXAMPLE_001).setName("My Project")); notificationUpdater.add(dbSession, twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, null); notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, null); notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_NEW_ISSUES, null); @@ -195,7 +203,9 @@ public class ListActionTest { String result = ws.newRequest().execute().getInput(); - assertJson(ws.getDef().responseExampleAsString()).withStrictArrayOrder().isSimilarTo(result); + assertJson(ws.getDef().responseExampleAsString()) + .withStrictArrayOrder() + .isSimilarTo(result); } @Test diff --git a/sonar-ws/src/main/protobuf/ws-notifications.proto b/sonar-ws/src/main/protobuf/ws-notifications.proto index 1cf7782806c..ac0cb034748 100644 --- a/sonar-ws/src/main/protobuf/ws-notifications.proto +++ b/sonar-ws/src/main/protobuf/ws-notifications.proto @@ -35,6 +35,7 @@ message ListResponse { message Notification { optional string channel = 1; optional string type = 2; + optional string organization = 5; optional string project = 3; optional string projectName = 4; } -- 2.39.5