List<PropertyDto> result = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setKey(String.join(".", PROP_NOTIFICATION_PREFIX, dispatcher, channel))
.setComponentId(component == null ? null : component.getId())
- .setUserId((int) userId)
+ .setUserId(userId)
.build(), dbSession).stream()
.filter(prop -> component == null ? prop.getResourceId() == null : prop.getResourceId() != null)
.collect(MoreCollectors.toList());
List<PropertyDto> result = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setKey(String.join(".", PROP_NOTIFICATION_PREFIX, dispatcher, channel))
.setComponentId(component == null ? null : component.getId())
- .setUserId((int) userId)
+ .setUserId(userId)
.build(), dbSession);
assertThat(result).isEmpty();
}
import org.sonar.db.component.ComponentDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.property.PropertyQuery;
-import org.sonar.server.user.UserSession;
+import org.sonar.db.user.UserDto;
import static com.google.common.base.Preconditions.checkArgument;
private static final String PROP_NOTIFICATION_PREFIX = "notification";
private static final String PROP_NOTIFICATION_VALUE = "true";
- private final UserSession userSession;
private final DbClient dbClient;
- public NotificationUpdater(UserSession userSession, DbClient dbClient) {
- this.userSession = userSession;
+ public NotificationUpdater(DbClient dbClient) {
this.dbClient = dbClient;
}
/**
- * Add a notification to the current authenticated user.
- * Does nothing if the user is not authenticated.
+ * Add a notification to a user.
*/
- public void add(DbSession dbSession, String channel, String dispatcher, @Nullable ComponentDto project) {
- if (!userSession.isLoggedIn()) {
- return;
- }
-
+ public void add(DbSession dbSession, String channel, String dispatcher, UserDto user, @Nullable ComponentDto project) {
String key = String.join(".", PROP_NOTIFICATION_PREFIX, dispatcher, channel);
Long projectId = project == null ? null : project.getId();
PropertyQuery.builder()
.setKey(key)
.setComponentId(projectId)
- .setUserId(userSession.getUserId())
+ .setUserId(user.getId())
.build(),
dbSession).stream()
.filter(notificationScope(project))
dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto()
.setKey(key)
- .setUserId(userSession.getUserId())
+ .setUserId(user.getId())
.setValue(PROP_NOTIFICATION_VALUE)
.setResourceId(projectId));
}
/**
- * Remove a notification to the current authenticated user.
- * Does nothing if the user is not authenticated.
+ * Remove a notification from a user.
*/
- public void remove(DbSession dbSession, String channel, String dispatcher, @Nullable ComponentDto project) {
- if (!userSession.isLoggedIn()) {
- return;
- }
-
+ public void remove(DbSession dbSession, String channel, String dispatcher, UserDto user, @Nullable ComponentDto project) {
String key = String.join(".", PROP_NOTIFICATION_PREFIX, dispatcher, channel);
Long projectId = project == null ? null : project.getId();
PropertyQuery.builder()
.setKey(key)
.setComponentId(projectId)
- .setUserId(userSession.getUserId())
+ .setUserId(user.getId())
.build(),
dbSession).stream()
.filter(notificationScope(project))
dbClient.propertiesDao().delete(dbSession, new PropertyDto()
.setKey(key)
- .setUserId(userSession.getUserId())
+ .setUserId(user.getId())
.setValue(PROP_NOTIFICATION_VALUE)
.setResourceId(projectId));
}
import java.util.List;
import java.util.Optional;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.stream.Stream;
+import javax.annotation.CheckForNull;
import org.sonar.api.notifications.NotificationChannel;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
+import org.sonar.db.user.UserDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.issue.notification.MyNewIssuesNotificationDispatcher;
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;
+import static org.sonar.server.ws.WsUtils.checkFound;
import static org.sonar.server.ws.WsUtils.checkRequest;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.ACTION_ADD;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_CHANNEL;
+import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_LOGIN;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_PROJECT;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_TYPE;
public void define(WebService.NewController context) {
WebService.NewAction action = context.createAction(ACTION_ADD)
.setDescription("Add a notification for the authenticated user.<br>" +
- "Requires authentication. If a project is provided, requires the 'Browse' permission on the specified project.")
+ "Requires one of the following permissions:" +
+ "<ul>" +
+ " <li>Authentication if no login is provided. If a project is provided, requires the 'Browse' permission on the specified project.</li>" +
+ " <li>System administration if a login is provided. If a project is provided, requires the 'Browse' permission on the specified project.</li>" +
+ "</ul>")
.setSince("6.3")
.setPost(true)
.setHandler(this);
String.join(", ", projectDispatchers))
.setRequired(true)
.setExampleValue(MyNewIssuesNotificationDispatcher.KEY);
+
+ action.createParam(PARAM_LOGIN)
+ .setDescription("User login")
+ .setSince("6.4");
}
@Override
public void handle(Request request, Response response) throws Exception {
- Stream.of(request)
- .map(toWsRequest())
- .peek(checkPermissions())
- .forEach(add());
+ AddRequest addRequest = toWsRequest(request);
+ add(addRequest);
response.noContent();
}
- private Consumer<AddRequest> add() {
- return request -> {
- try (DbSession dbSession = dbClient.openSession(false)) {
- Optional<ComponentDto> project = searchProject(dbSession, request);
- notificationUpdater.add(dbSession, request.getChannel(), request.getType(), project.orElse(null));
- dbSession.commit();
- }
- };
+ private void add(AddRequest request) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ checkPermissions(request);
+ UserDto user = getUser(dbSession, request);
+ ComponentDto project = searchProject(dbSession, request);
+ notificationUpdater.add(dbSession, request.getChannel(), request.getType(), user, project);
+ dbSession.commit();
+ }
+ }
+
+ private UserDto getUser(DbSession dbSession, AddRequest request) {
+ String login = request.getLogin() == null ? userSession.getLogin() : request.getLogin();
+ return checkFound(dbClient.userDao().selectByLogin(dbSession, login), "User '%s' not found", login);
}
- private Optional<ComponentDto> searchProject(DbSession dbSession, AddRequest request) {
+ @CheckForNull
+ private ComponentDto searchProject(DbSession dbSession, AddRequest request) {
Optional<ComponentDto> project = request.getProject() == null ? empty() : Optional.of(componentFinder.getByKey(dbSession, request.getProject()));
project.ifPresent(p -> checkRequest(Qualifiers.PROJECT.equals(p.qualifier()) && Scopes.PROJECT.equals(p.scope()),
"Component '%s' must be a project", request.getProject()));
- return project;
+ return project.orElse(null);
}
- private Consumer<AddRequest> checkPermissions() {
- return request -> userSession.checkLoggedIn();
+ private void checkPermissions(AddRequest request) {
+ if (request.getLogin() == null) {
+ userSession.checkLoggedIn();
+ } else {
+ userSession.checkIsSystemAdministrator();
+ }
}
- private Function<Request, AddRequest> toWsRequest() {
- return request -> {
- AddRequest.Builder requestBuilder = AddRequest.builder()
- .setType(request.mandatoryParam(PARAM_TYPE))
- .setChannel(request.mandatoryParam(PARAM_CHANNEL));
- String project = request.param(PARAM_PROJECT);
- setNullable(project, requestBuilder::setProject);
- AddRequest wsRequest = requestBuilder.build();
-
- if (wsRequest.getProject() == null) {
- checkRequest(globalDispatchers.contains(wsRequest.getType()), "Value of parameter '%s' (%s) must be one of: %s",
- PARAM_TYPE,
- wsRequest.getType(),
- globalDispatchers);
- } else {
- checkRequest(projectDispatchers.contains(wsRequest.getType()), "Value of parameter '%s' (%s) must be one of: %s",
- PARAM_TYPE,
- wsRequest.getType(),
- projectDispatchers);
- }
-
- return wsRequest;
- };
+ private AddRequest toWsRequest(Request request) {
+ AddRequest.Builder requestBuilder = AddRequest.builder()
+ .setType(request.mandatoryParam(PARAM_TYPE))
+ .setChannel(request.mandatoryParam(PARAM_CHANNEL));
+ setNullable(request.param(PARAM_PROJECT), requestBuilder::setProject);
+ setNullable(request.param(PARAM_LOGIN), requestBuilder::setLogin);
+ AddRequest wsRequest = requestBuilder.build();
+
+ if (wsRequest.getProject() == null) {
+ checkRequest(globalDispatchers.contains(wsRequest.getType()), "Value of parameter '%s' (%s) must be one of: %s",
+ PARAM_TYPE,
+ wsRequest.getType(),
+ globalDispatchers);
+ } else {
+ checkRequest(projectDispatchers.contains(wsRequest.getType()), "Value of parameter '%s' (%s) must be one of: %s",
+ PARAM_TYPE,
+ wsRequest.getType(),
+ projectDispatchers);
+ }
+
+ return wsRequest;
}
}
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;
import java.util.function.UnaryOperator;
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.notification.NotificationCenter;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Notifications.ListResponse;
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.sonarqube.ws.client.notification.NotificationsWsParameters.ACTION_LIST;
+import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_LOGIN;
public class ListAction implements NotificationsWsAction {
private static final Splitter PROPERTY_KEY_SPLITTER = Splitter.on(".");
@Override
public void define(WebService.NewController context) {
- context.createAction(ACTION_LIST)
+ WebService.NewAction action = context.createAction(ACTION_LIST)
.setDescription("List notifications of the authenticated user.<br>" +
- "Requires authentication.")
+ "Requires one of the following permissions:" +
+ "<ul>" +
+ " <li>Authentication if no login is provided</li>" +
+ " <li>System administration if a login is provided</li>" +
+ "</ul>")
.setSince("6.3")
.setResponseExample(getClass().getResource("list-example.json"))
.setHandler(this);
+
+ action.createParam(PARAM_LOGIN)
+ .setDescription("User login")
+ .setSince("6.4");
}
@Override
public void handle(Request request, Response response) throws Exception {
- ListResponse listResponse = Stream.of(request)
- .peek(checkPermissions())
- .map(search())
- .collect(toOneElement());
+ ListResponse listResponse = search(request);
writeProtobuf(listResponse, request, response);
}
- private Function<Request, ListResponse> search() {
- return request -> {
- try (DbSession dbSession = dbClient.openSession(false)) {
- return Stream
- .of(ListResponse.newBuilder())
- .map(r -> r.addAllChannels(channels))
- .map(r -> r.addAllGlobalTypes(globalDispatchers))
- .map(r -> r.addAllPerProjectTypes(perProjectDispatchers))
- .map(addNotifications(dbSession))
- .map(ListResponse.Builder::build)
- .collect(toOneElement());
- }
- };
+ private ListResponse search(Request request) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ checkPermissions(request);
+ UserDto user = getUser(dbSession, request);
+
+ return Stream
+ .of(ListResponse.newBuilder())
+ .map(r -> r.addAllChannels(channels))
+ .map(r -> r.addAllGlobalTypes(globalDispatchers))
+ .map(r -> r.addAllPerProjectTypes(perProjectDispatchers))
+ .map(addNotifications(dbSession, user))
+ .map(ListResponse.Builder::build)
+ .collect(toOneElement());
+ }
+ }
+
+ private UserDto getUser(DbSession dbSession, Request request) {
+ String login = request.param(PARAM_LOGIN) == null ? userSession.getLogin() : request.param(PARAM_LOGIN);
+ return checkFound(dbClient.userDao().selectByLogin(dbSession, login), "User '%s' not found", login);
}
- private UnaryOperator<ListResponse.Builder> addNotifications(DbSession dbSession) {
+ private UnaryOperator<ListResponse.Builder> addNotifications(DbSession dbSession, UserDto user) {
return response -> {
- List<PropertyDto> properties = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setUserId(userSession.getUserId()).build(), dbSession);
+ List<PropertyDto> properties = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setUserId(user.getId()).build(), dbSession);
Map<Long, ComponentDto> componentsById = searchProjects(dbSession, properties);
Map<String, OrganizationDto> organizationsByUuid = getOrganizations(dbSession, componentsById.values());
.setProjectName(project.name());
}
- private Consumer<Request> checkPermissions() {
- return request -> userSession.checkLoggedIn();
+ private void checkPermissions(Request request) {
+ if (request.param(PARAM_LOGIN) == null) {
+ userSession.checkLoggedIn();
+ } else {
+ userSession.checkIsSystemAdministrator();
+ }
}
}
import java.util.List;
import java.util.Optional;
-import java.util.function.Consumer;
-import java.util.function.Function;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
import org.sonar.api.notifications.NotificationChannel;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
+import org.sonar.db.user.UserDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.issue.notification.MyNewIssuesNotificationDispatcher;
import org.sonar.server.notification.NotificationCenter;
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.sonarqube.ws.client.notification.NotificationsWsParameters.ACTION_REMOVE;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_CHANNEL;
+import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_LOGIN;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_PROJECT;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_TYPE;
public void define(WebService.NewController context) {
WebService.NewAction action = context.createAction(ACTION_REMOVE)
.setDescription("Remove a notification for the authenticated user.<br>" +
- "Requires authentication. If a project is provided, requires the 'Browse' permission on the specified project.")
+ "Requires one of the following permissions:" +
+ "<ul>" +
+ " <li>Authentication if no login is provided</li>" +
+ " <li>System administration if a login is provided</li>" +
+ "</ul>")
.setSince("6.3")
.setPost(true)
.setHandler(this);
projectDispatchers.stream().sorted().collect(Collectors.joining(", ")))
.setRequired(true)
.setExampleValue(MyNewIssuesNotificationDispatcher.KEY);
+
+ action.createParam(PARAM_LOGIN)
+ .setDescription("User login")
+ .setSince("6.4");
}
@Override
public void handle(Request request, Response response) throws Exception {
- Stream.of(request)
- .map(toWsRequest())
- .peek(checkPermissions())
- .forEach(remove());
+ RemoveRequest removeRequest = toWsRequest(request);
+ remove(removeRequest);
response.noContent();
}
- private Consumer<RemoveRequest> remove() {
- return request -> {
- try (DbSession dbSession = dbClient.openSession(false)) {
- Optional<ComponentDto> project = searchProject(dbSession, request);
- notificationUpdater.remove(dbSession, request.getChannel(), request.getType(), project.orElse(null));
- dbSession.commit();
- }
- };
+ private void remove(RemoveRequest request) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ checkPermissions(request);
+ UserDto user = getUser(dbSession, request);
+ Optional<ComponentDto> project = searchProject(dbSession, request);
+ notificationUpdater.remove(dbSession, request.getChannel(), request.getType(), user, project.orElse(null));
+ dbSession.commit();
+ }
+ }
+
+ private UserDto getUser(DbSession dbSession, RemoveRequest request) {
+ String login = request.getLogin() == null ? userSession.getLogin() : request.getLogin();
+ return checkFound(dbClient.userDao().selectByLogin(dbSession, login), "User '%s' not found", login);
}
private Optional<ComponentDto> searchProject(DbSession dbSession, RemoveRequest request) {
return project;
}
- private Consumer<RemoveRequest> checkPermissions() {
- return request -> userSession.checkLoggedIn();
+ private void checkPermissions(RemoveRequest request) {
+ if (request.getLogin() == null) {
+ userSession.checkLoggedIn();
+ } else {
+ userSession.checkIsSystemAdministrator();
+ }
}
- private Function<Request, RemoveRequest> toWsRequest() {
- return request -> {
- RemoveRequest.Builder requestBuilder = RemoveRequest.builder()
- .setType(request.mandatoryParam(PARAM_TYPE))
- .setChannel(request.mandatoryParam(PARAM_CHANNEL));
- String project = request.param(PARAM_PROJECT);
- setNullable(project, requestBuilder::setProject);
- RemoveRequest wsRequest = requestBuilder.build();
-
- if (wsRequest.getProject() == null) {
- checkRequest(globalDispatchers.contains(wsRequest.getType()), "Value of parameter '%s' (%s) must be one of: %s",
- PARAM_TYPE,
- wsRequest.getType(),
- globalDispatchers);
- } else {
- checkRequest(projectDispatchers.contains(wsRequest.getType()), "Value of parameter '%s' (%s) must be one of: %s",
- PARAM_TYPE,
- wsRequest.getType(),
- projectDispatchers);
- }
-
- return wsRequest;
- };
+ private RemoveRequest toWsRequest(Request request) {
+ RemoveRequest.Builder requestBuilder = RemoveRequest.builder()
+ .setType(request.mandatoryParam(PARAM_TYPE))
+ .setChannel(request.mandatoryParam(PARAM_CHANNEL));
+ setNullable(request.param(PARAM_PROJECT), requestBuilder::setProject);
+ setNullable(request.param(PARAM_LOGIN), requestBuilder::setLogin);
+ RemoveRequest wsRequest = requestBuilder.build();
+
+ if (wsRequest.getProject() == null) {
+ checkRequest(globalDispatchers.contains(wsRequest.getType()), "Value of parameter '%s' (%s) must be one of: %s",
+ PARAM_TYPE,
+ wsRequest.getType(),
+ globalDispatchers);
+ } else {
+ checkRequest(projectDispatchers.contains(wsRequest.getType()), "Value of parameter '%s' (%s) must be one of: %s",
+ PARAM_TYPE,
+ wsRequest.getType(),
+ projectDispatchers);
+ }
+
+ return wsRequest;
}
}
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
+import org.sonar.db.user.UserDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.notification.NotificationCenter;
import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION;
import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_CHANNEL;
+import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_LOGIN;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_PROJECT;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_TYPE;
private static final String NOTIF_MY_NEW_ISSUES = "Dispatcher1";
private static final String NOTIF_NEW_ISSUES = "Dispatcher2";
private static final String NOTIF_NEW_QUALITY_GATE_STATUS = "Dispatcher3";
+ private static final String USER_LOGIN = "george.orwell";
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Rule
- public UserSessionRule userSession = UserSessionRule.standalone().logIn().setUserId(123);
+ public UserSessionRule userSession;
@Rule
public DbTester db = DbTester.create();
private DbClient dbClient = db.getDbClient();
// default channel, based on class simple name
private NotificationChannel defaultChannel = new FakeNotificationChannel("EmailNotificationChannel");
+ private UserDto user;
+
private NotificationCenter notificationCenter;
private AddAction underTest;
private WsActionTester ws;
.setProperty(GLOBAL_NOTIFICATION, "true")
.setProperty(PER_PROJECT_NOTIFICATION, "true");
+ user = db.users().insertUser(USER_LOGIN);
+ userSession = UserSessionRule.standalone().logIn(user);
+
notificationCenter = new NotificationCenter(
new NotificationDispatcherMetadata[] {metadata1, metadata2, metadata3},
new NotificationChannel[] {emailChannel, twitterChannel, defaultChannel});
- underTest = new AddAction(notificationCenter, new NotificationUpdater(userSession, dbClient), dbClient, new ComponentFinder(dbClient), userSession);
+ underTest = new AddAction(notificationCenter, new NotificationUpdater(dbClient), dbClient, new ComponentFinder(dbClient), userSession);
ws = new WsActionTester(underTest);
}
assertThat(result.getStatus()).isEqualTo(HTTP_NO_CONTENT);
}
+ @Test
+ public void add_a_notification_to_a_user_as_system_administrator() {
+ userSession.logIn().setSystemAdministrator();
+
+ call(request.setLogin(user.getLogin()));
+
+ db.notifications().assertExists(defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user.getId(), null);
+ }
+
+ @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(request.setLogin("LOGIN 404"));
+ }
+
+ @Test
+ public void fail_if_login_provided_and_not_system_administrator() {
+ userSession.logIn().setNonSystemAdministrator();
+
+ expectedException.expect(ForbiddenException.class);
+
+ call(request.setLogin(user.getLogin()));
+ }
+
@Test
public void fail_when_notification_already_exists() {
call(request);
request.setParam(PARAM_TYPE, wsRequest.getType());
setNullable(wsRequest.getChannel(), channel -> request.setParam(PARAM_CHANNEL, channel));
setNullable(wsRequest.getProject(), project -> request.setParam(PARAM_PROJECT, project));
+ setNullable(wsRequest.getLogin(), login -> request.setParam(PARAM_LOGIN, login));
return request.execute();
}
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.permission.UserPermissionDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.notification.NotificationCenter;
import org.sonar.server.notification.NotificationDispatcherMetadata;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Rule
- public UserSessionRule userSession = UserSessionRule.standalone().logIn().setUserId(123);
+ public UserSessionRule userSession;
@Rule
public DbTester db = DbTester.create();
private NotificationChannel emailChannel = new FakeNotificationChannel("EmailChannel");
private NotificationChannel twitterChannel = new FakeNotificationChannel("TwitterChannel");
+ private UserDto user;
private NotificationUpdater notificationUpdater;
.setProperty(GLOBAL_NOTIFICATION, "true")
.setProperty(PER_PROJECT_NOTIFICATION, "true");
+ user = db.users().insertUser();
+ userSession = UserSessionRule.standalone().logIn(user);
+
NotificationCenter notificationCenter = new NotificationCenter(
new NotificationDispatcherMetadata[] {metadata1, metadata2, metadata3},
new NotificationChannel[] {emailChannel, twitterChannel});
- notificationUpdater = new NotificationUpdater(userSession, dbClient);
+ notificationUpdater = new NotificationUpdater(dbClient);
ListAction underTest = new ListAction(notificationCenter, dbClient, userSession);
ws = new WsActionTester(underTest);
}
public void filter_unauthorized_projects() {
ComponentDto project = addComponent(ComponentTesting.newPrivateProjectDto(db.organizations().insert()).setKey("K1"));
ComponentDto anotherProject = db.components().insertPrivateProject();
- notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, project);
- notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, anotherProject);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, project);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, anotherProject);
dbSession.commit();
ListResponse result = call();
@Test
public void filter_channels() {
- notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, null);
- notificationUpdater.add(dbSession, "Unknown Channel", NOTIF_MY_NEW_ISSUES, null);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
+ notificationUpdater.add(dbSession, "Unknown Channel", NOTIF_MY_NEW_ISSUES, user, null);
dbSession.commit();
ListResponse result = call();
@Test
public void filter_overall_dispatchers() {
- notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, null);
- notificationUpdater.add(dbSession, emailChannel.getKey(), "Unknown Notification", null);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), "Unknown Notification", user, null);
dbSession.commit();
ListResponse result = call();
@Test
public void filter_per_project_dispatchers() {
ComponentDto project = addComponent(ComponentTesting.newPrivateProjectDto(db.organizations().insert()).setKey("K1"));
- notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, project);
- notificationUpdater.add(dbSession, emailChannel.getKey(), "Unknown Notification", project);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, project);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), "Unknown Notification", user, project);
dbSession.commit();
ListResponse result = call();
public void order_with_global_then_by_channel_and_dispatcher() {
OrganizationDto organization = db.organizations().insert();
ComponentDto project = addComponent(ComponentTesting.newPrivateProjectDto(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);
- notificationUpdater.add(dbSession, twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, project);
- notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, project);
- notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_NEW_QUALITY_GATE_STATUS, project);
+ notificationUpdater.add(dbSession, twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_NEW_ISSUES, user, null);
+ notificationUpdater.add(dbSession, twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, project);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, project);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_NEW_QUALITY_GATE_STATUS, user, project);
dbSession.commit();
ListResponse result = call();
tuple(twitterChannel.getKey(), organization.getKey(), NOTIF_MY_NEW_ISSUES, "K1"));
}
+ @Test
+ public void list_user_notifications_as_system_admin() {
+ userSession.logIn().setSystemAdministrator();
+
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_NEW_ISSUES, user, null);
+ dbSession.commit();
+
+ ListResponse result = call(user.getLogin());
+
+ assertThat(result.getNotificationsList())
+ .extracting(Notification::getType)
+ .containsOnly(NOTIF_MY_NEW_ISSUES, NOTIF_NEW_ISSUES);
+ }
+
+ @Test
+ public void fail_if_login_and_not_system_admin() {
+ userSession.logIn().setNonSystemAdministrator();
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
+ dbSession.commit();
+
+ expectedException.expect(ForbiddenException.class);
+
+ call(user.getLogin());
+ }
+
+ @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");
+ }
+
@Test
public void json_example() {
OrganizationDto organization = db.organizations().insertForKey("my-org-1");
ComponentDto project = addComponent(ComponentTesting.newPrivateProjectDto(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);
- notificationUpdater.add(dbSession, twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, project);
- notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, project);
- notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_NEW_QUALITY_GATE_STATUS, project);
+ notificationUpdater.add(dbSession, twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_NEW_ISSUES, user, null);
+ notificationUpdater.add(dbSession, twitterChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, project);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, project);
+ notificationUpdater.add(dbSession, emailChannel.getKey(), NOTIF_NEW_QUALITY_GATE_STATUS, user, project);
dbSession.commit();
String result = ws.newRequest().execute().getInput();
assertThat(definition.key()).isEqualTo("list");
assertThat(definition.isPost()).isFalse();
- assertThat(definition.params()).isEmpty();
+ assertThat(definition.since()).isEqualTo("6.3");
assertThat(definition.responseExampleAsString()).isNotEmpty();
+ assertThat(definition.params()).hasSize(1);
+
+ WebService.Param loginParam = definition.param("login");
+ assertThat(loginParam.since()).isEqualTo("6.4");
+ assertThat(loginParam.isRequired()).isFalse();
}
@Test
return ws.newRequest().executeProtobuf(ListResponse.class);
}
+ private ListResponse call(String login) {
+ return ws.newRequest().setParam("login", login).executeProtobuf(ListResponse.class);
+ }
+
private ComponentDto addComponent(ComponentDto component) {
db.components().insertComponent(component);
- dbClient.userPermissionDao().insert(dbSession, new UserPermissionDto(component.getOrganizationUuid(), UserRole.USER, userSession.getUserId(), component.getId()));
+ dbClient.userPermissionDao().insert(dbSession, new UserPermissionDto(component.getOrganizationUuid(), UserRole.USER, user.getId(), component.getId()));
db.commit();
return component;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
+import org.sonar.db.user.UserDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.notification.NotificationCenter;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;
-import org.sonarqube.ws.client.notification.AddRequest;
+import org.sonarqube.ws.client.notification.RemoveRequest;
import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION;
import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_CHANNEL;
+import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_LOGIN;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_PROJECT;
import static org.sonarqube.ws.client.notification.NotificationsWsParameters.PARAM_TYPE;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Rule
- public UserSessionRule userSession = UserSessionRule.standalone().logIn().setUserId(123);
+ public UserSessionRule userSession;
@Rule
public DbTester db = DbTester.create();
private DbClient dbClient = db.getDbClient();
private RemoveAction underTest;
private WsActionTester ws;
- private AddRequest.Builder request = AddRequest.builder()
- .setType(NOTIF_MY_NEW_ISSUES);
+ private RemoveRequest.Builder request = RemoveRequest.builder().setType(NOTIF_MY_NEW_ISSUES);
+
+ private UserDto user;
@Before
public void setUp() {
+ user = db.users().insertUser();
+ userSession = UserSessionRule.standalone().logIn(user);
+
NotificationDispatcherMetadata metadata1 = NotificationDispatcherMetadata.create(NOTIF_MY_NEW_ISSUES)
.setProperty(GLOBAL_NOTIFICATION, "true")
.setProperty(PER_PROJECT_NOTIFICATION, "true");
notificationCenter = new NotificationCenter(
new NotificationDispatcherMetadata[] {metadata1, metadata2, metadata3},
new NotificationChannel[] {emailChannel, twitterChannel, defaultChannel});
- notificationUpdater = new NotificationUpdater(userSession, dbClient);
+ notificationUpdater = new NotificationUpdater(dbClient);
underTest = new RemoveAction(notificationCenter, notificationUpdater, dbClient, new ComponentFinder(dbClient), userSession);
ws = new WsActionTester(underTest);
}
@Test
public void remove_to_email_channel_by_default() {
- notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, null);
+ notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
dbSession.commit();
call(request);
- db.notifications().assertDoesNotExist(defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, userSession.getUserId(), null);
+ db.notifications().assertDoesNotExist(defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user.getId(), null);
}
@Test
public void remove_from_a_specific_channel() {
- notificationUpdater.add(dbSession, twitterChannel.getKey(), NOTIF_NEW_QUALITY_GATE_STATUS, null);
+ notificationUpdater.add(dbSession, twitterChannel.getKey(), NOTIF_NEW_QUALITY_GATE_STATUS, user, null);
dbSession.commit();
call(request.setType(NOTIF_NEW_QUALITY_GATE_STATUS).setChannel(twitterChannel.getKey()));
- db.notifications().assertDoesNotExist(twitterChannel.getKey(), NOTIF_NEW_QUALITY_GATE_STATUS, userSession.getUserId(), null);
+ db.notifications().assertDoesNotExist(twitterChannel.getKey(), NOTIF_NEW_QUALITY_GATE_STATUS, user.getId(), null);
}
@Test
public void remove_a_project_notification() {
ComponentDto project = db.components().insertPrivateProject();
- notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, project);
+ notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, project);
dbSession.commit();
call(request.setProject(project.getKey()));
- db.notifications().assertDoesNotExist(defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, userSession.getUserId(), project);
+ db.notifications().assertDoesNotExist(defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user.getId(), project);
}
@Test
public void fail_when_remove_a_global_notification_when_a_project_one_exists() {
ComponentDto project = db.components().insertPrivateProject();
- notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, project);
+ notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, project);
dbSession.commit();
expectedException.expect(IllegalArgumentException.class);
@Test
public void fail_when_remove_a_project_notification_when_a_global_one_exists() {
ComponentDto project = db.components().insertPrivateProject();
- notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, null);
+ notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
dbSession.commit();
expectedException.expect(IllegalArgumentException.class);
@Test
public void http_no_content() {
- notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, null);
+ notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
dbSession.commit();
TestResponse result = call(request);
assertThat(result.getStatus()).isEqualTo(HTTP_NO_CONTENT);
}
+ @Test
+ public void remove_a_notification_from_a_user_as_system_administrator() {
+ notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
+ db.notifications().assertExists(defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user.getId(), null);
+ userSession.logIn().setSystemAdministrator();
+ dbSession.commit();
+
+ call(request.setLogin(user.getLogin()));
+
+ db.notifications().assertDoesNotExist(defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user.getId(), null);
+ }
+
+ @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(request.setLogin("LOGIN 404"));
+ }
+
+ @Test
+ public void fail_if_login_provided_and_not_system_administrator() {
+ userSession.logIn().setNonSystemAdministrator();
+ notificationUpdater.add(dbSession, defaultChannel.getKey(), NOTIF_MY_NEW_ISSUES, user, null);
+ dbSession.commit();
+
+ expectedException.expect(ForbiddenException.class);
+
+ call(request.setLogin(user.getLogin()));
+ }
+
@Test
public void fail_when_notification_does_not_exist() {
expectedException.expect(IllegalArgumentException.class);
call(request);
}
- private TestResponse call(AddRequest.Builder wsRequestBuilder) {
- AddRequest wsRequest = wsRequestBuilder.build();
+ private TestResponse call(RemoveRequest.Builder wsRequestBuilder) {
+ RemoveRequest wsRequest = wsRequestBuilder.build();
+
TestRequest request = ws.newRequest();
request.setParam(PARAM_TYPE, wsRequest.getType());
setNullable(wsRequest.getChannel(), channel -> request.setParam(PARAM_CHANNEL, channel));
setNullable(wsRequest.getProject(), project -> request.setParam(PARAM_PROJECT, project));
+ setNullable(wsRequest.getLogin(), login -> request.setParam(PARAM_LOGIN, login));
return request.execute();
}
private final String type;
private final String channel;
private final String project;
+ private final String login;
private AddRequest(Builder builder) {
this.channel = builder.channel;
this.type = builder.type;
this.project = builder.project;
+ this.login = builder.login;
}
public String getType() {
return project;
}
+ @CheckForNull
+ public String getLogin() {
+ return login;
+ }
+
public static Builder builder() {
return new Builder();
}
private String type;
private String channel;
private String project;
+ private String login;
private Builder() {
// enforce factory method
return this;
}
+ public Builder setLogin(@Nullable String login) {
+ this.login = login;
+ return this;
+ }
+
public AddRequest build() {
requireNonNull(type, "Notification is required");
return new AddRequest(this);
public static final String PARAM_PROJECT = "project";
public static final String PARAM_CHANNEL = "channel";
public static final String PARAM_TYPE = "type";
+ public static final String PARAM_LOGIN = "login";
private NotificationsWsParameters() {
// prevent instantiation
private final String type;
private final String channel;
private final String project;
+ private final String login;
private RemoveRequest(Builder builder) {
this.channel = builder.channel;
this.type = builder.type;
this.project = builder.project;
+ this.login = builder.login;
}
public String getType() {
return project;
}
+ @CheckForNull
+ public String getLogin() {
+ return login;
+ }
+
public static Builder builder() {
return new Builder();
}
private String type;
private String channel;
private String project;
+ private String login;
private Builder() {
// enforce factory method
return this;
}
+ public Builder setLogin(@Nullable String login) {
+ this.login = login;
+ return this;
+ }
+
public RemoveRequest build() {
requireNonNull(type, "Notification is required");
return new RemoveRequest(this);