From 4d4fa01b933038ba8e9639679fab4a36b6eff91a Mon Sep 17 00:00:00 2001 From: Pierre Date: Fri, 15 Jul 2022 11:50:31 +0200 Subject: [PATCH] SONAR-16613 Migrate users field 'sonarlint_ad_seen' to use the new notice mechanism --- .../main/java/org/sonar/db/DatabaseUtils.java | 1 + .../main/java/org/sonar/db/user/UserDao.java | 4 - .../main/java/org/sonar/db/user/UserDto.java | 10 --- .../java/org/sonar/db/user/UserMapper.java | 2 - .../org/sonar/db/user/UserMapper.xml | 11 --- server/sonar-db-dao/src/schema/schema-sq.ddl | 3 +- .../java/org/sonar/db/user/UserDaoTest.java | 12 --- .../db/migration/step/DropColumnChange.java | 2 +- .../step/DropColumnWithConstraint.java | 51 ++++++++++++ ...DropUserManagedColumnFromMetricsTable.java | 31 +------- .../db/migration/version/v96/DbVersion96.java | 2 + ...DropSonarlintAdSeenColumnInUsersTable.java | 34 ++++++++ ...eSonarlintAdSeenFromUsersToProperties.java | 66 ++++++++++++++++ ...SonarlintAdSeenColumnInUsersTableTest.java | 54 +++++++++++++ ...arlintAdSeenFromUsersToPropertiesTest.java | 78 +++++++++++++++++++ .../schema.sql | 28 +++++++ .../schema.sql | 42 ++++++++++ .../sonar/server/user/ws/CurrentAction.java | 9 ++- .../server/user/ws/DismissNoticeAction.java | 13 +++- .../user/ws/DismissSonarlintAdAction.java | 22 +++--- .../sonar/server/user/ws/current-example.json | 9 ++- .../server/user/ws/CurrentActionTest.java | 30 +++++-- .../user/ws/DismissNoticeActionTest.java | 29 +++++-- .../user/ws/DismissSonarlintAdActionTest.java | 15 ++-- .../ws/client/users/UsersService.java | 15 ---- sonar-ws/src/main/protobuf/ws-users.proto | 2 +- 26 files changed, 448 insertions(+), 127 deletions(-) create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnWithConstraint.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTable.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToProperties.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest.java create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest/schema.sql create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest/schema.sql diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java b/server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java index 82a7580087d..ea069210e78 100644 --- a/server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java +++ b/server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java @@ -458,4 +458,5 @@ public class DatabaseUtils { checkArgument(values.size() <= PARTITION_SIZE_FOR_ORACLE, message); } } + } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java index a859286377e..c43e3b1dce3 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java @@ -125,10 +125,6 @@ public class UserDao implements Dao { mapper(session).updateSonarlintLastConnectionDate(login, system2.now()); } - public void dismissSonarlintAd(DbSession session, String login) { - mapper(session).dismissSonarlintAd(login); - } - public void deactivateUser(DbSession dbSession, UserDto user) { mapper(dbSession).deactivateUser(user.getLogin(), system2.now()); auditPersister.deactivateUser(dbSession, new UserNewValue(user.getUuid(), user.getLogin())); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java index 71dcfd94b7c..e9387657f66 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java @@ -53,7 +53,6 @@ public class UserDto implements UserId { private String homepageParameter; private boolean local = true; private boolean resetPassword = false; - private boolean sonarlintAdSeen = false; /** * Date of the last time the user has accessed to the server. @@ -252,15 +251,6 @@ public class UserDto implements UserId { return this; } - public boolean isSonarlintAdSeen() { - return sonarlintAdSeen; - } - - public UserDto setSonarlintAdSeen(boolean sonarlintAdSeen) { - this.sonarlintAdSeen = sonarlintAdSeen; - return this; - } - @CheckForNull public Long getLastConnectionDate() { return lastConnectionDate; diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java index 802b1f1a30e..5e59ef4958f 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java @@ -79,6 +79,4 @@ public interface UserMapper { void clearHomepage(@Param("login") String login, @Param("now") long now); long countActiveSonarlintUsers(@Param("sinceDate") long sinceDate); - - void dismissSonarlintAd(@Param("login") String login); } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml index 858b063cc51..3dfd081b18f 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml @@ -22,7 +22,6 @@ u.homepage_parameter as "homepageParameter", u.last_connection_date as "lastConnectionDate", u.last_sonarlint_connection as "lastSonarlintConnectionDate", - u.sonarlint_ad_seen as "sonarlintAdSeen", u.created_at as "createdAt", u.updated_at as "updatedAt" @@ -160,13 +159,6 @@ login = #{login, jdbcType=VARCHAR} - - update users set - sonarlint_ad_seen = ${_true} - where - login = #{login, jdbcType=VARCHAR} - - update users set @@ -209,7 +201,6 @@ crypted_password, hash_method, last_sonarlint_connection, - sonarlint_ad_seen, reset_password, homepage_type, homepage_parameter, @@ -230,7 +221,6 @@ #{user.cryptedPassword,jdbcType=VARCHAR}, #{user.hashMethod,jdbcType=VARCHAR}, #{user.lastSonarlintConnectionDate,jdbcType=BIGINT}, - #{user.sonarlintAdSeen,jdbcType=BOOLEAN}, #{user.resetPassword,jdbcType=BOOLEAN}, #{user.homepageType,jdbcType=VARCHAR}, #{user.homepageParameter,jdbcType=VARCHAR}, @@ -258,7 +248,6 @@ homepage_parameter = #{user.homepageParameter, jdbcType=VARCHAR}, last_connection_date = #{user.lastConnectionDate,jdbcType=BIGINT}, last_sonarlint_connection = #{user.lastSonarlintConnectionDate,jdbcType=BIGINT}, - sonarlint_ad_seen = #{user.sonarlintAdSeen,jdbcType=BOOLEAN}, updated_at = #{user.updatedAt,jdbcType=BIGINT} where uuid = #{user.uuid, jdbcType=VARCHAR} diff --git a/server/sonar-db-dao/src/schema/schema-sq.ddl b/server/sonar-db-dao/src/schema/schema-sq.ddl index a9cfa3c66ad..074594cb5eb 100644 --- a/server/sonar-db-dao/src/schema/schema-sq.ddl +++ b/server/sonar-db-dao/src/schema/schema-sq.ddl @@ -993,8 +993,7 @@ CREATE TABLE "USERS"( "CREATED_AT" BIGINT, "UPDATED_AT" BIGINT, "RESET_PASSWORD" BOOLEAN NOT NULL, - "LAST_SONARLINT_CONNECTION" BIGINT, - "SONARLINT_AD_SEEN" BOOLEAN DEFAULT FALSE + "LAST_SONARLINT_CONNECTION" BIGINT ); ALTER TABLE "USERS" ADD CONSTRAINT "PK_USERS" PRIMARY KEY("UUID"); CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS"("LOGIN" NULLS FIRST); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java index b43af0453c0..6d2005ef246 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java @@ -97,18 +97,6 @@ public class UserDaoTest { assertThat(user).isNull(); } - @Test - public void dismiss_sonarlint_ad() { - UserDto user = db.users().insertUser(u -> u.setLogin("user")); - assertThat(user.isSonarlintAdSeen()).isFalse(); - - underTest.dismissSonarlintAd(session, "user"); - - user = db.users().selectUserByLogin("user").get(); - - assertThat(user.isSonarlintAdSeen()).isTrue(); - } - @Test public void selectUsersByLogins() { db.users().insertUser(user -> user.setLogin("user1")); diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java index 5bf051020f2..ba761526777 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java @@ -44,7 +44,7 @@ public abstract class DropColumnChange extends DdlChange { context.execute(new DropColumnsBuilder(getDatabase().getDialect(), tableName, columnName).build()); } - private boolean checkIfUseManagedColumnExists() throws SQLException { + public boolean checkIfUseManagedColumnExists() throws SQLException { try (var connection = getDatabase().getDataSource().getConnection()) { if (DatabaseUtils.tableColumnExists(connection, tableName, columnName)) { return true; diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnWithConstraint.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnWithConstraint.java new file mode 100644 index 00000000000..34d9dc1fe3b --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnWithConstraint.java @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.step; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.dialect.MsSql; +import org.sonar.server.platform.db.migration.sql.DropColumnsBuilder; +import org.sonar.server.platform.db.migration.sql.DropMsSQLDefaultConstraintsBuilder; + +public class DropColumnWithConstraint extends DropColumnChange { + + private final String tableName; + private final String column; + + public DropColumnWithConstraint(Database db, String tableName, String column) { + super(db, tableName, column); + this.tableName = tableName; + this.column = column; + } + + @Override + public void execute(Context context) throws SQLException { + if (!checkIfUseManagedColumnExists()) { + return; + } + + if (MsSql.ID.equals(getDatabase().getDialect().getId())) { + context.execute(new DropMsSQLDefaultConstraintsBuilder(getDatabase()).setTable(tableName).setColumns(column).build()); + } + context.execute(new DropColumnsBuilder(getDatabase().getDialect(), tableName, column).build()); + } + +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java index 05628583383..3cfcce03f27 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java @@ -19,41 +19,16 @@ */ package org.sonar.server.platform.db.migration.version.v91; -import java.sql.SQLException; import org.sonar.db.Database; -import org.sonar.db.DatabaseUtils; -import org.sonar.db.dialect.MsSql; -import org.sonar.server.platform.db.migration.sql.DropColumnsBuilder; -import org.sonar.server.platform.db.migration.sql.DropMsSQLDefaultConstraintsBuilder; -import org.sonar.server.platform.db.migration.step.DdlChange; +import org.sonar.server.platform.db.migration.step.DropColumnWithConstraint; -public class DropUserManagedColumnFromMetricsTable extends DdlChange { +public class DropUserManagedColumnFromMetricsTable extends DropColumnWithConstraint { private static final String TABLE_NAME = "metrics"; private static final String COLUMN = "user_managed"; public DropUserManagedColumnFromMetricsTable(Database db) { - super(db); + super(db, TABLE_NAME, COLUMN); } - @Override - public void execute(Context context) throws SQLException { - if (!checkIfUseManagedColumnExists()) { - return; - } - - if (MsSql.ID.equals(getDatabase().getDialect().getId())) { - context.execute(new DropMsSQLDefaultConstraintsBuilder(getDatabase()).setTable(TABLE_NAME).setColumns(COLUMN).build()); - } - context.execute(new DropColumnsBuilder(getDatabase().getDialect(), TABLE_NAME, COLUMN).build()); - } - - private boolean checkIfUseManagedColumnExists() throws SQLException { - try (var connection = getDatabase().getDataSource().getConnection()) { - if (DatabaseUtils.tableColumnExists(connection, TABLE_NAME, COLUMN)) { - return true; - } - } - return false; - } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DbVersion96.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DbVersion96.java index 4c3ad0be119..71692004e58 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DbVersion96.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DbVersion96.java @@ -36,6 +36,8 @@ public class DbVersion96 implements DbVersion { .add(6505, "Add column 'rule_description_context_key' to 'issues'", AddRuleDescriptionContextKeyInIssuesTable.class) .add(6506, "Add column 'education_principles' to 'rules'", AddEducationPrinciplesColumnToRuleTable.class) .add(6507, "Overwrite plugin file hash to force reloading rules", ForceReloadingOfAllPlugins.class) + .add(6508, "Migrate 'sonarlint_ad_seen' from users to properties", MigrateSonarlintAdSeenFromUsersToProperties.class) + .add(6509, "Drop column sonarlint_ad_seen in 'users'", DropSonarlintAdSeenColumnInUsersTable.class) ; } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTable.java new file mode 100644 index 00000000000..fa8bc33dfd7 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTable.java @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.v96; + +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DropColumnWithConstraint; + +public class DropSonarlintAdSeenColumnInUsersTable extends DropColumnWithConstraint { + + private static final String TABLE_NAME = "users"; + private static final String COLUMN = "sonarlint_ad_seen"; + + public DropSonarlintAdSeenColumnInUsersTable(Database db) { + super(db, TABLE_NAME, COLUMN); + } + +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToProperties.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToProperties.java new file mode 100644 index 00000000000..99c52dfd358 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToProperties.java @@ -0,0 +1,66 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.v96; + +import java.sql.SQLException; +import org.sonar.api.utils.System2; +import org.sonar.core.util.UuidFactory; +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 MigrateSonarlintAdSeenFromUsersToProperties extends DataChange { + + public static final String USER_DISMISSED_NOTICES_SONARLINT_AD = "user.dismissedNotices.sonarlintAd"; + + private final UuidFactory uuidFactory; + private final System2 system2; + + public MigrateSonarlintAdSeenFromUsersToProperties(Database db, UuidFactory uuidFactory, System2 system2) { + super(db); + this.uuidFactory = uuidFactory; + this.system2 = system2; + } + + @Override + protected void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + + massUpdate.select("select u.uuid, u.sonarlint_ad_seen, p.uuid from users u" + + " left join properties p on u.uuid = p.user_uuid and p.prop_key = ?" + + " where u.sonarlint_ad_seen = ?" + + " and p.uuid is null") + .setString(1, USER_DISMISSED_NOTICES_SONARLINT_AD) + .setBoolean(2, true); + + massUpdate.update("insert into properties (uuid,prop_key,user_uuid,is_empty,created_at) values (?, ?, ?, ?, ?)"); + + massUpdate.execute((row, update) -> { + update.setString(1, uuidFactory.create()); + update.setString(2, USER_DISMISSED_NOTICES_SONARLINT_AD); + update.setString(3, row.getString(1)); + update.setBoolean(4, true); + update.setLong(5, system2.now()); + + return true; + }); + + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest.java new file mode 100644 index 00000000000..1dca64023a9 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.v96; + +import java.sql.SQLException; +import java.sql.Types; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.step.DdlChange; + +public class DropSonarlintAdSeenColumnInUsersTableTest { + + private static final String COLUMN_NAME = "sonarlint_ad_seen"; + private static final String TABLE_NAME = "users"; + + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(DropSonarlintAdSeenColumnInUsersTableTest.class, "schema.sql"); + + private final DdlChange underTest = new DropSonarlintAdSeenColumnInUsersTable(db.database()); + + @Test + public void migration_should_drop_column() throws SQLException { + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.BOOLEAN, null, true); + underTest.execute(); + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + } + + @Test + public void migration_should_be_reentrant() throws SQLException { + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.BOOLEAN, null, true); + underTest.execute(); + // re-entrant + underTest.execute(); + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + } +} \ No newline at end of file diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest.java new file mode 100644 index 00000000000..02d0c929ba1 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest.java @@ -0,0 +1,78 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.v96; + +import java.sql.SQLException; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.core.util.UuidFactory; +import org.sonar.core.util.UuidFactoryFast; +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.server.platform.db.migration.version.v96.MigrateSonarlintAdSeenFromUsersToProperties.USER_DISMISSED_NOTICES_SONARLINT_AD; + +public class MigrateSonarlintAdSeenFromUsersToPropertiesTest { + + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(MigrateSonarlintAdSeenFromUsersToPropertiesTest.class, "schema.sql"); + + private final UuidFactory uuidFactory = UuidFactoryFast.getInstance(); + + private final System2 system2 = new System2(); + + private final DataChange underTest = new MigrateSonarlintAdSeenFromUsersToProperties(db.database(), uuidFactory, system2); + + @Test + public void migrate_sonarlintAd_to_properties() throws SQLException { + insertUser(db, "uuid-user-1", "user1", "externalId1", "externalLogin1", true); + insertUser(db, "uuid-user-2", "user2", "externalId2", "externalLogin2", false); + + underTest.execute(); + + assertThat(db.countSql("select count(*) from properties where prop_key='" + USER_DISMISSED_NOTICES_SONARLINT_AD + "' and user_uuid='uuid-user-1'")).isEqualTo(1); + assertThat(db.countSql("select count(*) from properties where prop_key='" + USER_DISMISSED_NOTICES_SONARLINT_AD + "' and user_uuid='uuid-user-2'")).isZero(); + } + + @Test + public void migration_is_reentrant() throws SQLException { + insertUser(db, "uuid-user-1", "user1", "externalId1", "externalLogin1", true); + + underTest.execute(); + underTest.execute(); + + assertThat(db.countSql("select count(*) from properties where prop_key='" + USER_DISMISSED_NOTICES_SONARLINT_AD + "' and user_uuid='uuid-user-1'")).isEqualTo(1); + } + + private void insertUser(CoreDbTester db, String userUuid, String login, String externalId, String externalLogin, boolean seen) { + db.executeInsert("users", "UUID", userUuid, + "login", login, + "external_identity_provider", "none", + "external_id", externalId, + "external_login", externalLogin, + "reset_password", false, + "sonarlint_ad_seen", seen + ); + } + + +} \ No newline at end of file diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest/schema.sql new file mode 100644 index 00000000000..82da2b04f35 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest/schema.sql @@ -0,0 +1,28 @@ +CREATE TABLE "USERS"( + "UUID" CHARACTER VARYING(255) NOT NULL, + "LOGIN" CHARACTER VARYING(255) NOT NULL, + "NAME" CHARACTER VARYING(200), + "EMAIL" CHARACTER VARYING(100), + "CRYPTED_PASSWORD" CHARACTER VARYING(100), + "SALT" CHARACTER VARYING(40), + "HASH_METHOD" CHARACTER VARYING(10), + "ACTIVE" BOOLEAN DEFAULT TRUE, + "SCM_ACCOUNTS" CHARACTER VARYING(4000), + "EXTERNAL_LOGIN" CHARACTER VARYING(255) NOT NULL, + "EXTERNAL_IDENTITY_PROVIDER" CHARACTER VARYING(100) NOT NULL, + "EXTERNAL_ID" CHARACTER VARYING(255) NOT NULL, + "USER_LOCAL" BOOLEAN, + "HOMEPAGE_TYPE" CHARACTER VARYING(40), + "HOMEPAGE_PARAMETER" CHARACTER VARYING(40), + "LAST_CONNECTION_DATE" BIGINT, + "CREATED_AT" BIGINT, + "UPDATED_AT" BIGINT, + "RESET_PASSWORD" BOOLEAN NOT NULL, + "LAST_SONARLINT_CONNECTION" BIGINT, + "SONARLINT_AD_SEEN" BOOLEAN DEFAULT FALSE +); +ALTER TABLE "USERS" ADD CONSTRAINT "PK_USERS" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS"("LOGIN" NULLS FIRST); +CREATE INDEX "USERS_UPDATED_AT" ON "USERS"("UPDATED_AT" NULLS FIRST); +CREATE UNIQUE INDEX "UNIQ_EXTERNAL_ID" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER" NULLS FIRST, "EXTERNAL_ID" NULLS FIRST); +CREATE UNIQUE INDEX "UNIQ_EXTERNAL_LOGIN" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER" NULLS FIRST, "EXTERNAL_LOGIN" NULLS FIRST); diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest/schema.sql new file mode 100644 index 00000000000..f5eda0e9df8 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest/schema.sql @@ -0,0 +1,42 @@ +CREATE TABLE "USERS"( + "UUID" CHARACTER VARYING(255) NOT NULL, + "LOGIN" CHARACTER VARYING(255) NOT NULL, + "NAME" CHARACTER VARYING(200), + "EMAIL" CHARACTER VARYING(100), + "CRYPTED_PASSWORD" CHARACTER VARYING(100), + "SALT" CHARACTER VARYING(40), + "HASH_METHOD" CHARACTER VARYING(10), + "ACTIVE" BOOLEAN DEFAULT TRUE, + "SCM_ACCOUNTS" CHARACTER VARYING(4000), + "EXTERNAL_LOGIN" CHARACTER VARYING(255) NOT NULL, + "EXTERNAL_IDENTITY_PROVIDER" CHARACTER VARYING(100) NOT NULL, + "EXTERNAL_ID" CHARACTER VARYING(255) NOT NULL, + "USER_LOCAL" BOOLEAN, + "HOMEPAGE_TYPE" CHARACTER VARYING(40), + "HOMEPAGE_PARAMETER" CHARACTER VARYING(40), + "LAST_CONNECTION_DATE" BIGINT, + "CREATED_AT" BIGINT, + "UPDATED_AT" BIGINT, + "RESET_PASSWORD" BOOLEAN NOT NULL, + "LAST_SONARLINT_CONNECTION" BIGINT, + "SONARLINT_AD_SEEN" BOOLEAN DEFAULT FALSE +); +ALTER TABLE "USERS" ADD CONSTRAINT "PK_USERS" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS"("LOGIN" NULLS FIRST); +CREATE INDEX "USERS_UPDATED_AT" ON "USERS"("UPDATED_AT" NULLS FIRST); +CREATE UNIQUE INDEX "UNIQ_EXTERNAL_ID" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER" NULLS FIRST, "EXTERNAL_ID" NULLS FIRST); +CREATE UNIQUE INDEX "UNIQ_EXTERNAL_LOGIN" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER" NULLS FIRST, "EXTERNAL_LOGIN" NULLS FIRST); + + +CREATE TABLE "PROPERTIES"( + "UUID" CHARACTER VARYING(40) NOT NULL, + "PROP_KEY" CHARACTER VARYING(512) NOT NULL, + "IS_EMPTY" BOOLEAN NOT NULL, + "TEXT_VALUE" CHARACTER VARYING(4000), + "CLOB_VALUE" CHARACTER LARGE OBJECT, + "CREATED_AT" BIGINT NOT NULL, + "COMPONENT_UUID" CHARACTER VARYING(40), + "USER_UUID" CHARACTER VARYING(255) +); +ALTER TABLE "PROPERTIES" ADD CONSTRAINT "PK_PROPERTIES" PRIMARY KEY("UUID"); +CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES"("PROP_KEY" NULLS FIRST); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/CurrentAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/CurrentAction.java index 9721fad2277..9d95d554204 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/CurrentAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/CurrentAction.java @@ -51,6 +51,7 @@ import static java.util.stream.Collectors.toList; import static org.apache.commons.lang.StringUtils.EMPTY; import static org.sonar.api.web.UserRole.USER; import static org.sonar.server.user.ws.DismissNoticeAction.EDUCATION_PRINCIPLES; +import static org.sonar.server.user.ws.DismissNoticeAction.SONARLINT_AD; import static org.sonar.server.ws.WsUtils.writeProtobuf; import static org.sonarqube.ws.Users.CurrentWsResponse.HomepageType.APPLICATION; import static org.sonarqube.ws.Users.CurrentWsResponse.HomepageType.PORTFOLIO; @@ -89,7 +90,9 @@ public class CurrentAction implements UsersWsAction { new Change("6.5", "showOnboardingTutorial is now returned in the response"), new Change("7.1", "'parameter' is replaced by 'component' and 'organization' in the response"), new Change("9.2", "boolean 'usingSonarLintConnectedMode' and 'sonarLintAdSeen' fields are now returned in the response"), - new Change("9.5", "showOnboardingTutorial is not returned anymore in the response")); + new Change("9.5", "showOnboardingTutorial is not returned anymore in the response"), + new Change("9.6", "'sonarLintAdSeen' is removed and replaced by a 'dismissedNotices' map that support multiple values") + ); } @Override @@ -122,8 +125,8 @@ public class CurrentAction implements UsersWsAction { .setPermissions(Permissions.newBuilder().addAllGlobal(getGlobalPermissions()).build()) .setHomepage(buildHomepage(dbSession, user)) .setUsingSonarLintConnectedMode(user.getLastSonarlintConnectionDate() != null) - .setSonarLintAdSeen(user.isSonarlintAdSeen()) - .putDismissedNotices(EDUCATION_PRINCIPLES, isNoticeDismissed(user, EDUCATION_PRINCIPLES)); + .putDismissedNotices(EDUCATION_PRINCIPLES, isNoticeDismissed(user, EDUCATION_PRINCIPLES)) + .putDismissedNotices(SONARLINT_AD, isNoticeDismissed(user, SONARLINT_AD)); ofNullable(emptyToNull(user.getEmail())).ifPresent(builder::setEmail); ofNullable(emptyToNull(user.getEmail())).ifPresent(u -> builder.setAvatar(avatarResolver.create(user))); ofNullable(user.getExternalLogin()).ifPresent(builder::setExternalIdentity); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DismissNoticeAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DismissNoticeAction.java index d4625ffb21e..5b830ebe486 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DismissNoticeAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DismissNoticeAction.java @@ -33,6 +33,7 @@ import static com.google.common.base.Preconditions.checkState; public class DismissNoticeAction implements UsersWsAction { public static final String EDUCATION_PRINCIPLES = "educationPrinciples"; + public static final String SONARLINT_AD = "sonarlintAd"; public static final String USER_DISMISS_CONSTANT = "user.dismissedNotices."; private final UserSession userSession; @@ -46,7 +47,7 @@ public class DismissNoticeAction implements UsersWsAction { @Override public void define(WebService.NewController context) { WebService.NewAction action = context.createAction("dismiss_notice") - .setDescription("Dismiss a notice for the current user.") + .setDescription("Dismiss a notice for the current user. Silently ignore if the notice is already dismissed.") .setSince("9.6") .setInternal(true) .setHandler(this) @@ -55,7 +56,7 @@ public class DismissNoticeAction implements UsersWsAction { action.createParam("notice") .setDescription("notice key to dismiss") .setExampleValue(EDUCATION_PRINCIPLES) - .setPossibleValues(EDUCATION_PRINCIPLES); + .setPossibleValues(EDUCATION_PRINCIPLES, SONARLINT_AD); } @Override @@ -66,6 +67,10 @@ public class DismissNoticeAction implements UsersWsAction { String noticeKeyParam = request.mandatoryParam("notice"); + dismissNotice(response, currentUserUuid, noticeKeyParam); + } + + public void dismissNotice(Response response, String currentUserUuid, String noticeKeyParam) { try (DbSession dbSession = dbClient.openSession(false)) { String paramKey = USER_DISMISS_CONSTANT + noticeKeyParam; PropertyQuery query = new PropertyQuery.Builder() @@ -74,7 +79,8 @@ public class DismissNoticeAction implements UsersWsAction { .build(); if (!dbClient.propertiesDao().selectByQuery(query, dbSession).isEmpty()) { - throw new IllegalArgumentException(String.format("Notice %s is already dismissed", noticeKeyParam)); + // already dismissed + response.noContent(); } PropertyDto property = new PropertyDto().setUserUuid(currentUserUuid).setKey(paramKey); @@ -82,6 +88,5 @@ public class DismissNoticeAction implements UsersWsAction { dbSession.commit(); response.noContent(); } - } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DismissSonarlintAdAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DismissSonarlintAdAction.java index 6762ffd6364..00840ca5d45 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DismissSonarlintAdAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/DismissSonarlintAdAction.java @@ -22,37 +22,37 @@ package org.sonar.server.user.ws; 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.server.user.UserSession; +import static org.sonar.server.user.ws.DismissNoticeAction.SONARLINT_AD; import static org.sonarqube.ws.client.user.UsersWsParameters.ACTION_DISMISS_SONARLINT_AD; +/** + * @deprecated use DismissNoticeAction + */ +@Deprecated(since = "9.6", forRemoval = true) public class DismissSonarlintAdAction implements UsersWsAction { private final UserSession userSession; - private final DbClient dbClient; + private final DismissNoticeAction dismissNoticeAction; - public DismissSonarlintAdAction(UserSession userSession, DbClient dbClient) { + public DismissSonarlintAdAction(UserSession userSession, DismissNoticeAction dismissNoticeAction) { this.userSession = userSession; - this.dbClient = dbClient; + this.dismissNoticeAction = dismissNoticeAction; } @Override public void define(WebService.NewController controller) { controller.createAction(ACTION_DISMISS_SONARLINT_AD) - .setDescription("Dismiss SonarLint advertisement.") + .setDescription("Dismiss SonarLint advertisement. Deprecated since 9.6, replaced api/users/dismiss_notice") .setSince("9.2") .setPost(true) + .setDeprecatedSince("9.6") .setHandler(this); } @Override public void handle(Request request, Response response) throws Exception { userSession.checkLoggedIn(); - try (DbSession dbSession = dbClient.openSession(false)) { - dbClient.userDao().dismissSonarlintAd(dbSession, userSession.getLogin()); - dbSession.commit(); - } - response.noContent(); + dismissNoticeAction.dismissNotice(response, userSession.getUuid(), SONARLINT_AD); } } diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/user/ws/current-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/user/ws/current-example.json index e1644c37888..50991030f29 100644 --- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/user/ws/current-example.json +++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/user/ws/current-example.json @@ -3,19 +3,22 @@ "login": "obiwan.kenobi", "name": "Obiwan Kenobi", "email": "obiwan.kenobi@starwars.com", - "avatar": "f5aa64437a1821ffe8b563099d506aef", "local": true, "externalIdentity": "obiwan.kenobi", "externalProvider": "sonarqube", "scmAccounts": ["obiwan:github", "obiwan:bitbucket"], "groups": ["Jedi", "Rebel"], - "usingSonarLintConnectedMode": false, - "sonarLintAdSeen": false, "permissions": { "global": ["profileadmin", "scan"] }, + "avatar": "f5aa64437a1821ffe8b563099d506aef", "homepage": { "type": "PROJECT", "component": "death-star-key" + }, + "usingSonarLintConnectedMode": false, + "dismissedNotices": { + "educationPrinciples": false, + "sonarlintAd": false } } diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/CurrentActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/CurrentActionTest.java index a46493caee5..8707a53595b 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/CurrentActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/CurrentActionTest.java @@ -130,8 +130,8 @@ public class CurrentActionTest { assertThat(response) .extracting(CurrentWsResponse::getIsLoggedIn, CurrentWsResponse::getLogin, CurrentWsResponse::getName, CurrentWsResponse::hasAvatar, CurrentWsResponse::getLocal, - CurrentWsResponse::getExternalIdentity, CurrentWsResponse::getExternalProvider, CurrentWsResponse::getUsingSonarLintConnectedMode, CurrentWsResponse::getSonarLintAdSeen) - .containsExactly(true, "obiwan.kenobi", "Obiwan Kenobi", false, true, "obiwan", "sonarqube", false, false); + CurrentWsResponse::getExternalIdentity, CurrentWsResponse::getExternalProvider, CurrentWsResponse::getUsingSonarLintConnectedMode) + .containsExactly(true, "obiwan.kenobi", "Obiwan Kenobi", false, true, "obiwan", "sonarqube", false); assertThat(response.hasEmail()).isFalse(); assertThat(response.getScmAccountsList()).isEmpty(); assertThat(response.getGroupsList()).isEmpty(); @@ -240,15 +240,33 @@ public class CurrentActionTest { } @Test - public void handle_givenSonarLintAdSeenUserInDatabase_returnSonarLintAdSeenUserFromTheEndpoint() { - UserDto user = db.users().insertUser(u -> u.setSonarlintAdSeen(true)); + public void return_sonarlintAd_dismiss_notice() { + UserDto user = db.users().insertUser(); + userSession.logIn(user); + + PropertyDto property = new PropertyDto().setUserUuid(user.getUuid()).setKey("user.dismissedNotices.sonarlintAd"); + db.properties().insertProperties(userSession.getLogin(), null, null, null, property); + + CurrentWsResponse response = call(); + + assertThat(response.getDismissedNoticesMap().entrySet()) + .extracting(Map.Entry::getKey, Map.Entry::getValue) + .contains(Tuple.tuple("sonarlintAd", true)); + } + + @Test + public void return_sonarlintAd_not_dismissed() { + UserDto user = db.users().insertUser(); userSession.logIn(user); CurrentWsResponse response = call(); - assertThat(response.getSonarLintAdSeen()).isTrue(); + assertThat(response.getDismissedNoticesMap().entrySet()) + .extracting(Map.Entry::getKey, Map.Entry::getValue) + .contains(Tuple.tuple("sonarlintAd", false)); } + @Test public void test_definition() { WebService.Action definition = ws.getDef(); @@ -259,7 +277,7 @@ public class CurrentActionTest { assertThat(definition.isInternal()).isTrue(); assertThat(definition.responseExampleAsString()).isNotEmpty(); assertThat(definition.params()).isEmpty(); - assertThat(definition.changelog()).hasSize(4); + assertThat(definition.changelog()).isNotEmpty(); } private CurrentWsResponse call() { diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DismissNoticeActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DismissNoticeActionTest.java index d20d7eeb301..2edf0c4da9a 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DismissNoticeActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DismissNoticeActionTest.java @@ -57,6 +57,20 @@ public class DismissNoticeActionTest { assertThat(propertyDto).isPresent(); } + @Test + public void dismiss_sonarlintAd() { + userSessionRule.logIn(); + + TestResponse testResponse = tester.newRequest() + .setParam("notice", "sonarlintAd") + .execute(); + + assertThat(testResponse.getStatus()).isEqualTo(204); + + Optional propertyDto = db.properties().findFirstUserProperty(userSessionRule.getUuid(), "user.dismissedNotices.sonarlintAd"); + assertThat(propertyDto).isPresent(); + } + @Test public void authentication_is_required() { @@ -88,22 +102,23 @@ public class DismissNoticeActionTest { assertThatThrownBy(testRequest::execute) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Value of parameter 'notice' (not_supported_value) must be one of: [educationPrinciples]"); + .hasMessage("Value of parameter 'notice' (not_supported_value) must be one of: [educationPrinciples, sonarlintAd]"); } @Test - public void notice_already_exist() { + public void notice_already_exist_dont_fail() { userSessionRule.logIn(); PropertyDto property = new PropertyDto().setKey("user.dismissedNotices.educationPrinciples").setUserUuid(userSessionRule.getUuid()); db.properties().insertProperties(userSessionRule.getLogin(), null, null, null, property); + assertThat(db.properties().findFirstUserProperty(userSessionRule.getUuid(), "user.dismissedNotices.educationPrinciples")).isPresent(); - TestRequest testRequest = tester.newRequest() - .setParam("notice", "educationPrinciples"); + TestResponse testResponse = tester.newRequest() + .setParam("notice", "sonarlintAd") + .execute(); - assertThatThrownBy(testRequest::execute) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Notice educationPrinciples is already dismissed"); + assertThat(testResponse.getStatus()).isEqualTo(204); + assertThat(db.properties().findFirstUserProperty(userSessionRule.getUuid(), "user.dismissedNotices.educationPrinciples")).isPresent(); } diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DismissSonarlintAdActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DismissSonarlintAdActionTest.java index 494016d9d2a..8b1a7b166e3 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DismissSonarlintAdActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/user/ws/DismissSonarlintAdActionTest.java @@ -40,13 +40,14 @@ public class DismissSonarlintAdActionTest { @Rule public DbTester db = DbTester.create(System2.INSTANCE); - private final WsActionTester tester = new WsActionTester(new DismissSonarlintAdAction(userSession, db.getDbClient())); + private final DismissNoticeAction dismissNoticeAction = new DismissNoticeAction(userSession, db.getDbClient()); + private final WsActionTester underTest = new WsActionTester(new DismissSonarlintAdAction(userSession, dismissNoticeAction)); @Test public void test_definition() { - WebService.Action definition = tester.getDef(); + WebService.Action definition = underTest.getDef(); assertThat(definition.key()).isEqualTo(ACTION_DISMISS_SONARLINT_AD); - assertThat(definition.description()).isEqualTo("Dismiss SonarLint advertisement."); + assertThat(definition.description()).isEqualTo("Dismiss SonarLint advertisement. Deprecated since 9.6, replaced api/users/dismiss_notice"); assertThat(definition.since()).isEqualTo("9.2"); assertThat(definition.isPost()).isTrue(); assertThat(definition.params()).isEmpty(); @@ -55,7 +56,7 @@ public class DismissSonarlintAdActionTest { @Test public void endpoint_throw_exception_if_no_user_login() { - final TestRequest request = tester.newRequest(); + final TestRequest request = underTest.newRequest(); assertThatThrownBy(request::execute) .isInstanceOf(UnauthorizedException.class); } @@ -67,10 +68,10 @@ public class DismissSonarlintAdActionTest { .setName("Obiwan Kenobi") .setEmail(null)); userSession.logIn(user); - assertThat(user.isSonarlintAdSeen()).isFalse(); + assertThat(db.properties().findFirstUserProperty(userSession.getUuid(), "user.dismissedNotices.sonarlintAd")).isEmpty(); - tester.newRequest().execute(); + underTest.newRequest().execute(); UserDto updatedUser = db.users().selectUserByLogin(user.getLogin()).get(); - assertThat(updatedUser.isSonarlintAdSeen()).isTrue(); + assertThat(db.properties().findFirstUserProperty(userSession.getUuid(), "user.dismissedNotices.sonarlintAd")).isPresent(); } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java index 0bdf172ec5c..f23de0edab8 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java @@ -217,21 +217,6 @@ public class UsersService extends BaseService { ).content(); } - /** - * - * This is part of the internal API. - * This is a POST request. - * @see Further information about this action online (including a response example) - * @since 9.2 - */ - public void dismissSonarlintAd() { - call( - new PostRequest(path("dismiss_sonarlint_ad")) - .setMediaType(MediaTypes.JSON) - ).content(); - } - - /** * * This is part of the internal API. diff --git a/sonar-ws/src/main/protobuf/ws-users.proto b/sonar-ws/src/main/protobuf/ws-users.proto index 462aa020fc8..cac3168f8d6 100644 --- a/sonar-ws/src/main/protobuf/ws-users.proto +++ b/sonar-ws/src/main/protobuf/ws-users.proto @@ -112,7 +112,7 @@ message CurrentWsResponse { optional Homepage homepage = 13; reserved 15; // settings removed optional bool usingSonarLintConnectedMode = 16; - optional bool sonarLintAdSeen = 17; + reserved 17; // sonarLintAdSeen removed map dismissedNotices = 18; message Permissions { -- 2.39.5