Skip to content

Commit

Permalink
SONAR-8964 Drop overall notifications not related to the current user…
Browse files Browse the repository at this point in the history
… on SonarCloud

* SONAR-8964 Remove some global notifications in api/notifications/list on SonarCloud
* SONAR-8964 Remove some global notifications in api/notifications/add on SonarCloud
* In WebServiceEngine, define web services in start()
* SONAR-8964 Remove some global notifications in api/notifications/remove on SonarCloud
* Move some user ITs to their own suite
* SONAR-8964 Add ITs on notifications (not for SonarCloud)
* SONAR-8964 Add ITs on notifications for SonarCloud
  • Loading branch information
julienlancelot committed Feb 12, 2018
1 parent 3ba0210 commit 4a85b3c
Show file tree
Hide file tree
Showing 29 changed files with 878 additions and 239 deletions.
2 changes: 1 addition & 1 deletion cix.sh
Expand Up @@ -43,7 +43,7 @@ case "$RUN_ACTIVITY" in
;;

Category4)
CATEGORY="Category4|duplication"
CATEGORY="Category4|duplication|user"
;;

Category5)
Expand Down
Expand Up @@ -42,34 +42,30 @@

import static java.util.Optional.empty;
import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.core.util.stream.MoreCollectors.toList;
import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION;
import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION;
import static org.sonar.server.ws.WsUtils.checkFound;
import static org.sonar.server.ws.WsUtils.checkRequest;
import static org.sonar.server.notification.ws.NotificationsWsParameters.ACTION_ADD;
import static org.sonar.server.notification.ws.NotificationsWsParameters.PARAM_CHANNEL;
import static org.sonar.server.notification.ws.NotificationsWsParameters.PARAM_LOGIN;
import static org.sonar.server.notification.ws.NotificationsWsParameters.PARAM_PROJECT;
import static org.sonar.server.notification.ws.NotificationsWsParameters.PARAM_TYPE;
import static org.sonar.server.ws.WsUtils.checkFound;
import static org.sonar.server.ws.WsUtils.checkRequest;

public class AddAction implements NotificationsWsAction {
private final NotificationCenter notificationCenter;
private final NotificationUpdater notificationUpdater;
private final Dispatchers dispatchers;
private final DbClient dbClient;
private final ComponentFinder componentFinder;
private final UserSession userSession;
private final List<String> globalDispatchers;
private final List<String> projectDispatchers;

public AddAction(NotificationCenter notificationCenter, NotificationUpdater notificationUpdater, DbClient dbClient, ComponentFinder componentFinder, UserSession userSession) {
public AddAction(NotificationCenter notificationCenter, NotificationUpdater notificationUpdater, Dispatchers dispatchers, DbClient dbClient, ComponentFinder componentFinder,
UserSession userSession) {
this.notificationCenter = notificationCenter;
this.notificationUpdater = notificationUpdater;
this.dispatchers = dispatchers;
this.dbClient = dbClient;
this.componentFinder = componentFinder;
this.userSession = userSession;
this.globalDispatchers = notificationCenter.getDispatcherKeysForProperty(GLOBAL_NOTIFICATION, "true").stream().sorted().collect(toList());
this.projectDispatchers = notificationCenter.getDispatcherKeysForProperty(PER_PROJECT_NOTIFICATION, "true").stream().sorted().collect(toList());
}

@Override
Expand Down Expand Up @@ -101,8 +97,8 @@ public void define(WebService.NewController context) {
" <li>Global notifications: %s</li>" +
" <li>Per project notifications: %s</li>" +
"</ul>",
String.join(", ", globalDispatchers),
String.join(", ", projectDispatchers))
String.join(", ", dispatchers.getGlobalDispatchers()),
String.join(", ", dispatchers.getProjectDispatchers()))
.setRequired(true)
.setExampleValue(MyNewIssuesNotificationDispatcher.KEY);

Expand Down Expand Up @@ -158,15 +154,15 @@ private AddRequest toWsRequest(Request request) {
setNullable(request.param(PARAM_LOGIN), add::setLogin);

if (add.getProject() == null) {
checkRequest(globalDispatchers.contains(add.getType()), "Value of parameter '%s' (%s) must be one of: %s",
checkRequest(dispatchers.getGlobalDispatchers().contains(add.getType()), "Value of parameter '%s' (%s) must be one of: %s",
PARAM_TYPE,
add.getType(),
globalDispatchers);
dispatchers.getGlobalDispatchers());
} else {
checkRequest(projectDispatchers.contains(add.getType()), "Value of parameter '%s' (%s) must be one of: %s",
checkRequest(dispatchers.getProjectDispatchers().contains(add.getType()), "Value of parameter '%s' (%s) must be one of: %s",
PARAM_TYPE,
add.getType(),
projectDispatchers);
dispatchers.getProjectDispatchers());
}

return add;
Expand Down
@@ -0,0 +1,30 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package org.sonar.server.notification.ws;

import java.util.List;

public interface Dispatchers {

List<String> getGlobalDispatchers();

List<String> getProjectDispatchers();
}
@@ -0,0 +1,89 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package org.sonar.server.notification.ws;

import com.google.common.collect.ImmutableSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import org.sonar.api.Startable;
import org.sonar.api.config.Configuration;
import org.sonar.process.ProcessProperties;
import org.sonar.server.event.NewAlerts;
import org.sonar.server.issue.notification.DoNotFixNotificationDispatcher;
import org.sonar.server.issue.notification.NewIssuesNotificationDispatcher;
import org.sonar.server.notification.NotificationCenter;

import static org.sonar.core.util.stream.MoreCollectors.toList;
import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION;
import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION;

public class DispatchersImpl implements Dispatchers, Startable {

private static final Set<String> GLOBAL_DISPATCHERS_TO_IGNORE_ON_SONAR_CLOUD = ImmutableSet.of(
NewAlerts.KEY,
DoNotFixNotificationDispatcher.KEY,
NewIssuesNotificationDispatcher.KEY);

private final NotificationCenter notificationCenter;
private final Configuration configuration;

private List<String> projectDispatchers;
private List<String> globalDispatchers;

public DispatchersImpl(NotificationCenter notificationCenter, Configuration configuration) {
this.notificationCenter = notificationCenter;
this.configuration = configuration;
}

@Override
public List<String> getGlobalDispatchers() {
return globalDispatchers;
}

@Override
public List<String> getProjectDispatchers() {
return projectDispatchers;
}

@Override
public void start() {
boolean isOnSonarCloud = configuration.getBoolean(ProcessProperties.Property.SONARCLOUD_ENABLED.getKey()).orElse(false);
this.globalDispatchers = notificationCenter.getDispatcherKeysForProperty(GLOBAL_NOTIFICATION, "true")
.stream()
.filter(filterDispatcherForSonarCloud(isOnSonarCloud))
.sorted()
.collect(toList());
this.projectDispatchers = notificationCenter.getDispatcherKeysForProperty(PER_PROJECT_NOTIFICATION, "true")
.stream()
.sorted()
.collect(toList());
}

private static Predicate<String> filterDispatcherForSonarCloud(boolean isOnSonarCloud) {
return dispatcher -> !(isOnSonarCloud && GLOBAL_DISPATCHERS_TO_IGNORE_ON_SONAR_CLOUD.contains(dispatcher));
}

@Override
public void stop() {
// nothing to do
}
}
Expand Up @@ -53,28 +53,25 @@
import static java.util.Comparator.nullsFirst;
import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.core.util.stream.MoreCollectors.toOneElement;
import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION;
import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION;
import static org.sonar.server.ws.WsUtils.checkFound;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonar.server.notification.ws.NotificationsWsParameters.ACTION_LIST;
import static org.sonar.server.notification.ws.NotificationsWsParameters.PARAM_LOGIN;
import static org.sonar.server.ws.WsUtils.checkFound;
import static org.sonar.server.ws.WsUtils.writeProtobuf;

public class ListAction implements NotificationsWsAction {

private static final Splitter PROPERTY_KEY_SPLITTER = Splitter.on(".");

private final DbClient dbClient;
private final UserSession userSession;
private final List<String> globalDispatchers;
private final List<String> perProjectDispatchers;
private final List<String> channels;
private final Dispatchers dispatchers;

public ListAction(NotificationCenter notificationCenter, DbClient dbClient, UserSession userSession) {
public ListAction(NotificationCenter notificationCenter, DbClient dbClient, UserSession userSession, Dispatchers dispatchers) {
this.dbClient = dbClient;
this.userSession = userSession;
this.globalDispatchers = notificationCenter.getDispatcherKeysForProperty(GLOBAL_NOTIFICATION, "true").stream().sorted().collect(MoreCollectors.toList());
this.perProjectDispatchers = notificationCenter.getDispatcherKeysForProperty(PER_PROJECT_NOTIFICATION, "true").stream().sorted().collect(MoreCollectors.toList());
this.channels = notificationCenter.getChannels().stream().map(NotificationChannel::getKey).sorted().collect(MoreCollectors.toList());
this.dispatchers = dispatchers;
}

@Override
Expand Down Expand Up @@ -110,8 +107,8 @@ private ListResponse search(Request request) {
return Stream
.of(ListResponse.newBuilder())
.map(r -> r.addAllChannels(channels))
.map(r -> r.addAllGlobalTypes(globalDispatchers))
.map(r -> r.addAllPerProjectTypes(perProjectDispatchers))
.map(r -> r.addAllGlobalTypes(dispatchers.getGlobalDispatchers()))
.map(r -> r.addAllPerProjectTypes(dispatchers.getProjectDispatchers()))
.map(addNotifications(dbSession, user))
.map(ListResponse.Builder::build)
.collect(toOneElement());
Expand Down Expand Up @@ -158,7 +155,7 @@ private Predicate<PropertyDto> channelAndDispatcherAuthorized() {
}

private boolean isDispatcherAuthorized(PropertyDto prop, String dispatcher) {
return (prop.getResourceId() != null && perProjectDispatchers.contains(dispatcher)) || globalDispatchers.contains(dispatcher);
return (prop.getResourceId() != null && dispatchers.getProjectDispatchers().contains(dispatcher)) || dispatchers.getGlobalDispatchers().contains(dispatcher);
}

private Map<Long, ComponentDto> searchProjects(DbSession dbSession, List<PropertyDto> properties) {
Expand Down
Expand Up @@ -25,6 +25,7 @@ public class NotificationWsModule extends Module {
@Override
protected void configureModule() {
add(
DispatchersImpl.class,
// WS
NotificationsWs.class,
AddAction.class,
Expand Down
Expand Up @@ -42,33 +42,30 @@

import static java.util.Optional.empty;
import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION;
import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION;
import static org.sonar.server.ws.WsUtils.checkFound;
import static org.sonar.server.ws.WsUtils.checkRequest;
import static org.sonar.server.notification.ws.NotificationsWsParameters.ACTION_REMOVE;
import static org.sonar.server.notification.ws.NotificationsWsParameters.PARAM_CHANNEL;
import static org.sonar.server.notification.ws.NotificationsWsParameters.PARAM_LOGIN;
import static org.sonar.server.notification.ws.NotificationsWsParameters.PARAM_PROJECT;
import static org.sonar.server.notification.ws.NotificationsWsParameters.PARAM_TYPE;
import static org.sonar.server.ws.WsUtils.checkFound;
import static org.sonar.server.ws.WsUtils.checkRequest;

public class RemoveAction implements NotificationsWsAction {
private final NotificationCenter notificationCenter;
private final NotificationUpdater notificationUpdater;
private final Dispatchers dispatchers;
private final DbClient dbClient;
private final ComponentFinder componentFinder;
private final UserSession userSession;
private final List<String> globalDispatchers;
private final List<String> projectDispatchers;

public RemoveAction(NotificationCenter notificationCenter, NotificationUpdater notificationUpdater, DbClient dbClient, ComponentFinder componentFinder, UserSession userSession) {
public RemoveAction(NotificationCenter notificationCenter, NotificationUpdater notificationUpdater, Dispatchers dispatchers, DbClient dbClient, ComponentFinder componentFinder,
UserSession userSession) {
this.notificationCenter = notificationCenter;
this.notificationUpdater = notificationUpdater;
this.dispatchers = dispatchers;
this.dbClient = dbClient;
this.componentFinder = componentFinder;
this.userSession = userSession;
this.globalDispatchers = notificationCenter.getDispatcherKeysForProperty(GLOBAL_NOTIFICATION, "true");
this.projectDispatchers = notificationCenter.getDispatcherKeysForProperty(PER_PROJECT_NOTIFICATION, "true");
}

@Override
Expand Down Expand Up @@ -100,8 +97,8 @@ public void define(WebService.NewController context) {
" <li>Global notifications: %s</li>" +
" <li>Per project notifications: %s</li>" +
"</ul>",
globalDispatchers.stream().sorted().collect(Collectors.joining(", ")),
projectDispatchers.stream().sorted().collect(Collectors.joining(", ")))
dispatchers.getGlobalDispatchers().stream().sorted().collect(Collectors.joining(", ")),
dispatchers.getProjectDispatchers().stream().sorted().collect(Collectors.joining(", ")))
.setRequired(true)
.setExampleValue(MyNewIssuesNotificationDispatcher.KEY);

Expand Down Expand Up @@ -156,15 +153,15 @@ private RemoveRequest toWsRequest(Request request) {
setNullable(request.param(PARAM_LOGIN), remove::setLogin);

if (remove.getProject() == null) {
checkRequest(globalDispatchers.contains(remove.getType()), "Value of parameter '%s' (%s) must be one of: %s",
checkRequest(dispatchers.getGlobalDispatchers().contains(remove.getType()), "Value of parameter '%s' (%s) must be one of: %s",
PARAM_TYPE,
remove.getType(),
globalDispatchers);
dispatchers.getGlobalDispatchers());
} else {
checkRequest(projectDispatchers.contains(remove.getType()), "Value of parameter '%s' (%s) must be one of: %s",
checkRequest(dispatchers.getProjectDispatchers().contains(remove.getType()), "Value of parameter '%s' (%s) must be one of: %s",
PARAM_TYPE,
remove.getType(),
projectDispatchers);
dispatchers.getProjectDispatchers());
}

return remove;
Expand Down
Expand Up @@ -44,6 +44,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang.StringUtils.substring;
import static org.apache.commons.lang.StringUtils.substringAfterLast;
import static org.apache.commons.lang.StringUtils.substringBeforeLast;
Expand All @@ -59,28 +60,33 @@ public class WebServiceEngine implements LocalConnector, Startable {

private static final Logger LOGGER = Loggers.get(WebServiceEngine.class);

private final WebService.Context context;
private final WebService[] webServices;

private WebService.Context context;

public WebServiceEngine(WebService[] webServices) {
context = new WebService.Context();
for (WebService webService : webServices) {
webService.define(context);
}
this.webServices = webServices;
}

@Override
public void start() {
// Force execution of constructor to be sure that web services
// are validated and initialized at server startup.
context = new WebService.Context();
for (WebService webService : webServices) {
webService.define(context);
}
}

@Override
public void stop() {
// nothing
}

private WebService.Context getContext() {
return requireNonNull(context, "Web services has not yet been initialized");
}

List<WebService.Controller> controllers() {
return context.controllers();
return getContext().controllers();
}

@Override
Expand Down Expand Up @@ -117,7 +123,7 @@ public void execute(Request request, Response response) {
private WebService.Action getAction(ActionExtractor actionExtractor) {
String controllerPath = actionExtractor.getController();
String actionKey = actionExtractor.getAction();
WebService.Controller controller = context.controller(controllerPath);
WebService.Controller controller = getContext().controller(controllerPath);
return controller == null ? null : controller.action(actionKey);
}

Expand Down

0 comments on commit 4a85b3c

Please sign in to comment.