diff options
author | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2017-05-12 12:37:11 +0200 |
---|---|---|
committer | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2017-05-12 17:48:36 +0200 |
commit | 44bb86905beb8652719b777ec45befc9d325af01 (patch) | |
tree | 3ce300b306d3b67f0d1e605b127db0f9c804cfd2 /server | |
parent | 2128feb694eeeada2b33fd8772e70cbeb44ed4b8 (diff) | |
download | sonarqube-44bb86905beb8652719b777ec45befc9d325af01.tar.gz sonarqube-44bb86905beb8652719b777ec45befc9d325af01.zip |
SONAR-3915 Give the admin a way to send users issue reports on non favorite projects
Diffstat (limited to 'server')
8 files changed, 339 insertions, 174 deletions
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/notification/NotificationDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/notification/NotificationDbTester.java index 43fbcbb14fd..778019eba87 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/notification/NotificationDbTester.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/notification/NotificationDbTester.java @@ -48,7 +48,7 @@ public class NotificationDbTester { 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()); @@ -60,7 +60,7 @@ public class NotificationDbTester { 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(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationUpdater.java index 3df6ce3c812..8ae948cc9c0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationUpdater.java +++ b/server/sonar-server/src/main/java/org/sonar/server/notification/NotificationUpdater.java @@ -28,7 +28,7 @@ import org.sonar.db.DbSession; 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; @@ -36,23 +36,16 @@ public class NotificationUpdater { 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(); @@ -60,7 +53,7 @@ public class NotificationUpdater { PropertyQuery.builder() .setKey(key) .setComponentId(projectId) - .setUserId(userSession.getUserId()) + .setUserId(user.getId()) .build(), dbSession).stream() .filter(notificationScope(project)) @@ -70,20 +63,15 @@ public class NotificationUpdater { 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(); @@ -91,7 +79,7 @@ public class NotificationUpdater { PropertyQuery.builder() .setKey(key) .setComponentId(projectId) - .setUserId(userSession.getUserId()) + .setUserId(user.getId()) .build(), dbSession).stream() .filter(notificationScope(project)) @@ -100,7 +88,7 @@ public class NotificationUpdater { dbClient.propertiesDao().delete(dbSession, new PropertyDto() .setKey(key) - .setUserId(userSession.getUserId()) + .setUserId(user.getId()) .setValue(PROP_NOTIFICATION_VALUE) .setResourceId(projectId)); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/notification/ws/AddAction.java b/server/sonar-server/src/main/java/org/sonar/server/notification/ws/AddAction.java index 921387991bd..51685ce0c1f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/notification/ws/AddAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/notification/ws/AddAction.java @@ -21,9 +21,7 @@ package org.sonar.server.notification.ws; 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; @@ -33,6 +31,7 @@ import org.sonar.api.server.ws.WebService; 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; @@ -47,9 +46,11 @@ 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.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; @@ -76,7 +77,11 @@ public class AddAction implements NotificationsWsAction { 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); @@ -101,61 +106,71 @@ public class AddAction implements NotificationsWsAction { 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; } } 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 a24d4c8a37a..b1b634b389c 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 @@ -25,7 +25,6 @@ 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; import java.util.function.UnaryOperator; @@ -42,6 +41,7 @@ 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.notification.NotificationCenter; import org.sonar.server.user.UserSession; import org.sonarqube.ws.Notifications.ListResponse; @@ -55,8 +55,10 @@ 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.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("."); @@ -77,42 +79,53 @@ public class ListAction implements NotificationsWsAction { @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()); @@ -195,7 +208,11 @@ public class ListAction implements NotificationsWsAction { .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(); + } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/notification/ws/RemoveAction.java b/server/sonar-server/src/main/java/org/sonar/server/notification/ws/RemoveAction.java index 573f2a7df62..e44841a5bde 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/notification/ws/RemoveAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/notification/ws/RemoveAction.java @@ -21,10 +21,7 @@ package org.sonar.server.notification.ws; 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; @@ -34,6 +31,7 @@ import org.sonar.api.server.ws.WebService; 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; @@ -47,9 +45,11 @@ 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.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; @@ -76,7 +76,11 @@ public class RemoveAction implements NotificationsWsAction { 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); @@ -101,26 +105,33 @@ public class RemoveAction implements NotificationsWsAction { 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) { @@ -130,32 +141,34 @@ public class RemoveAction implements NotificationsWsAction { 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; } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/notification/ws/AddActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/ws/AddActionTest.java index 99b06b03cfa..3cc9e0de99f 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/notification/ws/AddActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/notification/ws/AddActionTest.java @@ -28,8 +28,10 @@ import org.sonar.api.notifications.NotificationChannel; 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; @@ -48,6 +50,7 @@ import static org.sonar.db.component.ComponentTesting.newView; 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; @@ -55,10 +58,11 @@ public class AddActionTest { 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(); @@ -68,6 +72,8 @@ public class AddActionTest { // 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; @@ -86,10 +92,13 @@ public class AddActionTest { .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); } @@ -146,6 +155,34 @@ public class AddActionTest { } @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); @@ -219,6 +256,7 @@ public class AddActionTest { 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(); } 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 83bbe48a3ab..7707606d715 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,9 @@ import org.sonar.db.component.ComponentDto; 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; @@ -57,7 +60,7 @@ public class ListActionTest { @Rule public ExpectedException expectedException = ExpectedException.none(); @Rule - public UserSessionRule userSession = UserSessionRule.standalone().logIn().setUserId(123); + public UserSessionRule userSession; @Rule public DbTester db = DbTester.create(); @@ -66,6 +69,7 @@ public class ListActionTest { private NotificationChannel emailChannel = new FakeNotificationChannel("EmailChannel"); private NotificationChannel twitterChannel = new FakeNotificationChannel("TwitterChannel"); + private UserDto user; private NotificationUpdater notificationUpdater; @@ -82,10 +86,13 @@ public class ListActionTest { .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); } @@ -115,8 +122,8 @@ public class ListActionTest { 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(); @@ -126,8 +133,8 @@ public class ListActionTest { @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(); @@ -137,8 +144,8 @@ public class ListActionTest { @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(); @@ -149,8 +156,8 @@ public class ListActionTest { @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(); @@ -164,12 +171,12 @@ public class ListActionTest { 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(); @@ -186,15 +193,51 @@ public class ListActionTest { } @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(); @@ -210,8 +253,13 @@ public class ListActionTest { 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 @@ -227,9 +275,13 @@ public class ListActionTest { 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; diff --git a/server/sonar-server/src/test/java/org/sonar/server/notification/ws/RemoveActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/notification/ws/RemoveActionTest.java index c467a1cc2af..8cc3f5ccf87 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/notification/ws/RemoveActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/notification/ws/RemoveActionTest.java @@ -29,8 +29,10 @@ 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.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; @@ -40,7 +42,7 @@ import org.sonar.server.tester.UserSessionRule; 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; @@ -49,6 +51,7 @@ import static org.sonar.db.component.ComponentTesting.newView; 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; @@ -59,7 +62,7 @@ public class RemoveActionTest { @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(); @@ -75,11 +78,15 @@ public class RemoveActionTest { 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"); @@ -92,46 +99,46 @@ public class RemoveActionTest { 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); @@ -143,7 +150,7 @@ public class RemoveActionTest { @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); @@ -154,7 +161,7 @@ public class RemoveActionTest { @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); @@ -163,6 +170,39 @@ public class RemoveActionTest { } @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); expectedException.expectMessage("Notification doesn't exist"); @@ -228,12 +268,14 @@ public class RemoveActionTest { 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(); } |