ソースを参照

SONAR-13999 drop organization from Notification WS

tags/8.7.0.41497
Jacek 3年前
コミット
f1c02b0e08

+ 7
- 24
server/sonar-webserver-webapi/src/main/java/org/sonar/server/notification/ws/ListAction.java ファイルの表示

@@ -20,7 +20,6 @@
package org.sonar.server.notification.ws;

import com.google.common.base.Splitter;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -38,15 +37,14 @@ import org.sonar.core.util.stream.MoreCollectors;
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.db.user.UserDto;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Notifications.ListResponse;
import org.sonarqube.ws.Notifications.Notification;
import org.sonarqube.ws.Notifications.Notification.Builder;

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;
@@ -123,7 +121,6 @@ public class ListAction implements NotificationsWsAction {
return response -> {
List<PropertyDto> properties = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setUserUuid(user.getUuid()).build(), dbSession);
Map<String, ComponentDto> componentsByUuid = searchProjects(dbSession, properties);
Map<String, OrganizationDto> organizationsByUuid = getOrganizations(dbSession, componentsByUuid.values());

Predicate<PropertyDto> isNotification = prop -> prop.getKey().startsWith("notification.");
Predicate<PropertyDto> isComponentInDb = prop -> prop.getComponentUuid() == null || componentsByUuid.containsKey(prop.getComponentUuid());
@@ -134,7 +131,7 @@ public class ListAction implements NotificationsWsAction {
.filter(isNotification)
.filter(channelAndDispatcherAuthorized())
.filter(isComponentInDb)
.map(toWsNotification(notification, organizationsByUuid, componentsByUuid))
.map(toWsNotification(notification, componentsByUuid))
.sorted(comparing(Notification::getProject, nullsFirst(naturalOrder()))
.thenComparing(Notification::getChannel)
.thenComparing(Notification::getType))
@@ -169,36 +166,22 @@ public class ListAction implements NotificationsWsAction {
.collect(MoreCollectors.uniqueIndex(ComponentDto::uuid));
}

private Map<String, OrganizationDto> getOrganizations(DbSession dbSession, Collection<ComponentDto> values) {
Set<String> organizationUuids = values.stream()
.map(ComponentDto::getOrganizationUuid)
.collect(MoreCollectors.toSet());
return dbClient.organizationDao().selectByUuids(dbSession, organizationUuids)
.stream()
.collect(MoreCollectors.uniqueIndex(OrganizationDto::getUuid));
}

private static Function<PropertyDto, Notification> toWsNotification(Notification.Builder notification,
Map<String, OrganizationDto> organizationsByUuid, Map<String, ComponentDto> projectsByUuid) {
private static Function<PropertyDto, Notification> toWsNotification(Notification.Builder notification, Map<String, ComponentDto> projectsByUuid) {
return property -> {
notification.clear();
List<String> propertyKey = Splitter.on(".").splitToList(property.getKey());
notification.setType(propertyKey.get(1));
notification.setChannel(propertyKey.get(2));
ofNullable(property.getComponentUuid()).ifPresent(componentUuid -> populateProjectFields(notification, componentUuid, organizationsByUuid, projectsByUuid));
ofNullable(property.getComponentUuid()).ifPresent(componentUuid -> populateProjectFields(notification, componentUuid, projectsByUuid));

return notification.build();
};
}

private static Notification.Builder populateProjectFields(Notification.Builder notification, String componentUuid,
Map<String, OrganizationDto> organizationsByUuid, Map<String, ComponentDto> projectsByUuid) {
private static void populateProjectFields(Builder notification, String componentUuid,
Map<String, ComponentDto> projectsByUuid) {
ComponentDto project = projectsByUuid.get(componentUuid);
String organizationUuid = project.getOrganizationUuid();
OrganizationDto organizationDto = organizationsByUuid.get(organizationUuid);
checkArgument(organizationDto != null, "No organization for uuid '%s'", organizationUuid);

return notification.setOrganization(organizationDto.getKey())
notification
.setProject(project.getDbKey())
.setProjectName(project.name());
}

+ 1
- 1
server/sonar-webserver-webapi/src/test/java/org/sonar/server/notification/ws/AddActionTest.java ファイルの表示

@@ -306,7 +306,7 @@ public class AddActionTest {
public void fail_when_component_is_not_a_project() {
UserDto user = db.users().insertUser();
userSession.logIn(user);
db.components().insertViewAndSnapshot(newView(db.organizations().insert()).setDbKey("VIEW_1"));
db.components().insertViewAndSnapshot(newView().setDbKey("VIEW_1"));
when(dispatchers.getGlobalDispatchers()).thenReturn(singletonList(NOTIF_MY_NEW_ISSUES));
when(dispatchers.getProjectDispatchers()).thenReturn(singletonList(NOTIF_MY_NEW_ISSUES));


+ 27
- 34
server/sonar-webserver-webapi/src/test/java/org/sonar/server/notification/ws/ListActionTest.java ファイルの表示

@@ -21,14 +21,12 @@ package org.sonar.server.notification.ws;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.notifications.NotificationChannel;
import org.sonar.api.server.ws.WebService;
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.user.UserDto;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
@@ -42,6 +40,7 @@ import org.sonarqube.ws.Notifications.Notification;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -55,22 +54,20 @@ public class ListActionTest {
private static final String NOTIF_NEW_QUALITY_GATE_STATUS = "NewQualityGateStatus";

@Rule
public ExpectedException expectedException = ExpectedException.none();
public final UserSessionRule userSession = UserSessionRule.standalone();
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
@Rule
public DbTester db = DbTester.create();
public final DbTester db = DbTester.create();

private DbClient dbClient = db.getDbClient();
private DbSession dbSession = db.getSession();
private final DbClient dbClient = db.getDbClient();
private final DbSession dbSession = db.getSession();

private NotificationChannel emailChannel = new FakeNotificationChannel("EmailChannel");
private NotificationChannel twitterChannel = new FakeNotificationChannel("TwitterChannel");
private final NotificationChannel emailChannel = new FakeNotificationChannel("EmailChannel");
private final NotificationChannel twitterChannel = new FakeNotificationChannel("TwitterChannel");

private NotificationUpdater notificationUpdater = new NotificationUpdater(dbClient);
private Dispatchers dispatchers = mock(Dispatchers.class);
private final NotificationUpdater notificationUpdater = new NotificationUpdater(dbClient);
private final Dispatchers dispatchers = mock(Dispatchers.class);

private WsActionTester ws = new WsActionTester(new ListAction(new NotificationCenter(
private final WsActionTester ws = new WsActionTester(new ListAction(new NotificationCenter(
new NotificationDispatcherMetadata[] {},
new NotificationChannel[] {emailChannel, twitterChannel}),
dbClient, userSession, dispatchers));
@@ -177,8 +174,7 @@ public class ListActionTest {
userSession.logIn(user);
when(dispatchers.getGlobalDispatchers()).thenReturn(asList(NOTIF_MY_NEW_ISSUES, NOTIF_NEW_ISSUES, NOTIF_NEW_QUALITY_GATE_STATUS));
when(dispatchers.getProjectDispatchers()).thenReturn(asList(NOTIF_MY_NEW_ISSUES, NOTIF_NEW_QUALITY_GATE_STATUS));
OrganizationDto organization = db.organizations().insert();
ComponentDto project = db.components().insertPrivateProject(organization);
ComponentDto project = db.components().insertPrivateProject();
db.users().insertProjectPermissionOnUser(user, USER, project);
notificationUpdater.add(dbSession, twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
@@ -191,14 +187,14 @@ public class ListActionTest {
ListResponse result = call();

assertThat(result.getNotificationsList())
.extracting(Notification::getChannel, Notification::getOrganization, Notification::getType, Notification::getProject)
.extracting(Notification::getChannel, 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(), organization.getKey(), NOTIF_MY_NEW_ISSUES, project.getKey()),
tuple(emailChannel.getKey(), organization.getKey(), NOTIF_NEW_QUALITY_GATE_STATUS, project.getKey()),
tuple(twitterChannel.getKey(), organization.getKey(), NOTIF_MY_NEW_ISSUES, project.getKey()));
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, project.getKey()),
tuple(emailChannel.getKey(), NOTIF_NEW_QUALITY_GATE_STATUS, project.getKey()),
tuple(twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, project.getKey()));
}

@Test
@@ -225,19 +221,18 @@ public class ListActionTest {
notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
dbSession.commit();

expectedException.expect(ForbiddenException.class);
call(user.getLogin());
String userLogin = user.getLogin();
assertThatThrownBy(() -> call(userLogin))
.isInstanceOf(ForbiddenException.class);
}

@Test
public void fail_if_login_is_provided_and_unknown() {
userSession.logIn().setSystemAdministrator();

expectedException.expect(NotFoundException.class);
expectedException.expectMessage("User 'LOGIN 404' not found");

call("LOGIN 404");
assertThatThrownBy(() -> call("LOGIN 404"))
.isInstanceOf(NotFoundException.class)
.hasMessage("User 'LOGIN 404' not found");
}

@Test
@@ -246,8 +241,7 @@ public class ListActionTest {
userSession.logIn(user);
when(dispatchers.getGlobalDispatchers()).thenReturn(asList(NOTIF_MY_NEW_ISSUES, NOTIF_NEW_ISSUES, NOTIF_NEW_QUALITY_GATE_STATUS));
when(dispatchers.getProjectDispatchers()).thenReturn(asList(NOTIF_MY_NEW_ISSUES, NOTIF_NEW_QUALITY_GATE_STATUS));
OrganizationDto organization = db.organizations().insertForKey("my-org-1");
ComponentDto project = db.components().insertPrivateProject(organization, p -> p.setDbKey(KEY_PROJECT_EXAMPLE_001).setName("My Project"));
ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey(KEY_PROJECT_EXAMPLE_001).setName("My Project"));
db.users().insertProjectPermissionOnUser(user, USER, project);
notificationUpdater.add(dbSession, twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
@@ -283,9 +277,8 @@ public class ListActionTest {
public void fail_when_not_authenticated() {
userSession.anonymous();

expectedException.expect(UnauthorizedException.class);

call();
assertThatThrownBy(this::call)
.isInstanceOf(UnauthorizedException.class);
}

private ListResponse call() {

+ 58
- 63
server/sonar-webserver-webapi/src/test/java/org/sonar/server/notification/ws/RemoveActionTest.java ファイルの表示

@@ -21,7 +21,6 @@ package org.sonar.server.notification.ws;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.notifications.Notification;
import org.sonar.api.notifications.NotificationChannel;
import org.sonar.db.DbClient;
@@ -47,6 +46,7 @@ import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.Optional.ofNullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.db.component.ComponentTesting.newView;
@@ -60,25 +60,23 @@ public class RemoveActionTest {
private static final String NOTIF_NEW_ISSUES = "Dispatcher2";
private static final String NOTIF_NEW_QUALITY_GATE_STATUS = "Dispatcher3";
@Rule
public ExpectedException expectedException = ExpectedException.none();
public final UserSessionRule userSession = UserSessionRule.standalone();
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
@Rule
public DbTester db = DbTester.create();
private DbClient dbClient = db.getDbClient();
private DbSession dbSession = db.getSession();
public final DbTester db = DbTester.create();
private final DbClient dbClient = db.getDbClient();
private final DbSession dbSession = db.getSession();

private NotificationChannel emailChannel = new FakeNotificationChannel("EmailChannel");
private NotificationChannel twitterChannel = new FakeNotificationChannel("TwitterChannel");
private final NotificationChannel emailChannel = new FakeNotificationChannel("EmailChannel");
private final NotificationChannel twitterChannel = new FakeNotificationChannel("TwitterChannel");
// default channel, based on class simple name
private NotificationChannel defaultChannel = new FakeNotificationChannel("EmailNotificationChannel");
private final NotificationChannel defaultChannel = new FakeNotificationChannel("EmailNotificationChannel");

private NotificationUpdater notificationUpdater = new NotificationUpdater(dbClient);
private Dispatchers dispatchers = mock(Dispatchers.class);
private final NotificationUpdater notificationUpdater = new NotificationUpdater(dbClient);
private final Dispatchers dispatchers = mock(Dispatchers.class);

private RemoveRequest request = new RemoveRequest().setType(NOTIF_MY_NEW_ISSUES);
private final RemoveRequest request = new RemoveRequest().setType(NOTIF_MY_NEW_ISSUES);

private WsActionTester ws = new WsActionTester(new RemoveAction(new NotificationCenter(
private final WsActionTester ws = new WsActionTester(new RemoveAction(new NotificationCenter(
new NotificationDispatcherMetadata[] {},
new NotificationChannel[] {emailChannel, twitterChannel, defaultChannel}), notificationUpdater, dispatchers, dbClient, TestComponentFinder.from(db), userSession));

@@ -133,10 +131,9 @@ public class RemoveActionTest {
notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, project);
dbSession.commit();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Notification doesn't exist");

call(request);
assertThatThrownBy(() -> call(request))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Notification doesn't exist");
}

@Test
@@ -149,10 +146,10 @@ public class RemoveActionTest {
notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
dbSession.commit();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Notification doesn't exist");
call(request.setProject(project.getDbKey()));
RemoveRequest request = this.request.setProject(project.getDbKey());
assertThatThrownBy(() -> call(request))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Notification doesn't exist");
}

@Test
@@ -190,10 +187,10 @@ public class RemoveActionTest {
userSession.logIn(user).setSystemAdministrator();
when(dispatchers.getGlobalDispatchers()).thenReturn(singletonList(NOTIF_MY_NEW_ISSUES));

expectedException.expect(NotFoundException.class);
expectedException.expectMessage("User 'LOGIN 404' not found");
call(request.setLogin("LOGIN 404"));
RemoveRequest request = this.request.setLogin("LOGIN 404");
assertThatThrownBy(() -> call(request))
.isInstanceOf(NotFoundException.class)
.hasMessage("User 'LOGIN 404' not found");
}

@Test
@@ -204,9 +201,9 @@ public class RemoveActionTest {
notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
dbSession.commit();

expectedException.expect(ForbiddenException.class);
call(request.setLogin(user.getLogin()));
RemoveRequest request = this.request.setLogin(user.getLogin());
assertThatThrownBy(() -> call(request))
.isInstanceOf(ForbiddenException.class);
}

@Test
@@ -215,10 +212,9 @@ public class RemoveActionTest {
userSession.logIn(user);
when(dispatchers.getGlobalDispatchers()).thenReturn(singletonList(NOTIF_MY_NEW_ISSUES));

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Notification doesn't exist");

call(request);
assertThatThrownBy(() -> call(request))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Notification doesn't exist");
}

@Test
@@ -227,9 +223,9 @@ public class RemoveActionTest {
userSession.logIn(user);
when(dispatchers.getGlobalDispatchers()).thenReturn(singletonList(NOTIF_MY_NEW_ISSUES));

expectedException.expect(IllegalArgumentException.class);
call(request.setChannel("Channel42"));
RemoveRequest request = this.request.setChannel("Channel42");
assertThatThrownBy(() -> call(request))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
@@ -238,10 +234,10 @@ public class RemoveActionTest {
userSession.logIn(user);
when(dispatchers.getGlobalDispatchers()).thenReturn(asList(NOTIF_MY_NEW_ISSUES, NOTIF_NEW_ISSUES, NOTIF_NEW_QUALITY_GATE_STATUS));

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Value of parameter 'type' (Dispatcher42) must be one of: [Dispatcher1, Dispatcher2, Dispatcher3]");
call(request.setType("Dispatcher42"));
RemoveRequest request = this.request.setType("Dispatcher42");
assertThatThrownBy(() -> call(request))
.isInstanceOf(BadRequestException.class)
.hasMessage("Value of parameter 'type' (Dispatcher42) must be one of: [Dispatcher1, Dispatcher2, Dispatcher3]");
}

@Test
@@ -252,10 +248,10 @@ public class RemoveActionTest {
when(dispatchers.getProjectDispatchers()).thenReturn(asList(NOTIF_MY_NEW_ISSUES, NOTIF_NEW_QUALITY_GATE_STATUS));
ComponentDto project = db.components().insertPrivateProject();

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Value of parameter 'type' (Dispatcher42) must be one of: [Dispatcher1, Dispatcher3]");
call(request.setType("Dispatcher42").setProject(project.getDbKey()));
RemoveRequest request = this.request.setType("Dispatcher42").setProject(project.getDbKey());
assertThatThrownBy(() -> call(request))
.isInstanceOf(BadRequestException.class)
.hasMessage("Value of parameter 'type' (Dispatcher42) must be one of: [Dispatcher1, Dispatcher3]");
}

@Test
@@ -264,10 +260,10 @@ public class RemoveActionTest {
userSession.logIn(user);
when(dispatchers.getGlobalDispatchers()).thenReturn(singletonList(NOTIF_MY_NEW_ISSUES));

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("The 'type' parameter is missing");
ws.newRequest().execute();
TestRequest request = ws.newRequest();
assertThatThrownBy(request::execute)
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("The 'type' parameter is missing");
}

@Test
@@ -277,9 +273,9 @@ public class RemoveActionTest {
when(dispatchers.getGlobalDispatchers()).thenReturn(singletonList(NOTIF_MY_NEW_ISSUES));
when(dispatchers.getProjectDispatchers()).thenReturn(singletonList(NOTIF_MY_NEW_ISSUES));

expectedException.expect(NotFoundException.class);
call(request.setProject("Project-42"));
RemoveRequest request = this.request.setProject("Project-42");
assertThatThrownBy(() -> call(request))
.isInstanceOf(NotFoundException.class);
}

@Test
@@ -288,12 +284,12 @@ public class RemoveActionTest {
userSession.logIn(user);
when(dispatchers.getGlobalDispatchers()).thenReturn(singletonList(NOTIF_MY_NEW_ISSUES));
when(dispatchers.getProjectDispatchers()).thenReturn(singletonList(NOTIF_MY_NEW_ISSUES));
db.components().insertViewAndSnapshot(newView(db.organizations().insert()).setDbKey("VIEW_1"));

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Component 'VIEW_1' must be a project");
db.components().insertViewAndSnapshot(newView().setDbKey("VIEW_1"));

call(request.setProject("VIEW_1"));
RemoveRequest request = this.request.setProject("VIEW_1");
assertThatThrownBy(() -> call(request))
.isInstanceOf(BadRequestException.class)
.hasMessage("Component 'VIEW_1' must be a project");
}

@Test
@@ -301,9 +297,8 @@ public class RemoveActionTest {
userSession.anonymous();
when(dispatchers.getGlobalDispatchers()).thenReturn(singletonList(NOTIF_MY_NEW_ISSUES));

expectedException.expect(UnauthorizedException.class);

call(request);
assertThatThrownBy(() -> call(request))
.isInstanceOf(UnauthorizedException.class);
}

@Test
@@ -315,10 +310,10 @@ public class RemoveActionTest {
ComponentDto project = db.components().insertPublicProject();
ComponentDto branch = db.components().insertProjectBranch(project);

expectedException.expect(NotFoundException.class);
expectedException.expectMessage(format("Component key '%s' not found", branch.getDbKey()));
call(request.setProject(branch.getDbKey()));
RemoveRequest request = this.request.setProject(branch.getDbKey());
assertThatThrownBy(() -> call(request))
.isInstanceOf(NotFoundException.class)
.hasMessage(format("Component key '%s' not found", branch.getDbKey()));
}

private TestResponse call(RemoveRequest remove) {

+ 0
- 1
sonar-ws/src/main/protobuf/ws-notifications.proto ファイルの表示

@@ -35,7 +35,6 @@ message ListResponse {
message Notification {
optional string channel = 1;
optional string type = 2;
optional string organization = 5;
optional string project = 3;
optional string projectName = 4;
}

読み込み中…
キャンセル
保存