diff options
author | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2016-12-16 15:27:24 +0100 |
---|---|---|
committer | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2016-12-19 10:12:54 +0100 |
commit | 95ab6f6776c4b8e4ead3a4f6215c9c03c9b475c5 (patch) | |
tree | 6c5fc22660b58ba557cab2be13c7d2f9789f79df | |
parent | 6a94b3b976758fb094488c36b9c178e6ef811f81 (diff) | |
download | sonarqube-95ab6f6776c4b8e4ead3a4f6215c9c03c9b475c5.tar.gz sonarqube-95ab6f6776c4b8e4ead3a4f6215c9c03c9b475c5.zip |
SONAR-7288 Create WS api/favorites/remove
11 files changed, 328 insertions, 8 deletions
diff --git a/it/it-tests/src/test/java/it/user/FavoritesWsTest.java b/it/it-tests/src/test/java/it/user/FavoritesWsTest.java index af4dd0740d1..62ec54251e8 100644 --- a/it/it-tests/src/test/java/it/user/FavoritesWsTest.java +++ b/it/it-tests/src/test/java/it/user/FavoritesWsTest.java @@ -28,7 +28,6 @@ import org.junit.ClassRule; import org.junit.Test; import org.sonar.wsclient.Sonar; import org.sonar.wsclient.services.Favourite; -import org.sonar.wsclient.services.FavouriteDeleteQuery; import org.sonar.wsclient.services.FavouriteQuery; import org.sonarqube.ws.client.WsClient; @@ -69,7 +68,7 @@ public class FavoritesWsTest { assertThat(favourites.stream().map(Favourite::getKey)).containsOnly("sample", "sample:src/main/xoo/sample/Sample.xoo"); // DELETE (a favorite) - oldWsClient.delete(new FavouriteDeleteQuery("sample")); + adminClient.favorites().remove("sample"); favourites = oldWsClient.findAll(new FavouriteQuery()); assertThat(favourites.stream().map(Favourite::getKey)).containsOnly("sample:src/main/xoo/sample/Sample.xoo"); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/favorite/FavoriteModule.java b/server/sonar-server/src/main/java/org/sonar/server/favorite/FavoriteModule.java index 7107b6df851..0a0da3ecedd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/favorite/FavoriteModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/favorite/FavoriteModule.java @@ -23,6 +23,7 @@ package org.sonar.server.favorite; import org.sonar.core.platform.Module; import org.sonar.server.favorite.ws.AddAction; import org.sonar.server.favorite.ws.FavoritesWs; +import org.sonar.server.favorite.ws.RemoveAction; public class FavoriteModule extends Module { @@ -31,7 +32,8 @@ public class FavoriteModule extends Module { add( FavoriteUpdater.class, FavoritesWs.class, - AddAction.class); + AddAction.class, + RemoveAction.class); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/favorite/FavoriteUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/favorite/FavoriteUpdater.java index 8aaf771f4dd..540e20fdb7f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/favorite/FavoriteUpdater.java +++ b/server/sonar-server/src/main/java/org/sonar/server/favorite/FavoriteUpdater.java @@ -22,9 +22,13 @@ package org.sonar.server.favorite; import org.sonar.db.DbClient; import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; import org.sonar.db.property.PropertyDto; +import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.user.UserSession; +import static org.sonar.server.ws.WsUtils.checkRequest; + public class FavoriteUpdater { private static final String PROP_FAVORITE_KEY = "favourite"; @@ -39,14 +43,30 @@ public class FavoriteUpdater { /** * Set favorite to the logged in user. If no user is logged, no action is done */ - public void add(DbSession dbSession, long componentDbId) { + public void add(DbSession dbSession, long componentDtoId) { if (!userSession.isLoggedIn()) { return; } dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto() .setKey(PROP_FAVORITE_KEY) - .setResourceId(componentDbId) + .setResourceId(componentDtoId) + .setUserId(Long.valueOf(userSession.getUserId()))); + } + + /** + * Remove a favorite to the logged in user. + * @throws BadRequestException if the component is not a favorite + */ + public void remove(DbSession dbSession, ComponentDto component) { + if (!userSession.isLoggedIn()) { + return; + } + + int result = dbClient.propertiesDao().delete(dbSession, new PropertyDto() + .setKey(PROP_FAVORITE_KEY) + .setResourceId(component.getId()) .setUserId(Long.valueOf(userSession.getUserId()))); + checkRequest(result == 1, "Component '%s' is not a favorite", component.key()); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/favorite/ws/RemoveAction.java b/server/sonar-server/src/main/java/org/sonar/server/favorite/ws/RemoveAction.java new file mode 100644 index 00000000000..32591f84b9e --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/favorite/ws/RemoveAction.java @@ -0,0 +1,82 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.favorite.ws; + +import java.util.function.Consumer; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +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.server.component.ComponentFinder; +import org.sonar.server.favorite.FavoriteUpdater; +import org.sonar.server.user.UserSession; +import org.sonar.server.ws.KeyExamples; + +import static org.sonarqube.ws.client.favorite.FavoritesWsParameters.PARAM_COMPONENT; + +public class RemoveAction implements FavoritesWsAction { + private final UserSession userSession; + private final DbClient dbClient; + private final FavoriteUpdater favoriteUpdater; + private final ComponentFinder componentFinder; + + public RemoveAction(UserSession userSession, DbClient dbClient, FavoriteUpdater favoriteUpdater, ComponentFinder componentFinder) { + this.userSession = userSession; + this.dbClient = dbClient; + this.favoriteUpdater = favoriteUpdater; + this.componentFinder = componentFinder; + } + + @Override + public void define(WebService.NewController context) { + WebService.NewAction action = context.createAction("remove") + .setDescription("Remove a component (project, directory, file etc.) as favorite for the authenticated user.<br>" + + "Requires authentication.") + .setSince("6.3") + .setPost(true) + .setHandler(this); + + action.createParam("component") + .setDescription("Component key") + .setRequired(true) + .setExampleValue(KeyExamples.KEY_PROJECT_EXAMPLE_001); + } + + @Override + public void handle(Request request, Response response) throws Exception { + removeFavorite().accept(request); + response.noContent(); + } + + private Consumer<Request> removeFavorite() { + return request -> { + try (DbSession dbSession = dbClient.openSession(false)) { + ComponentDto component = componentFinder.getByKey(dbSession, request.mandatoryParam(PARAM_COMPONENT)); + userSession + .checkLoggedIn(); + favoriteUpdater.remove(dbSession, component); + dbSession.commit(); + } + }; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/favorite/FavoriteModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/favorite/FavoriteModuleTest.java index 22c4bf1de6c..1affa2ca117 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/favorite/FavoriteModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/favorite/FavoriteModuleTest.java @@ -30,6 +30,6 @@ public class FavoriteModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new FavoriteModule().configure(container); - assertThat(container.size()).isEqualTo(3 + 2); + assertThat(container.size()).isEqualTo(4 + 2); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/favorite/ws/RemoveActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/favorite/ws/RemoveActionTest.java new file mode 100644 index 00000000000..eb0659339a5 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/favorite/ws/RemoveActionTest.java @@ -0,0 +1,131 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.favorite.ws; + +import javax.annotation.Nullable; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.server.ws.WebService; +import org.sonar.db.DbClient; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.server.component.ComponentFinder; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.exceptions.UnauthorizedException; +import org.sonar.server.favorite.FavoriteUpdater; +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 static java.net.HttpURLConnection.HTTP_NO_CONTENT; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.core.util.Protobuf.setNullable; +import static org.sonar.db.component.ComponentTesting.newFileDto; +import static org.sonar.db.component.ComponentTesting.newProjectDto; +import static org.sonarqube.ws.client.favorite.FavoritesWsParameters.PARAM_COMPONENT; + +public class RemoveActionTest { + private static final String PROJECT_KEY = "project-key"; + private static final String PROJECT_UUID = "project-uuid"; + private static final int USER_ID = 123; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + @Rule + public DbTester db = DbTester.create(); + private DbClient dbClient = db.getDbClient(); + + private FavoriteUpdater favoriteUpdater = new FavoriteUpdater(dbClient, userSession); + private WsActionTester ws = new WsActionTester(new RemoveAction(userSession, dbClient, favoriteUpdater, new ComponentFinder(dbClient))); + + @Test + public void remove_a_favorite_project() { + ComponentDto project = insertProjectAndPermissions(); + ComponentDto file = db.components().insertComponent(newFileDto(project)); + db.favorites().add(project, USER_ID); + db.favorites().add(file, USER_ID); + + TestResponse result = call(PROJECT_KEY); + + assertThat(result.getStatus()).isEqualTo(HTTP_NO_CONTENT); + assertThat(db.favorites().hasFavorite(project, USER_ID)).isFalse(); + assertThat(db.favorites().hasFavorite(file, USER_ID)).isTrue(); + } + + @Test + public void fail_if_not_already_a_favorite() { + insertProjectAndPermissions(); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Component '" + PROJECT_KEY + "' is not a favorite"); + + call(PROJECT_KEY); + } + + @Test + public void fail_when_component_is_not_found() { + userSession.login(); + + expectedException.expect(NotFoundException.class); + + call("P42"); + } + + @Test + public void fail_when_user_is_not_authenticated() { + insertProject(); + + expectedException.expect(UnauthorizedException.class); + + call(PROJECT_KEY); + } + + @Test + public void definition() { + WebService.Action definition = ws.getDef(); + + assertThat(definition.key()).isEqualTo("remove"); + assertThat(definition.isPost()).isTrue(); + assertThat(definition.param("component").isRequired()).isTrue(); + } + + private ComponentDto insertProject() { + return db.components().insertComponent(newProjectDto(PROJECT_UUID).setKey(PROJECT_KEY)); + } + + private ComponentDto insertProjectAndPermissions() { + userSession.login().setUserId(USER_ID); + + return insertProject(); + } + + private TestResponse call(@Nullable String componentKey) { + TestRequest request = ws.newRequest(); + setNullable(componentKey, c -> request.setParam(PARAM_COMPONENT, c)); + + return request.execute(); + } +} diff --git a/sonar-db/src/test/java/org/sonar/db/DbTester.java b/sonar-db/src/test/java/org/sonar/db/DbTester.java index 9d4a3e2bd1c..48fbd8494c3 100644 --- a/sonar-db/src/test/java/org/sonar/db/DbTester.java +++ b/sonar-db/src/test/java/org/sonar/db/DbTester.java @@ -65,6 +65,7 @@ import org.sonar.api.utils.log.Loggers; import org.sonar.core.util.SequenceUuidFactory; import org.sonar.db.component.ComponentDbTester; import org.sonar.db.event.EventDbTester; +import org.sonar.db.favorite.FavoriteDbTester; import org.sonar.db.issue.IssueDbTester; import org.sonar.db.organization.OrganizationDbTester; import org.sonar.db.organization.OrganizationDto; @@ -102,6 +103,7 @@ public class DbTester extends ExternalResource { private final UserDbTester userTester; private final ComponentDbTester componentTester; + private final FavoriteDbTester favoriteTester; private final EventDbTester eventTester; private final OrganizationDbTester organizationTester; private final PermissionTemplateDbTester permissionTemplateTester; @@ -116,6 +118,7 @@ public class DbTester extends ExternalResource { initDbClient(); this.userTester = new UserDbTester(this); this.componentTester = new ComponentDbTester(this); + this.favoriteTester = new FavoriteDbTester(this); this.eventTester = new EventDbTester(this); this.organizationTester = new OrganizationDbTester(this); this.permissionTemplateTester = new PermissionTemplateDbTester(this); @@ -190,6 +193,10 @@ public class DbTester extends ExternalResource { return componentTester; } + public FavoriteDbTester favorites() { + return favoriteTester; + } + public EventDbTester events() { return eventTester; } @@ -587,7 +594,7 @@ public class DbTester extends ExternalResource { */ public void assertIndexDoesNotExist(String tableName, String indexName) { try (Connection connection = getConnection(); - ResultSet rs = connection.getMetaData().getIndexInfo(null, null, tableName.toUpperCase(Locale.ENGLISH), false, false)) { + ResultSet rs = connection.getMetaData().getIndexInfo(null, null, tableName.toUpperCase(Locale.ENGLISH), false, false)) { List<String> indices = new ArrayList<>(); while (rs.next()) { indices.add(rs.getString("INDEX_NAME").toLowerCase(Locale.ENGLISH)); diff --git a/sonar-db/src/test/java/org/sonar/db/favorite/FavoriteDbTester.java b/sonar-db/src/test/java/org/sonar/db/favorite/FavoriteDbTester.java new file mode 100644 index 00000000000..452bfbac197 --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/favorite/FavoriteDbTester.java @@ -0,0 +1,61 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.db.favorite; + +import java.util.List; +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.property.PropertyDto; +import org.sonar.db.property.PropertyQuery; + +public class FavoriteDbTester { + private static final String PROP_FAVORITE_KEY = "favourite"; + + private final DbTester db; + private final DbClient dbClient; + private final DbSession dbSession; + + public FavoriteDbTester(DbTester db) { + this.db = db; + this.dbClient = db.getDbClient(); + this.dbSession = db.getSession(); + } + + public void add(ComponentDto componentDto, long userId) { + dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto() + .setKey(PROP_FAVORITE_KEY) + .setUserId(userId) + .setResourceId(componentDto.getId())); + dbSession.commit(); + } + + public boolean hasFavorite(ComponentDto componentDto, long userId) { + List<PropertyDto> result = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder() + .setKey(PROP_FAVORITE_KEY) + .setComponentId(componentDto.getId()) + .setUserId((int) userId) + .build(), dbSession); + + return !result.isEmpty(); + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/favorite/FavoritesService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/favorite/FavoritesService.java index dab17b13317..91eede2140b 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/favorite/FavoritesService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/favorite/FavoritesService.java @@ -25,10 +25,11 @@ import org.sonarqube.ws.client.PostRequest; import org.sonarqube.ws.client.WsConnector; import static org.sonarqube.ws.client.favorite.FavoritesWsParameters.ACTION_ADD; +import static org.sonarqube.ws.client.favorite.FavoritesWsParameters.ACTION_REMOVE; import static org.sonarqube.ws.client.favorite.FavoritesWsParameters.CONTROLLER_FAVORITES; import static org.sonarqube.ws.client.favorite.FavoritesWsParameters.PARAM_COMPONENT; -public class FavoritesService extends BaseService{ +public class FavoritesService extends BaseService { public FavoritesService(WsConnector wsConnector) { super(wsConnector, CONTROLLER_FAVORITES); } @@ -38,4 +39,10 @@ public class FavoritesService extends BaseService{ call(post); } + + public void remove(String component) { + PostRequest post = new PostRequest(path(ACTION_REMOVE)).setParam(PARAM_COMPONENT, component); + + call(post); + } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/favorite/FavoritesWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/favorite/FavoritesWsParameters.java index 5349f5fcf34..3c39c735ede 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/favorite/FavoritesWsParameters.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/favorite/FavoritesWsParameters.java @@ -24,6 +24,7 @@ public class FavoritesWsParameters { public static final String CONTROLLER_FAVORITES = "api/favorites"; public static final String ACTION_ADD = "add"; + public static final String ACTION_REMOVE = "remove"; public static final String PARAM_COMPONENT = "component"; diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/favorite/FavoritesServiceTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/favorite/FavoritesServiceTest.java index 4b6c8a40feb..b6ce72fa1da 100644 --- a/sonar-ws/src/test/java/org/sonarqube/ws/client/favorite/FavoritesServiceTest.java +++ b/sonar-ws/src/test/java/org/sonarqube/ws/client/favorite/FavoritesServiceTest.java @@ -43,4 +43,14 @@ public class FavoritesServiceTest { .hasParam(PARAM_COMPONENT, "my_project") .andNoOtherParam(); } + + @Test + public void remove() { + underTest.remove("my_project"); + + serviceTester.assertThat(serviceTester.getPostRequest()) + .hasPath("remove") + .hasParam(PARAM_COMPONENT, "my_project") + .andNoOtherParam(); + } } |