Browse Source

SONAR-12266 Drop favourites for files

tags/8.4.0.35506
Jacek 4 years ago
parent
commit
121c04fd83

+ 2
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v84/DbVersion84.java View File

@@ -780,6 +780,8 @@ public class DbVersion84 implements DbVersion {
.add(3710, "Add primary key on 'UUID' column of 'RULES' table", AddPrimaryKeyOnUuidColumnOfRulesTable.class)
.add(3711, "Drop column 'ID' of 'RULES' table", DropIdColumnOfRulesTable.class)

.add(3800, "Remove favourites for components with qualifiers 'DIR', 'FIL', 'UTS'", RemoveFilesFavouritesFromProperties.class);

;
}
}

+ 48
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v84/RemoveFilesFavouritesFromProperties.java View File

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

import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.step.DataChange;
import org.sonar.server.platform.db.migration.step.MassUpdate;

public class RemoveFilesFavouritesFromProperties extends DataChange {

public RemoveFilesFavouritesFromProperties(Database db) {
super(db);
}

@Override
protected void execute(Context context) throws SQLException {
MassUpdate massUpdate = context.prepareMassUpdate();
massUpdate.select("select p.uuid from components c " +
"join properties p on c.uuid = p.component_uuid " +
"where p.prop_key = ? and c.qualifier in ('FIL', 'DIR', 'UTS')")
.setString(1, "favourite");
massUpdate.update("delete from properties where uuid = ?");

massUpdate.execute((row, update) -> {
String propertyUuid = row.getString(1);
update.setString(1, propertyUuid);
return true;
});
}
}

+ 147
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v84/RemoveFilesFavouritesFromPropertiesTest.java View File

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

import java.sql.SQLException;
import java.time.Instant;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.core.util.Uuids;
import org.sonar.db.CoreDbTester;
import org.sonar.server.platform.db.migration.step.DataChange;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.api.resources.Qualifiers.APP;
import static org.sonar.api.resources.Qualifiers.DIRECTORY;
import static org.sonar.api.resources.Qualifiers.FILE;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;

public class RemoveFilesFavouritesFromPropertiesTest {

private static final String PROPERTIES_TABLE_NAME = "properties";

@Rule
public CoreDbTester dbTester = CoreDbTester.createForSchema(RemoveFilesFavouritesFromPropertiesTest.class, "schema.sql");

private DataChange underTest = new RemoveFilesFavouritesFromProperties(dbTester.database());

private static final String APPLICATION_UUID_1 = Uuids.createFast();
private static final String PROJECT_UUID_2 = Uuids.createFast();
private static final String FILE_UUID_3 = Uuids.createFast();
private static final String DIRECTORY_UUID_4 = Uuids.createFast();
private static final String UNIT_TEST_FILE_UUID_5 = Uuids.createFast();

private static final String USER_UUID_1 = "1";
private static final String USER_UUID_2 = "2";

@Before
public void setup() {
insertComponent(APPLICATION_UUID_1, APP);
insertComponent(PROJECT_UUID_2, PROJECT);
insertComponent(FILE_UUID_3, FILE);
insertComponent(DIRECTORY_UUID_4, DIRECTORY);
insertComponent(UNIT_TEST_FILE_UUID_5, UNIT_TEST_FILE);

insertProperty("1", USER_UUID_1, APPLICATION_UUID_1, "test");
insertProperty("2", USER_UUID_2, PROJECT_UUID_2, "test2");
insertProperty("3", USER_UUID_2, null, "test3");
}

@Test
public void migrate() throws SQLException {
insertProperty("4", USER_UUID_1, APPLICATION_UUID_1, "favourite");
// properties to remove
insertProperty("5", USER_UUID_1, FILE_UUID_3, "favourite");
insertProperty("6", USER_UUID_2, FILE_UUID_3, "favourite");
insertProperty("7", USER_UUID_2, DIRECTORY_UUID_4, "favourite");
insertProperty("8", USER_UUID_2, UNIT_TEST_FILE_UUID_5, "favourite");

underTest.execute();

assertThat(dbTester.countRowsOfTable(PROPERTIES_TABLE_NAME)).isEqualTo(4);
assertThat(dbTester.select("select uuid from properties").stream().map(columns -> columns.get("UUID")))
.containsOnly("1", "2", "3", "4");
}

@Test
public void properties_table_empty() throws SQLException {
dbTester.executeUpdateSql("delete from properties");

underTest.execute();

assertThat(dbTester.countRowsOfTable(PROPERTIES_TABLE_NAME)).isEqualTo(0);
}

@Test
public void does_not_remove_properties_with_key_other_than_favourite() throws SQLException {
underTest.execute();

assertThat(dbTester.countRowsOfTable(PROPERTIES_TABLE_NAME)).isEqualTo(3L);
}

@Test
public void migration_is_re_entrant() throws SQLException {
insertProperty("4", USER_UUID_1, APPLICATION_UUID_1, "favourite");
// properties to remove
insertProperty("5", USER_UUID_1, FILE_UUID_3, "favourite");
insertProperty("6", USER_UUID_2, FILE_UUID_3, "favourite");

underTest.execute();

insertProperty("7", USER_UUID_2, DIRECTORY_UUID_4, "favourite");
insertProperty("8", USER_UUID_2, UNIT_TEST_FILE_UUID_5, "favourite");

// re-entrant
underTest.execute();

assertThat(dbTester.countRowsOfTable(PROPERTIES_TABLE_NAME)).isEqualTo(4);
assertThat(dbTester.select("select uuid from properties").stream().map(columns -> columns.get("UUID")))
.containsOnly("1", "2", "3", "4");
}

private void insertComponent(String uuid, String qualifier) {
dbTester.executeInsert("COMPONENTS",
"UUID", uuid,
"NAME", uuid + "-name",
"DESCRIPTION", uuid + "-description",
"ORGANIZATION_UUID", "default",
"KEE", uuid + "-key",
"PROJECT_UUID", uuid,
"MAIN_BRANCH_PROJECT_UUID", "project_uuid",
"UUID_PATH", ".",
"ROOT_UUID", uuid,
"PRIVATE", Boolean.toString(false),
"SCOPE", "TRK",
"QUALIFIER", qualifier);
}

private void insertProperty(String id, String userUuid, @Nullable String componentUuid, String propKey) {
dbTester.executeInsert(PROPERTIES_TABLE_NAME,
"uuid", id,
"user_uuid", userUuid,
"component_uuid", componentUuid,
"prop_key", propKey,
"is_empty", true,
"created_at", Instant.now().toEpochMilli());
}
}

+ 55
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v84/RemoveFilesFavouritesFromPropertiesTest/schema.sql View File

@@ -0,0 +1,55 @@
CREATE TABLE "COMPONENTS"(
"UUID" VARCHAR(50) NOT NULL,
"ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
"KEE" VARCHAR(400),
"DEPRECATED_KEE" VARCHAR(400),
"NAME" VARCHAR(2000),
"LONG_NAME" VARCHAR(2000),
"DESCRIPTION" VARCHAR(2000),
"ENABLED" BOOLEAN DEFAULT TRUE NOT NULL,
"SCOPE" VARCHAR(3),
"QUALIFIER" VARCHAR(10),
"PRIVATE" BOOLEAN NOT NULL,
"ROOT_UUID" VARCHAR(50) NOT NULL,
"LANGUAGE" VARCHAR(20),
"COPY_COMPONENT_UUID" VARCHAR(50),
"PATH" VARCHAR(2000),
"UUID_PATH" VARCHAR(1500) NOT NULL,
"PROJECT_UUID" VARCHAR(50) NOT NULL,
"MODULE_UUID" VARCHAR(50),
"MODULE_UUID_PATH" VARCHAR(1500),
"MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
"B_CHANGED" BOOLEAN,
"B_NAME" VARCHAR(500),
"B_LONG_NAME" VARCHAR(500),
"B_DESCRIPTION" VARCHAR(2000),
"B_ENABLED" BOOLEAN,
"B_QUALIFIER" VARCHAR(10),
"B_LANGUAGE" VARCHAR(20),
"B_COPY_COMPONENT_UUID" VARCHAR(50),
"B_PATH" VARCHAR(2000),
"B_UUID_PATH" VARCHAR(1500),
"B_MODULE_UUID" VARCHAR(50),
"B_MODULE_UUID_PATH" VARCHAR(1500),
"CREATED_AT" TIMESTAMP
);
CREATE INDEX "PROJECTS_ORGANIZATION" ON "COMPONENTS"("ORGANIZATION_UUID");
CREATE UNIQUE INDEX "PROJECTS_KEE" ON "COMPONENTS"("KEE");
CREATE INDEX "PROJECTS_MODULE_UUID" ON "COMPONENTS"("MODULE_UUID");
CREATE INDEX "PROJECTS_PROJECT_UUID" ON "COMPONENTS"("PROJECT_UUID");
CREATE INDEX "PROJECTS_QUALIFIER" ON "COMPONENTS"("QUALIFIER");
CREATE INDEX "PROJECTS_ROOT_UUID" ON "COMPONENTS"("ROOT_UUID");
CREATE INDEX "PROJECTS_UUID" ON "COMPONENTS"("UUID");

CREATE TABLE "PROPERTIES"(
"UUID" VARCHAR(40) NOT NULL,
"PROP_KEY" VARCHAR(512) NOT NULL,
"USER_UUID" VARCHAR(40),
"IS_EMPTY" BOOLEAN NOT NULL,
"TEXT_VALUE" VARCHAR(4000),
"CLOB_VALUE" CLOB(2147483647),
"CREATED_AT" BIGINT NOT NULL,
"COMPONENT_UUID" VARCHAR(40)
);
ALTER TABLE "PROPERTIES" ADD CONSTRAINT "PK_PROPERTIES" PRIMARY KEY("UUID");
CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES"("PROP_KEY");

+ 2
- 3
server/sonar-webserver-webapi/src/main/java/org/sonar/server/favorite/ws/AddAction.java View File

@@ -38,17 +38,15 @@ import static java.lang.String.format;
import static java.lang.String.join;
import static java.util.Arrays.asList;
import static org.sonar.api.resources.Qualifiers.APP;
import static org.sonar.api.resources.Qualifiers.FILE;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.api.resources.Qualifiers.SUBVIEW;
import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
import static org.sonar.api.resources.Qualifiers.VIEW;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.server.favorite.ws.FavoritesWsParameters.PARAM_COMPONENT;

public class AddAction implements FavoritesWsAction {

private static final List<String> SUPPORTED_QUALIFIERS = asList(PROJECT, VIEW, SUBVIEW, APP, FILE, UNIT_TEST_FILE);
private static final List<String> SUPPORTED_QUALIFIERS = asList(PROJECT, VIEW, SUBVIEW, APP);
private static final String SUPPORTED_QUALIFIERS_AS_STRING = join(", ", SUPPORTED_QUALIFIERS);

private final UserSession userSession;
@@ -71,6 +69,7 @@ public class AddAction implements FavoritesWsAction {
"Requires authentication and the following permission: 'Browse' on the project of the specified component.")
.setSince("6.3")
.setChangelog(
new Change("8.4", "It's no longer possible to set a file as favorite"),
new Change("7.7", "It's no longer possible to have more than 100 favorites by qualifier"),
new Change("7.7", "It's no longer possible to set a directory as favorite"),
new Change("7.6", format("The use of module keys in parameter '%s' is deprecated", PARAM_COMPONENT)))

+ 2
- 3
server/sonar-webserver-webapi/src/main/java/org/sonar/server/favorite/ws/RemoveAction.java View File

@@ -50,7 +50,7 @@ public class RemoveAction implements FavoritesWsAction {
@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>" +
.setDescription("Remove a component (project, portfolio, application etc.) as favorite for the authenticated user.<br>" +
"Requires authentication.")
.setSince("6.3")
.setChangelog(new Change("7.6", String.format("The use of module keys in parameter '%s' is deprecated", PARAM_COMPONENT)))
@@ -73,8 +73,7 @@ public class RemoveAction implements FavoritesWsAction {
return request -> {
try (DbSession dbSession = dbClient.openSession(false)) {
ComponentDto component = componentFinder.getByKey(dbSession, request.mandatoryParam(PARAM_COMPONENT));
userSession
.checkLoggedIn();
userSession.checkLoggedIn();
favoriteUpdater.remove(dbSession, component, userSession.isLoggedIn() ? userSession.getUuid() : null);
dbSession.commit();
}

+ 28
- 21
server/sonar-webserver-webapi/src/test/java/org/sonar/server/favorite/ws/AddActionTest.java View File

@@ -48,6 +48,7 @@ import static java.lang.String.format;
import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
import static java.util.Optional.ofNullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
@@ -88,26 +89,6 @@ public class AddActionTest {
.containsOnly(project.uuid(), user.getUuid(), "favourite");
}

@Test
public void add_a_file() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(USER, project);

call(file.getKey());

List<PropertyDto> favorites = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setUserUuid(user.getUuid())
.setKey("favourite")
.build(), dbSession);
assertThat(favorites).hasSize(1);
PropertyDto favorite = favorites.get(0);
assertThat(favorite)
.extracting(PropertyDto::getComponentUuid, PropertyDto::getUserUuid, PropertyDto::getKey)
.containsOnly(file.uuid(), user.getUuid(), "favourite");
}

@Test
public void fail_when_no_browse_permission_on_the_project() {
ComponentDto project = db.components().insertPrivateProject();
@@ -158,11 +139,37 @@ public class AddActionTest {
userSession.logIn(user).addProjectPermission(USER, project);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Only components with qualifiers TRK, VW, SVW, APP, FIL, UTS are supported");
expectedException.expectMessage("Only components with qualifiers TRK, VW, SVW, APP are supported");

call(directory.getKey());
}

@Test
public void fail_on_file() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(USER, project);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Only components with qualifiers TRK, VW, SVW, APP are supported");

call(file.getKey());
}

@Test
public void fail_on_unit_test_file() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto unitTestFile = db.components().insertComponent(newFileDto(project).setQualifier(UNIT_TEST_FILE));
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(USER, project);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Only components with qualifiers TRK, VW, SVW, APP are supported");

call(unitTestFile.getKey());
}

@Test
public void definition() {
WebService.Action definition = ws.getDef();

Loading…
Cancel
Save