]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10597 Update QPROFILE_CHANGES#USER_LOGIN to USER_UUID
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 11 May 2018 07:13:40 +0000 (09:13 +0200)
committerSonarTech <sonartech@sonarsource.com>
Wed, 23 May 2018 18:20:47 +0000 (20:20 +0200)
* SONAR-10597 Refactor ChangelogActionTest

Improve unit tests to really check JSON response
Add some tests on not covered use cases

* SONAR-10597 Rename QPROFILE_CHANGES#USER_LOGIN to USER_UUID

* SONAR-10597 Fix ChangelogAction to not return user uuid

* SONAR-10597 Add ITs to check qProfile changes after login update

* SONAR-10597 Set user uuid instead of login when saving qprofile changes

20 files changed:
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileChangeDto.java
server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileChangeMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileChangeDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileTesting.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/DbVersion72.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableQProfileChanges.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/DbVersion72Test.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableQProfileChangesTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableQProfileChangesTest/qprofile_changes.sql [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleChange.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ChangelogAction.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ActiveRuleChangeTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileInsertImplTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRulesImplTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangelogActionTest.java
tests/src/test/java/org/sonarqube/tests/qualityProfile/QualityProfilesWsTest.java
tests/src/test/java/org/sonarqube/tests/user/SonarCloudUpdateLoginDuringAuthenticationTest.java
tests/src/test/java/org/sonarqube/tests/user/SonarCloudUserSuite.java

index 4986fd747fa39a5f1f3321a6e778a5cf142c23ca..885d377e2c0bbb65b7a4459355cb42c863798fdd 100644 (file)
@@ -603,7 +603,7 @@ CREATE TABLE "QPROFILE_CHANGES" (
   "RULES_PROFILE_UUID" VARCHAR(255) NOT NULL,
   "CHANGE_TYPE" VARCHAR(20) NOT NULL,
   "CREATED_AT" BIGINT NOT NULL,
-  "USER_LOGIN" VARCHAR(255),
+  "USER_UUID" VARCHAR(255),
   "CHANGE_DATA" CLOB
 );
 CREATE INDEX "QP_CHANGES_RULES_PROFILE_UUID" ON "QPROFILE_CHANGES" ("RULES_PROFILE_UUID");
index bd5ab4ca6fb94722b78e29cd8374e2388127ba4a..18244cc10990fff360bf0297ced31f8f59b58bb3 100644 (file)
@@ -32,7 +32,7 @@ public class QProfileChangeDto {
   private String uuid;
   private String rulesProfileUuid;
   private String changeType;
-  private String login;
+  private String userUuid;
   private String data;
   private long createdAt;
 
@@ -73,12 +73,12 @@ public class QProfileChangeDto {
   }
 
   @CheckForNull
-  public String getLogin() {
-    return login;
+  public String getUserUuid() {
+    return userUuid;
   }
 
-  public QProfileChangeDto setLogin(@Nullable String s) {
-    this.login = s;
+  public QProfileChangeDto setUserUuid(@Nullable String s) {
+    this.userUuid = s;
     return this;
   }
 
index 9e9b5e8d76b2ad6f9d7f5bb89442a0072a6174fe..53b284f6374a8d03bd2356b5d4e66ec9cd001589 100644 (file)
@@ -7,7 +7,7 @@
     qpc.kee as "uuid",
     qpc.rules_profile_uuid as rulesProfileUuid,
     qpc.created_at as createdAt,
-    qpc.user_login as login,
+    qpc.user_uuid as userUuid,
     qpc.change_type as changeType,
     qpc.change_data as data
   </sql>
       kee,
       rules_profile_uuid,
       created_at,
-      user_login,
+      user_uuid,
       change_type,
       change_data
     ) values (
       #{uuid, jdbcType=VARCHAR},
       #{rulesProfileUuid, jdbcType=VARCHAR},
       #{createdAt, jdbcType=BIGINT},
-      #{login, jdbcType=VARCHAR},
+      #{userUuid, jdbcType=VARCHAR},
       #{changeType, jdbcType=VARCHAR},
       #{data, jdbcType=VARCHAR}
     )
@@ -53,8 +53,8 @@
   </select>
 
   <select id="selectByQuery" databaseId="oracle" resultType="org.sonar.db.qualityprofile.QProfileChangeDto">
-    select "uuid", rulesProfileUuid, createdAt, login, changeType, data from (
-    select rownum rnum, "uuid", rulesProfileUuid, createdAt, login, changeType, data from (
+    select "uuid", rulesProfileUuid, createdAt, userUuid, changeType, data from (
+    select rownum rnum, "uuid", rulesProfileUuid, createdAt, userUuid, changeType, data from (
     select <include refid="selectColumns" />
     <include refid="sqlSelectByQuery" />
     order by qpc.created_at desc
index 175b057c0d436898dc1a7077147ece85e613cb27..7165a209b13f13157f8e7518a1bac1c5efd1ba9f 100644 (file)
@@ -51,7 +51,7 @@ public class QProfileChangeDaoTest {
 
   @Test
   public void insert() {
-    QProfileChangeDto dto = insertChange("P1", "ACTIVATED", "marcel", "some_data");
+    QProfileChangeDto dto = insertChange("P1", "ACTIVATED", "marcel_uuid", "some_data");
 
     verifyInserted(dto);
   }
@@ -71,7 +71,7 @@ public class QProfileChangeDaoTest {
     assertThat(reloaded.getUuid()).isEqualTo(dto.getUuid());
     assertThat(reloaded.getChangeType()).isEqualTo(dto.getChangeType());
     assertThat(reloaded.getData()).isEqualTo(dto.getData());
-    assertThat(reloaded.getLogin()).isEqualTo(dto.getLogin());
+    assertThat(reloaded.getUserUuid()).isEqualTo(dto.getUserUuid());
     assertThat(reloaded.getRulesProfileUuid()).isEqualTo(dto.getRulesProfileUuid());
     assertThat(reloaded.getCreatedAt()).isPositive();
   }
@@ -180,7 +180,7 @@ public class QProfileChangeDaoTest {
     assertThat(result).hasSize(1);
     QProfileChangeDto change = result.get(0);
     assertThat(change.getRulesProfileUuid()).isEqualTo(inserted.getRulesProfileUuid());
-    assertThat(change.getLogin()).isEqualTo(inserted.getLogin());
+    assertThat(change.getUserUuid()).isEqualTo(inserted.getUserUuid());
     assertThat(change.getData()).isEqualTo(inserted.getData());
     assertThat(change.getChangeType()).isEqualTo(inserted.getChangeType());
     assertThat(change.getUuid()).isEqualTo(inserted.getUuid());
@@ -238,10 +238,10 @@ public class QProfileChangeDaoTest {
     return insertChange(profile.getRulesProfileUuid(), type, login, data);
   }
 
-  private QProfileChangeDto insertChange(String rulesProfileUuid, String type, @Nullable String login, @Nullable String data) {
+  private QProfileChangeDto insertChange(String rulesProfileUuid, String type, @Nullable String userUuid, @Nullable String data) {
     QProfileChangeDto dto = new QProfileChangeDto()
       .setRulesProfileUuid(rulesProfileUuid)
-      .setLogin(login)
+      .setUserUuid(userUuid)
       .setChangeType(type)
       .setData(data);
     underTest.insert(dbSession, dto);
@@ -250,13 +250,13 @@ public class QProfileChangeDaoTest {
 
   private QProfileChangeDto selectChangeByUuid(String uuid) {
     Map<String, Object> map = db.selectFirst(dbSession,
-      "select kee as \"uuid\", rules_profile_uuid as \"rulesProfileUuid\", created_at as \"createdAt\", user_login as \"login\", change_type as \"changeType\", change_data as \"changeData\" from qprofile_changes where kee='"
+      "select kee as \"uuid\", rules_profile_uuid as \"rulesProfileUuid\", created_at as \"createdAt\", user_uuid as \"userUuid\", change_type as \"changeType\", change_data as \"changeData\" from qprofile_changes where kee='"
         + uuid + "'");
     return new QProfileChangeDto()
       .setUuid((String) map.get("uuid"))
       .setRulesProfileUuid((String) map.get("rulesProfileUuid"))
       .setCreatedAt((long) map.get("createdAt"))
-      .setLogin((String) map.get("login"))
+      .setUserUuid((String) map.get("userUuid"))
       .setChangeType((String) map.get("changeType"))
       .setData((String) map.get("changeData"));
   }
index 1b385b00af46df3ff32851034d1529ab3a20c9b3..1518def1142ebab83fd6449dd752f00bc0120a3d 100644 (file)
@@ -57,7 +57,7 @@ public class QualityProfileTesting {
       .setRulesProfileUuid(randomAlphanumeric(40))
       .setCreatedAt(nextLong())
       .setChangeType("ACTIVATED")
-      .setLogin(randomAlphanumeric(10));
+      .setUserUuid("userUuid_" + randomAlphanumeric(10));
   }
 
   /**
index 9693526a7fc6dc59b8082df7e3edef1ce7e9f87a..4a5d0e223e3fa1ce39bd1a79c489b184fb6e5b38 100644 (file)
@@ -45,6 +45,7 @@ public class DbVersion72 implements DbVersion {
       .add(2115, "Add ORGANIZATION_UUID on table users", AddOrganizationUuidToUsers.class)
       .add(2116, "Populate ORGANIZATION_UUID in table users", PopulateOrganizationUuidOnUsers.class)
       .add(2117, "Drop USER_ID from table organizations", DropUserIdFromOrganizations.class)
+      .add(2118, "Rename USER_LOGIN TO USER_UUID on table QPROFILE_CHANGES", RenameUserLoginToUserUuidOnTableQProfileChanges.class)
     ;
   }
 }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableQProfileChanges.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableQProfileChanges.java
new file mode 100644 (file)
index 0000000..1471669
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v72;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.RenameColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class RenameUserLoginToUserUuidOnTableQProfileChanges extends DdlChange {
+
+  public RenameUserLoginToUserUuidOnTableQProfileChanges(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new RenameColumnsBuilder(getDialect(), "qprofile_changes")
+      .renameColumn("user_login",
+        newVarcharColumnDefBuilder()
+          .setColumnName("user_uuid")
+          .setLimit(255)
+          .setIsNullable(true)
+          .build())
+      .build());
+  }
+}
index 4eef7d6033046170e3ffe1f93922cb3ef973916b..346d97cbcb65eadce7264043380df14d7e480c1e 100644 (file)
@@ -34,7 +34,7 @@ public class DbVersion72Test {
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 18);
+    verifyMigrationCount(underTest, 19);
   }
 
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableQProfileChangesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableQProfileChangesTest.java
new file mode 100644 (file)
index 0000000..9eb1978
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.v72;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.VARCHAR;
+
+public class RenameUserLoginToUserUuidOnTableQProfileChangesTest {
+
+  @Rule
+  public final CoreDbTester db = CoreDbTester.createForSchema(RenameUserLoginToUserUuidOnTableQProfileChangesTest.class, "qprofile_changes.sql");
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private RenameUserLoginToUserUuidOnTableQProfileChanges underTest = new RenameUserLoginToUserUuidOnTableQProfileChanges(db.database());
+
+  @Test
+  public void rename_column() throws SQLException {
+    underTest.execute();
+
+    db.assertColumnDefinition("qprofile_changes", "user_uuid", VARCHAR, 255, true);
+    db.assertColumnDoesNotExist("qprofile_changes", "user_login");
+  }
+
+  public void migration_is_not_reentrant() throws SQLException {
+    underTest.execute();
+
+    expectedException.expect(IllegalStateException.class);
+
+    underTest.execute();
+  }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableQProfileChangesTest/qprofile_changes.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableQProfileChangesTest/qprofile_changes.sql
new file mode 100644 (file)
index 0000000..470f311
--- /dev/null
@@ -0,0 +1,9 @@
+CREATE TABLE "QPROFILE_CHANGES" (
+  "KEE" VARCHAR(40) NOT NULL PRIMARY KEY,
+  "RULES_PROFILE_UUID" VARCHAR(255) NOT NULL,
+  "CHANGE_TYPE" VARCHAR(20) NOT NULL,
+  "CREATED_AT" BIGINT NOT NULL,
+  "USER_LOGIN" VARCHAR(255),
+  "CHANGE_DATA" CLOB
+);
+CREATE INDEX "QP_CHANGES_RULES_PROFILE_UUID" ON "QPROFILE_CHANGES" ("RULES_PROFILE_UUID");
\ No newline at end of file
index b1b55fff3c31a2845ae2c809c26ef5a41476a6fa..0dc1c35003afccd9959c6c94159200610b1e999d 100644 (file)
@@ -115,11 +115,11 @@ public class ActiveRuleChange {
     return this;
   }
 
-  public QProfileChangeDto toDto(@Nullable String login) {
+  public QProfileChangeDto toDto(@Nullable String userUuid) {
     QProfileChangeDto dto = new QProfileChangeDto();
     dto.setChangeType(type.name());
     dto.setRulesProfileUuid(getKey().getRuleProfileUuid());
-    dto.setLogin(login);
+    dto.setUserUuid(userUuid);
     Map<String, String> data = new HashMap<>();
     data.put("ruleId", String.valueOf(getRuleId()));
 
index 3544ebbb7fe7fbd828234aec2a76251b4e5f27b9..5237fa2734c6e0691d75df727486ba021b64bd23 100644 (file)
@@ -242,7 +242,7 @@ public class RuleActivator {
       activeRule = doUpdate(change, context, dbSession);
     }
     change.setActiveRule(activeRule);
-    db.qProfileChangeDao().insert(dbSession, change.toDto(userSession.getLogin()));
+    db.qProfileChangeDao().insert(dbSession, change.toDto(userSession.getUuid()));
   }
 
   private ActiveRuleDto doInsert(ActiveRuleChange change, RuleActivationContext context, DbSession dbSession) {
index da685cd8f682cd8c822cba9c05956bfcdce1f85f..45726955d0b8b104bd44f67c234683741b530922 100644 (file)
@@ -25,11 +25,9 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.Set;
 import javax.annotation.CheckForNull;
 import org.sonar.api.resources.Languages;
-import org.sonar.api.rule.RuleKey;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService.NewAction;
@@ -48,6 +46,8 @@ import org.sonar.db.user.UserDto;
 
 import static org.sonar.api.utils.DateUtils.parseEndingDateOrDateTime;
 import static org.sonar.api.utils.DateUtils.parseStartingDateOrDateTime;
+import static org.sonar.core.util.stream.MoreCollectors.toSet;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
 import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_SINCE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_TO;
@@ -113,27 +113,61 @@ public class ChangelogAction implements QProfileWsAction {
       int total = dbClient.qProfileChangeDao().countByQuery(dbSession, query);
 
       List<Change> changelogs = load(dbSession, query);
-      writeResponse(response.newJsonWriter(), total, page, pageSize, changelogs);
+      Map<String, UserDto> usersByUuid = getUsersByUserUuid(dbSession, changelogs);
+      Map<Integer, RuleDefinitionDto> rulesByRuleIds = getRulesByRuleIds(dbSession, changelogs);
+      writeResponse(response.newJsonWriter(), total, page, pageSize, changelogs, usersByUuid, rulesByRuleIds);
     }
   }
 
-  private static void writeResponse(JsonWriter json, int total, int page, int pageSize, List<Change> changelogs) {
+  private Map<String, UserDto> getUsersByUserUuid(DbSession dbSession, List<Change> changes) {
+    Set<String> userUuids = changes.stream()
+      .map(Change::getUserUuid)
+      .filter(Objects::nonNull)
+      .collect(toSet());
+    return dbClient.userDao()
+      .selectByUuids(dbSession, userUuids)
+      .stream()
+      .collect(uniqueIndex(UserDto::getUuid));
+  }
+
+  private Map<Integer, RuleDefinitionDto> getRulesByRuleIds(DbSession dbSession, List<Change> changes) {
+    Set<Integer> ruleIds = changes.stream()
+      .map(c -> c.ruleId)
+      .filter(Objects::nonNull)
+      .collect(toSet());
+    return dbClient.ruleDao()
+      .selectDefinitionByIds(dbSession, Lists.newArrayList(ruleIds))
+      .stream()
+      .collect(uniqueIndex(RuleDefinitionDto::getId));
+  }
+
+  private static void writeResponse(JsonWriter json, int total, int page, int pageSize, List<Change> changelogs,
+    Map<String, UserDto> usersByUuid, Map<Integer, RuleDefinitionDto> rulesByRuleIds) {
     json.beginObject();
     json.prop("total", total);
     json.prop(Param.PAGE, page);
     json.prop(Param.PAGE_SIZE, pageSize);
     json.name("events").beginArray();
-    for (Change change : changelogs) {
-      json.beginObject()
+    changelogs.forEach(change -> {
+      JsonWriter changeWriter = json.beginObject();
+      changeWriter
         .prop("date", DateUtils.formatDateTime(change.getCreatedAt()))
-        .prop("authorLogin", change.getUserLogin())
-        .prop("authorName", change.getUserName())
-        .prop("action", change.getType())
-        .prop("ruleKey", change.getRuleKey().map(RuleKey::toString).orElse(null))
-        .prop("ruleName", change.getRuleName().orElse(null));
+        .prop("action", change.getType());
+      UserDto user = usersByUuid.get(change.getUserUuid());
+      if (user != null) {
+        changeWriter
+          .prop("authorLogin", user.getLogin())
+          .prop("authorName", user.getName());
+      }
+      RuleDefinitionDto rule = rulesByRuleIds.get(change.getRuleId());
+      if (rule != null) {
+        changeWriter
+          .prop("ruleKey", rule.getKey().toString())
+          .prop("ruleName", rule.getName());
+      }
       writeParameters(json, change);
       json.endObject();
-    }
+    });
     json.endArray();
     json.endObject().close();
   }
@@ -151,38 +185,10 @@ public class ChangelogAction implements QProfileWsAction {
    * @return non-null list of changes, by descending order of date
    */
   public List<Change> load(DbSession dbSession, QProfileChangeQuery query) {
-    List<QProfileChangeDto> dtos = dbClient.qProfileChangeDao().selectByQuery(dbSession, query);
-    List<Change> changes = dtos.stream()
+    List<QProfileChangeDto> changeDtos = dbClient.qProfileChangeDao().selectByQuery(dbSession, query);
+    return changeDtos.stream()
       .map(Change::from)
-      .collect(MoreCollectors.toList(dtos.size()));
-    completeUserAndRuleNames(dbSession, changes);
-    return changes;
-  }
-
-  private void completeUserAndRuleNames(DbSession dbSession, List<Change> changes) {
-    Set<String> logins = changes.stream().filter(c -> c.userLogin != null).map(c -> c.userLogin).collect(MoreCollectors.toSet());
-    Map<String, String> userNamesByLogins = dbClient.userDao()
-      .selectByLogins(dbSession, logins)
-      .stream()
-      .collect(java.util.stream.Collectors.toMap(UserDto::getLogin, UserDto::getName));
-
-    Set<Integer> ruleIds = changes.stream()
-      .map(c -> c.ruleId)
-      .filter(Objects::nonNull)
-      .collect(MoreCollectors.toSet());
-    Map<Integer, RuleDefinitionDto> ruleDefinitionsById = dbClient.ruleDao()
-      .selectDefinitionByIds(dbSession, Lists.newArrayList(ruleIds))
-      .stream()
-      .collect(MoreCollectors.uniqueIndex(RuleDefinitionDto::getId));
-
-    changes.forEach(c -> {
-      c.userName = userNamesByLogins.get(c.userLogin);
-      RuleDefinitionDto ruleDefinitionDto = ruleDefinitionsById.get(c.ruleId);
-      if (ruleDefinitionDto != null) {
-        c.ruleKey = ruleDefinitionDto.getKey();
-        c.ruleName = ruleDefinitionDto.getName();
-      }
-    });
+      .collect(MoreCollectors.toList(changeDtos.size()));
   }
 
   static class Change {
@@ -190,12 +196,9 @@ public class ChangelogAction implements QProfileWsAction {
     private String type;
     private long at;
     private String severity;
-    private String userLogin;
-    private String userName;
+    private String userUuid;
     private String inheritance;
     private Integer ruleId;
-    private RuleKey ruleKey;
-    private String ruleName;
     private final Map<String, String> params = new HashMap<>();
 
     private Change() {
@@ -211,13 +214,8 @@ public class ChangelogAction implements QProfileWsAction {
     }
 
     @CheckForNull
-    public String getUserLogin() {
-      return userLogin;
-    }
-
-    @CheckForNull
-    public String getUserName() {
-      return userName;
+    public String getUserUuid() {
+      return userUuid;
     }
 
     public String getType() {
@@ -229,16 +227,9 @@ public class ChangelogAction implements QProfileWsAction {
       return inheritance;
     }
 
-    public Optional<Integer> getRuleId() {
-      return Optional.ofNullable(ruleId);
-    }
-
-    public Optional<RuleKey> getRuleKey() {
-      return Optional.ofNullable(ruleKey);
-    }
-
-    public Optional<String> getRuleName() {
-      return Optional.ofNullable(ruleName);
+    @CheckForNull
+    public Integer getRuleId() {
+      return ruleId;
     }
 
     public long getCreatedAt() {
@@ -253,7 +244,7 @@ public class ChangelogAction implements QProfileWsAction {
       Map<String, String> data = dto.getDataAsMap();
       Change change = new Change();
       change.key = dto.getUuid();
-      change.userLogin = dto.getLogin();
+      change.userUuid = dto.getUserUuid();
       change.type = dto.getChangeType();
       change.at = dto.getCreatedAt();
       // see content of data in class org.sonar.server.qualityprofile.ActiveRuleChange
index 8ed65789da2f2470952d7d404697007e318ebb11..11b0fd2a594a7edff33b3c9c83581798624db2c0 100644 (file)
@@ -33,7 +33,7 @@ import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
 
 public class ActiveRuleChangeTest {
 
-  private static final String A_LOGIN = "A_LOGIN";
+  private static final String A_USER_UUID = "A_USER_UUID";
 
   @Test
   public void toDto() {
@@ -42,11 +42,11 @@ public class ActiveRuleChangeTest {
     int ruleId = new Random().nextInt(963);
     ActiveRuleChange underTest = new ActiveRuleChange(ACTIVATED, key, new RuleDefinitionDto().setId(ruleId));
 
-    QProfileChangeDto result = underTest.toDto(A_LOGIN);
+    QProfileChangeDto result = underTest.toDto(A_USER_UUID);
 
     assertThat(result.getChangeType()).isEqualTo(ACTIVATED.name());
     assertThat(result.getRulesProfileUuid()).isEqualTo(profile.getRulesProfileUuid());
-    assertThat(result.getLogin()).isEqualTo(A_LOGIN);
+    assertThat(result.getUserUuid()).isEqualTo(A_USER_UUID);
     assertThat(result.getDataAsMap().get("ruleId")).isEqualTo(String.valueOf(ruleId));
   }
 }
index f2a2081b53f9771816c2a88bced78850e5a8eafd..a37fb3af4f2851cfd08c6d32046b14bb3748882f 100644 (file)
@@ -193,7 +193,7 @@ public class BuiltInQProfileInsertImplTest {
     assertThat(change.getChangeType()).isEqualTo(ActiveRuleChange.Type.ACTIVATED.name());
     assertThat(change.getCreatedAt()).isPositive();
     assertThat(change.getUuid()).isNotEmpty();
-    assertThat(change.getLogin()).isNull();
+    assertThat(change.getUserUuid()).isNull();
     assertThat(change.getRulesProfileUuid()).isEqualTo(profile.getRulesProfileUuid());
     assertThat(change.getDataAsMap().get("severity")).isEqualTo(expectedSeverity);
   }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRulesImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRulesImplTest.java
new file mode 100644 (file)
index 0000000..e52bf1a
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualityprofile;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.qualityprofile.ActiveRuleDto;
+import org.sonar.db.qualityprofile.QProfileChangeDto;
+import org.sonar.db.qualityprofile.QProfileChangeQuery;
+import org.sonar.db.qualityprofile.QProfileDto;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
+import org.sonar.server.rule.index.RuleIndex;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.util.IntegerTypeValidation;
+import org.sonar.server.util.TypeValidations;
+
+import static java.util.Collections.singleton;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+
+public class QProfileRulesImplTest {
+
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+  @Rule
+  public DbTester db = DbTester.create();
+  @Rule
+  public EsTester es = EsTester.create();
+
+  private RuleIndex ruleIndex = new RuleIndex(es.client(), System2.INSTANCE);
+  private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client());
+  private RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, db.getDbClient(), new TypeValidations(singletonList(new IntegerTypeValidation())), userSession);
+
+  private QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, ruleIndex, activeRuleIndexer);
+
+  @Test
+  public void activate_one_rule() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto qProfile = db.qualityProfiles().insert(organization);
+    RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage(qProfile.getLanguage()));
+    RuleActivation ruleActivation = RuleActivation.create(rule.getId(), Severity.CRITICAL, Collections.emptyMap());
+
+    qProfileRules.activateAndCommit(db.getSession(), qProfile, singleton(ruleActivation));
+
+    assertThat(db.getDbClient().activeRuleDao().selectByProfile(db.getSession(), qProfile))
+      .extracting(ActiveRuleDto::getRuleKey, ActiveRuleDto::getSeverityString)
+      .containsExactlyInAnyOrder(tuple(rule.getKey(), Severity.CRITICAL));
+  }
+
+  @Test
+  public void active_rule_change() {
+    UserDto user = db.users().insertUser();
+    userSession.logIn(user);
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto qProfile = db.qualityProfiles().insert(organization);
+    RuleDefinitionDto rule = db.rules().insert(r -> r.setLanguage(qProfile.getLanguage()));
+    RuleActivation ruleActivation = RuleActivation.create(rule.getId(), Severity.CRITICAL, Collections.emptyMap());
+
+    qProfileRules.activateAndCommit(db.getSession(), qProfile, singleton(ruleActivation));
+
+    assertThat(db.getDbClient().qProfileChangeDao().selectByQuery(db.getSession(), new QProfileChangeQuery(qProfile.getKee())))
+      .extracting(QProfileChangeDto::getUserUuid, QProfileChangeDto::getDataAsMap)
+      .containsExactlyInAnyOrder(tuple(user.getUuid(), ImmutableMap.of("ruleId", Integer.toString(rule.getId()), "severity", Severity.CRITICAL)));
+  }
+}
index 380eba535bb98f6d64b67c6ce92cdd467fa61be3..0a6de2cfecc37284b7467a8e2f944e7724140ffa 100644 (file)
@@ -24,7 +24,6 @@ import java.util.Arrays;
 import java.util.Map;
 import java.util.function.Consumer;
 import javax.annotation.Nullable;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -33,23 +32,18 @@ import org.sonar.api.rule.RuleKey;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.internal.TestSystem2;
-import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileChangeDto;
 import org.sonar.db.qualityprofile.QProfileDto;
-import org.sonar.db.qualityprofile.QualityProfileTesting;
 import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.NotFoundException;
-import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.organization.TestDefaultOrganizationProvider;
 import org.sonar.server.qualityprofile.ActiveRuleChange;
 import org.sonar.server.qualityprofile.ActiveRuleInheritance;
 import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.ws.TestRequest;
 import org.sonar.server.ws.WsActionTester;
-import org.sonar.test.JsonAssert;
 
 import static java.lang.String.valueOf;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -62,29 +56,20 @@ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.
 
 public class ChangelogActionTest {
 
-  private TestSystem2 system2 = new TestSystem2().setNow(1_500_000_000_000L);
+  private static final String DATE = "2011-04-25T01:15:42+0100";
+
+  private TestSystem2 system2 = new TestSystem2().setNow(DateUtils.parseDateTime(DATE).getTime());
 
   @Rule
-  public DbTester dbTester = DbTester.create(system2);
+  public DbTester db = DbTester.create(system2);
   @Rule
   public UserSessionRule userSession = UserSessionRule.standalone();
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
-  private WsActionTester ws;
-  private QProfileWsSupport wsSupport;
-  private OrganizationDto organization;
-  private DefaultOrganizationProvider defaultOrganizationProvider;
-
-  @Before
-  public void before() {
-    system2.setNow(DateUtils.parseDateTime("2011-04-25T01:15:42+0100").getTime());
-    defaultOrganizationProvider = TestDefaultOrganizationProvider.from(dbTester);
-    wsSupport = new QProfileWsSupport(dbTester.getDbClient(), userSession, defaultOrganizationProvider);
-    ws = new WsActionTester(
-      new ChangelogAction(wsSupport, new Languages(), dbTester.getDbClient()));
-    organization = dbTester.organizations().insert();
-  }
+  private QProfileWsSupport wsSupport = new QProfileWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db));
+  private WsActionTester ws = new WsActionTester(
+    new ChangelogAction(wsSupport, new Languages(), db.getDbClient()));
 
   @Test
   public void definition() {
@@ -101,35 +86,34 @@ public class ChangelogActionTest {
 
   @Test
   public void example() {
-    QProfileDto profile = dbTester.qualityProfiles().insert(organization);
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(organization);
     String profileUuid = profile.getRulesProfileUuid();
 
     system2.setNow(DateUtils.parseDateTime("2015-02-23T17:58:39+0100").getTime());
-    RuleDefinitionDto rule1 = dbTester.rules().insert(RuleKey.of("squid", "S2438"), r -> r.setName("\"Threads\" should not be used where \"Runnables\" are expected"));
-    UserDto user1 = dbTester.users().insertUser(u -> u.setLogin("anakin.skywalker").setName("Anakin Skywalker"));
-    insertChange(profile, c -> c.setRulesProfileUuid(profileUuid)
-      .setLogin(user1.getLogin())
+    RuleDefinitionDto rule1 = db.rules().insert(RuleKey.of("squid", "S2438"), r -> r.setName("\"Threads\" should not be used where \"Runnables\" are expected"));
+    UserDto user1 = db.users().insertUser(u -> u.setLogin("anakin.skywalker").setName("Anakin Skywalker"));
+    insertChange(c -> c.setRulesProfileUuid(profileUuid)
+      .setUserUuid(user1.getUuid())
       .setChangeType(ActiveRuleChange.Type.ACTIVATED.name())
       .setData(ImmutableMap.of("severity", "CRITICAL", "ruleId", valueOf(rule1.getId()))));
 
     system2.setNow(DateUtils.parseDateTime("2015-02-23T17:58:18+0100").getTime());
-    RuleDefinitionDto rule2 = dbTester.rules().insert(RuleKey.of("squid", "S2162"), r -> r.setName("\"equals\" methods should be symmetric and work for subclasses"));
-    UserDto user2 = dbTester.users().insertUser(u -> u.setLogin("padme.amidala").setName("Padme Amidala"));
-    QProfileChangeDto change2 = insertChange(profile, c -> c.setRulesProfileUuid(profileUuid)
-      .setLogin(user2.getLogin())
+    RuleDefinitionDto rule2 = db.rules().insert(RuleKey.of("squid", "S2162"), r -> r.setName("\"equals\" methods should be symmetric and work for subclasses"));
+    UserDto user2 = db.users().insertUser(u -> u.setLogin("padme.amidala").setName("Padme Amidala"));
+    QProfileChangeDto change2 = insertChange(c -> c.setRulesProfileUuid(profileUuid)
+      .setUserUuid(user2.getUuid())
       .setChangeType(ActiveRuleChange.Type.DEACTIVATED.name())
       .setData(ImmutableMap.of("ruleId", valueOf(rule2.getId()))));
 
     system2.setNow(DateUtils.parseDateTime("2014-09-12T15:20:46+0200").getTime());
-    RuleDefinitionDto rule3 = dbTester.rules().insert(RuleKey.of("squid", "S00101"), r -> r.setName("Class names should comply with a naming convention"));
-    UserDto user3 = dbTester.users().insertUser(u -> u.setLogin("obiwan.kenobi").setName("Obiwan Kenobi"));
-    QProfileChangeDto change3 = insertChange(profile, c -> c.setRulesProfileUuid(profileUuid)
-      .setLogin(user3.getLogin())
+    RuleDefinitionDto rule3 = db.rules().insert(RuleKey.of("squid", "S00101"), r -> r.setName("Class names should comply with a naming convention"));
+    UserDto user3 = db.users().insertUser(u -> u.setLogin("obiwan.kenobi").setName("Obiwan Kenobi"));
+    QProfileChangeDto change3 = insertChange(c -> c.setRulesProfileUuid(profileUuid)
+      .setUserUuid(user3.getUuid())
       .setChangeType(ActiveRuleChange.Type.ACTIVATED.name())
       .setData(ImmutableMap.of("severity", "MAJOR", "param_format", "^[A-Z][a-zA-Z0-9]*$", "ruleId", valueOf(rule3.getId()))));
 
-    dbTester.commit();
-
     String response = ws.newRequest()
       .setMethod("GET")
       .setParam(PARAM_KEY, profile.getKee())
@@ -141,8 +125,17 @@ public class ChangelogActionTest {
   }
 
   @Test
-  public void find_changelog_by_profile_key() {
-    QProfileDto profile = dbTester.qualityProfiles().insert(organization);
+  public void return_change_with_all_fields() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(organization);
+    UserDto user = db.users().insertUser();
+    RuleDefinitionDto rule = db.rules().insert(RuleKey.of("java", "S001"));
+    insertChange(profile, ActiveRuleChange.Type.ACTIVATED, user, ImmutableMap.of(
+      "ruleId", valueOf(rule.getId()),
+      "severity", "MINOR",
+      "inheritance", ActiveRuleInheritance.INHERITED.name(),
+      "param_foo", "foo_value",
+      "param_bar", "bar_value"));
 
     String response = ws.newRequest()
       .setMethod("GET")
@@ -150,80 +143,133 @@ public class ChangelogActionTest {
       .execute()
       .getInput();
 
-    assertThat(response).isNotEmpty();
+    assertJson(response).isSimilarTo("{\n" +
+      "  \"total\": 1,\n" +
+      "  \"p\": 1,\n" +
+      "  \"ps\": 50,\n" +
+      "  \"events\": [\n" +
+      "    {\n" +
+      "      \"date\": \"" + DATE + "\",\n" +
+      "      \"authorLogin\": \"" + user.getLogin() + "\",\n" +
+      "      \"authorName\": \"" + user.getName() + "\",\n" +
+      "      \"action\": \"ACTIVATED\",\n" +
+      "      \"ruleKey\": \"" + rule.getKey() + "\",\n" +
+      "      \"ruleName\": \"" + rule.getName() + "\",\n" +
+      "      \"params\": {\n" +
+      "        \"severity\": \"MINOR\",\n" +
+      "        \"bar\": \"bar_value\",\n" +
+      "        \"foo\": \"foo_value\"\n" +
+      "      }\n" +
+      "    }\n" +
+      "  ]\n" +
+      "}");
   }
 
   @Test
-  public void find_changelog_by_language_and_name() {
-    QProfileDto qualityProfile = dbTester.qualityProfiles().insert(dbTester.getDefaultOrganization());
+  public void find_changelog_by_profile_key() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(organization);
+    RuleDefinitionDto rule = db.rules().insert();
+    UserDto user = db.users().insertUser();
+    insertChange(profile, ActiveRuleChange.Type.ACTIVATED, user,
+      ImmutableMap.of(
+        "ruleId", valueOf(rule.getId()),
+        "severity", "MINOR"));
 
     String response = ws.newRequest()
       .setMethod("GET")
-      .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
-      .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
+      .setParam(PARAM_KEY, profile.getKee())
       .execute()
       .getInput();
 
-    assertThat(response).isNotEmpty();
+    assertJson(response).isSimilarTo("{\n" +
+      "  \"events\": [\n" +
+      "    {\n" +
+      "      \"date\": \"" + DATE + "\",\n" +
+      "      \"authorLogin\": \"" + user.getLogin() + "\",\n" +
+      "      \"action\": \"ACTIVATED\",\n" +
+      "      \"ruleKey\": \"" + rule.getKey() + "\",\n" +
+      "      \"ruleName\": \"" + rule.getName() + "\",\n" +
+      "      \"params\": {\n" +
+      "        \"severity\": \"MINOR\"\n" +
+      "      }\n" +
+      "    }\n" +
+      "  ]\n" +
+      "}");
   }
 
   @Test
-  public void find_changelog_by_organization_and_language_and_name() {
-    QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization);
+  public void find_changelog_by_language_and_name() {
+    QProfileDto qualityProfile = db.qualityProfiles().insert(db.getDefaultOrganization());
+    RuleDefinitionDto rule = db.rules().insert();
+    UserDto user = db.users().insertUser();
+    insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user,
+      ImmutableMap.of(
+        "ruleId", valueOf(rule.getId()),
+        "severity", "MINOR"));
 
     String response = ws.newRequest()
       .setMethod("GET")
       .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
       .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
-      .setParam(PARAM_ORGANIZATION, organization.getKey())
       .execute()
       .getInput();
 
-    assertThat(response).isNotEmpty();
+    assertJson(response).isSimilarTo("{\n" +
+      "  \"events\": [\n" +
+      "    {\n" +
+      "      \"date\": \"" + DATE + "\",\n" +
+      "      \"authorLogin\": \"" + user.getLogin() + "\",\n" +
+      "      \"action\": \"ACTIVATED\",\n" +
+      "      \"ruleKey\": \"" + rule.getKey() + "\",\n" +
+      "      \"ruleName\": \"" + rule.getName() + "\",\n" +
+      "      \"params\": {\n" +
+      "        \"severity\": \"MINOR\"\n" +
+      "      }\n" +
+      "    }\n" +
+      "  ]\n" +
+      "}");
   }
 
   @Test
-  public void do_not_find_changelog_by_wrong_organization_and_language_and_name() {
-    OrganizationDto organization1 = dbTester.organizations().insert();
-    OrganizationDto organization2 = dbTester.organizations().insert();
-
-    QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization1);
+  public void find_changelog_by_organization_and_language_and_name() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
+    RuleDefinitionDto rule = db.rules().insert();
+    UserDto user = db.users().insertUser();
+    insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user,
+      ImmutableMap.of(
+        "ruleId", valueOf(rule.getId()),
+        "severity", "MINOR"));
 
-    TestRequest request = ws.newRequest()
+    String response = ws.newRequest()
       .setMethod("GET")
       .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
       .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
-      .setParam(PARAM_ORGANIZATION, organization2.getKey());
-
-    thrown.expect(NotFoundException.class);
-
-    request.execute();
-  }
-
-  @Test
-  public void changelog_empty() {
-    QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization);
-
-    String response = ws.newRequest()
-      .setMethod("GET")
-      .setParam(PARAM_KEY, qualityProfile.getKee())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
       .execute()
       .getInput();
 
-    assertThat(response).contains("\"total\":0");
-    assertThat(response).contains("\"events\":[]");
+    assertJson(response).isSimilarTo("{\n" +
+      "  \"events\": [\n" +
+      "    {\n" +
+      "      \"date\": \"" + DATE + "\",\n" +
+      "      \"authorLogin\": \"" + user.getLogin() + "\",\n" +
+      "      \"action\": \"ACTIVATED\",\n" +
+      "      \"ruleKey\": \"" + rule.getKey() + "\",\n" +
+      "      \"ruleName\": \"" + rule.getName() + "\",\n" +
+      "      \"params\": {\n" +
+      "        \"severity\": \"MINOR\"\n" +
+      "      }\n" +
+      "    }\n" +
+      "  ]\n" +
+      "}");
   }
 
   @Test
-  public void changelog_not_empty() {
-    QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization);
-    QProfileChangeDto change = QualityProfileTesting.newQProfileChangeDto()
-      .setUuid(null)
-      .setCreatedAt(0)
-      .setRulesProfileUuid(qualityProfile.getRulesProfileUuid());
-    DbSession session = dbTester.getSession();
-    dbTester.getDbClient().qProfileChangeDao().insert(session, change);
-    session.commit();
+  public void changelog_empty() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
 
     String response = ws.newRequest()
       .setMethod("GET")
@@ -231,48 +277,63 @@ public class ChangelogActionTest {
       .execute()
       .getInput();
 
-    assertThat(response).contains("\"total\":1");
+    assertJson(response).isSimilarTo("{\"total\":0,\"p\":1,\"ps\":50,\"events\":[]}");
   }
 
   @Test
   public void changelog_filter_by_since() {
-    QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization);
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
     system2.setNow(DateUtils.parseDateTime("2011-04-25T01:15:42+0100").getTime());
-    QProfileChangeDto change = QualityProfileTesting.newQProfileChangeDto()
-      .setUuid(null)
-      .setCreatedAt(0)
-      .setRulesProfileUuid(qualityProfile.getRulesProfileUuid());
-    DbSession session = dbTester.getSession();
-    dbTester.getDbClient().qProfileChangeDao().insert(session, change);
-    session.commit();
-
-    String response = ws.newRequest()
+    RuleDefinitionDto rule = db.rules().insert();
+    UserDto user = db.users().insertUser();
+    insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user,
+      ImmutableMap.of(
+        "ruleId", valueOf(rule.getId()),
+        "severity", "MINOR"));
+
+    assertJson(ws.newRequest()
       .setMethod("GET")
       .setParam(PARAM_KEY, qualityProfile.getKee())
       .setParam(PARAM_SINCE, "2011-04-25T01:15:42+0100")
       .execute()
-      .getInput();
-
-    assertThat(response).contains("\"total\":1");
-
-    String response2 = ws.newRequest()
+      .getInput()).isSimilarTo("{\n" +
+        "  \"events\": [\n" +
+        "    {\n" +
+        "      \"date\": \"2011-04-25T01:15:42+0100\",\n" +
+        "      \"authorLogin\": \"" + user.getLogin() + "\",\n" +
+        "      \"action\": \"ACTIVATED\",\n" +
+        "      \"ruleKey\": \"" + rule.getKey() + "\",\n" +
+        "      \"ruleName\": \"" + rule.getName() + "\",\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}");
+
+    assertJson(ws.newRequest()
       .setMethod("GET")
       .setParam(PARAM_KEY, qualityProfile.getKee())
       .setParam(PARAM_SINCE, "2011-04-25T01:15:43+0100")
       .execute()
-      .getInput();
-
-    assertThat(response2).contains("\"total\":0");
+      .getInput()).isSimilarTo("{\n" +
+        "  \"events\": []\n" +
+        "}");
   }
 
   @Test
   public void sort_changelog_events_in_reverse_chronological_order() {
-    QProfileDto profile = dbTester.qualityProfiles().insert(organization);
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(organization);
     system2.setNow(DateUtils.parseDateTime("2011-04-25T01:15:42+0100").getTime());
-    QProfileChangeDto change1 = insertChange(profile, ActiveRuleChange.Type.ACTIVATED, null, null);
+    RuleDefinitionDto rule1 = db.rules().insert();
+    insertChange(profile, ActiveRuleChange.Type.ACTIVATED, null,
+      ImmutableMap.of(
+        "ruleId", valueOf(rule1.getId()),
+        "severity", "MINOR"));
     system2.setNow(DateUtils.parseDateTime("2011-04-25T01:15:43+0100").getTime());
-    QProfileChangeDto change2 = insertChange(profile, ActiveRuleChange.Type.DEACTIVATED, "mazout", null);
-    dbTester.commit();
+    UserDto user = db.users().insertUser();
+    RuleDefinitionDto rule2 = db.rules().insert();
+    insertChange(profile, ActiveRuleChange.Type.DEACTIVATED, user,
+      ImmutableMap.of("ruleId", valueOf(rule2.getId())));
 
     String response = ws.newRequest()
       .setMethod("GET")
@@ -280,60 +341,124 @@ public class ChangelogActionTest {
       .execute()
       .getInput();
 
-    assertThat(response).containsSubsequence("15:43", "15:42");
+    assertJson(response).isSimilarTo("{\n" +
+      "\"events\": [\n" +
+      "    {\n" +
+      "      \"date\": \"2011-04-25T02:15:43+0200\",\n" +
+      "      \"action\": \"DEACTIVATED\",\n" +
+      "      \"authorLogin\": \"" + user.getLogin() + "\",\n" +
+      "      \"ruleKey\": \"" + rule2.getKey() + "\",\n" +
+      "      \"ruleName\": \"" + rule2.getName() + "\",\n" +
+      "      \"params\": {}\n" +
+      "    },\n" +
+      "    {\n" +
+      "      \"date\": \"2011-04-25T02:15:42+0200\",\n" +
+      "      \"action\": \"ACTIVATED\",\n" +
+      "      \"ruleKey\": \"" + rule1.getKey() + "\",\n" +
+      "      \"ruleName\": \"" + rule1.getName() + "\",\n" +
+      "      \"params\": {\n" +
+      "        \"severity\": \"MINOR\"\n" +
+      "      }\n" +
+      "    }\n" +
+      "  ]" +
+      "}");
   }
 
   @Test
-  public void return_change_with_all_fields() {
-    QProfileDto profile = dbTester.qualityProfiles().insert(organization);
-    RuleDefinitionDto rule1 = dbTester.rules().insert(RuleKey.of("java", "S001"));
+  public void changelog_on_no_more_existing_rule() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
+    UserDto user = db.users().insertUser();
+    insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user,
+      ImmutableMap.of("ruleId", "123"));
 
-    Map<String, Object> data = ImmutableMap.of(
-      "ruleId", valueOf(rule1.getId()),
-      "severity", "MINOR",
-      "inheritance", ActiveRuleInheritance.INHERITED.name(),
-      "param_foo", "foo_value",
-      "param_bar", "bar_value");
-    QProfileChangeDto change = insertChange(profile, ActiveRuleChange.Type.ACTIVATED, "theLogin", data);
-    dbTester.commit();
+    String response = ws.newRequest()
+      .setMethod("GET")
+      .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
+      .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .execute()
+      .getInput();
+
+    assertJson(response).isSimilarTo("{\n" +
+      "  \"events\": [\n" +
+      "    {\n" +
+      "      \"date\": \"" + DATE + "\",\n" +
+      "      \"action\": \"ACTIVATED\",\n" +
+      "      \"params\": {}\n" +
+      "    }\n" +
+      "  ]\n" +
+      "}");
+    assertThat(response).doesNotContain("ruleKey", "ruleName");
+  }
+
+  @Test
+  public void changelog_on_no_more_existing_user() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization);
+    RuleDefinitionDto rule = db.rules().insert();
+    insertChange(c -> c.setRulesProfileUuid(qualityProfile.getRulesProfileUuid())
+      .setUserUuid("UNKNOWN")
+      .setChangeType(ActiveRuleChange.Type.ACTIVATED.name())
+      .setData(ImmutableMap.of("ruleId", rule.getId())));
 
     String response = ws.newRequest()
       .setMethod("GET")
-      .setParam(PARAM_KEY, profile.getKee())
+      .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
+      .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
       .execute()
       .getInput();
 
-    JsonAssert.assertJson(response).isSimilarTo("{\n" +
-      "  \"total\": 1,\n" +
-      "  \"p\": 1,\n" +
-      "  \"ps\": 50,\n" +
+    assertJson(response).isSimilarTo("{\n" +
       "  \"events\": [\n" +
       "    {\n" +
-      "      \"date\": \"2011-04-25T02:15:42+0200\",\n" +
-      "      \"authorLogin\": \"theLogin\",\n" +
+      "      \"date\": \"" + DATE + "\",\n" +
+      "      \"ruleKey\": \"" + rule.getKey() + "\",\n" +
+      "      \"ruleName\": \"" + rule.getName() + "\",\n" +
       "      \"action\": \"ACTIVATED\",\n" +
-      "      \"ruleKey\": \"java:S001\",\n" +
-      "      \"params\": {\n" +
-      "        \"severity\": \"MINOR\",\n" +
-      "        \"bar\": \"bar_value\",\n" +
-      "        \"foo\": \"foo_value\"\n" +
-      "      }\n" +
+      "      \"params\": {}\n" +
       "    }\n" +
       "  ]\n" +
       "}");
+    assertThat(response).doesNotContain("authorLogin", "authorName");
+  }
+
+  @Test
+  public void do_not_find_changelog_by_wrong_organization_and_language_and_name() {
+    OrganizationDto organization1 = db.organizations().insert();
+    OrganizationDto organization2 = db.organizations().insert();
+    QProfileDto qualityProfile = db.qualityProfiles().insert(organization1);
+    RuleDefinitionDto rule = db.rules().insert();
+    UserDto user = db.users().insertUser();
+    insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, user,
+      ImmutableMap.of(
+        "ruleId", valueOf(rule.getId()),
+        "severity", "MINOR"));
+
+    thrown.expect(NotFoundException.class);
+
+    ws.newRequest()
+      .setMethod("GET")
+      .setParam(PARAM_LANGUAGE, qualityProfile.getLanguage())
+      .setParam(PARAM_QUALITY_PROFILE, qualityProfile.getName())
+      .setParam(PARAM_ORGANIZATION, organization2.getKey())
+      .execute();
   }
 
-  private QProfileChangeDto insertChange(QProfileDto profile, ActiveRuleChange.Type type, @Nullable String login, @Nullable Map<String, Object> data) {
-    return insertChange(profile, c -> c.setRulesProfileUuid(profile.getRulesProfileUuid())
-      .setLogin(login)
+  private QProfileChangeDto insertChange(QProfileDto profile, ActiveRuleChange.Type type, @Nullable UserDto user, @Nullable Map<String, Object> data) {
+    return insertChange(c -> c.setRulesProfileUuid(profile.getRulesProfileUuid())
+      .setUserUuid(user == null ? null : user.getUuid())
       .setChangeType(type.name())
       .setData(data));
   }
 
-  private QProfileChangeDto insertChange(QProfileDto profile, Consumer<QProfileChangeDto>... consumers) {
+  @SafeVarargs
+  private final QProfileChangeDto insertChange(Consumer<QProfileChangeDto>... consumers) {
     QProfileChangeDto dto = new QProfileChangeDto();
     Arrays.stream(consumers).forEach(c -> c.accept(dto));
-    dbTester.getDbClient().qProfileChangeDao().insert(dbTester.getSession(), dto);
+    db.getDbClient().qProfileChangeDao().insert(db.getSession(), dto);
+    db.commit();
     return dto;
   }
 }
index dc94a7db42d636a61a590b1cf264c74203f53c52..04cb43794702e79e06458c801abbb4427932d2df 100644 (file)
@@ -25,7 +25,6 @@ import org.json.JSONException;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
-import org.skyscreamer.jsonassert.JSONAssert;
 import org.skyscreamer.jsonassert.JSONCompareMode;
 import org.sonarqube.qa.util.Tester;
 import org.sonarqube.tests.Category6Suite;
@@ -35,25 +34,22 @@ import org.sonarqube.ws.Qualityprofiles.SearchWsResponse;
 import org.sonarqube.ws.Qualityprofiles.ShowResponse;
 import org.sonarqube.ws.Qualityprofiles.ShowResponse.CompareToSonarWay;
 import org.sonarqube.ws.Qualityprofiles.ShowResponse.QualityProfile;
+import org.sonarqube.ws.Users.CreateWsResponse.User;
 import org.sonarqube.ws.client.GetRequest;
 import org.sonarqube.ws.client.PostRequest;
 import org.sonarqube.ws.client.WsResponse;
+import org.sonarqube.ws.client.permissions.AddUserRequest;
 import org.sonarqube.ws.client.qualityprofiles.ChangelogRequest;
 import org.sonarqube.ws.client.qualityprofiles.SearchRequest;
 import org.sonarqube.ws.client.qualityprofiles.ShowRequest;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
 
 public class QualityProfilesWsTest {
   private static final String RULE_ONE_BUG_PER_LINE = "xoo:OneBugIssuePerLine";
   private static final String RULE_ONE_ISSUE_PER_LINE = "xoo:OneIssuePerLine";
 
-  private static final String EXPECTED_CHANGELOG = "{\"total\":2,\"p\":1,\"ps\":50,\"events\":[" +
-    "{\"authorLogin\":\"admin\",\"authorName\":\"Administrator\",\"action\":\"ACTIVATED\",\"ruleKey\":\"xoo:OneIssuePerLine\",\"ruleName\":\"One Issue Per Line\",\"params\":{\"severity\":\"MAJOR\"}}," +
-    "{\"authorLogin\":\"admin\",\"authorName\":\"Administrator\",\"action\":\"ACTIVATED\",\"ruleKey\":\"xoo:OneBugIssuePerLine\",\"ruleName\":\"One Bug Issue Per Line\",\"params\":{\"severity\":\"MAJOR\"}}" +
-    "]}";
-  private static final String EXPECTED_CHANGELOG_EMPTY = "{\"total\":0,\"p\":1,\"ps\":50,\"events\":[]}";
-
   @ClassRule
   public static Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
 
@@ -111,8 +107,8 @@ public class QualityProfilesWsTest {
     assertThat(tester.qProfiles().service().show(new ShowRequest()
       .setKey(xooProfile.getKey())
       .setCompareToSonarWay("true")).getCompareToSonarWay())
-      .extracting(CompareToSonarWay::getProfile, CompareToSonarWay::getProfileName, CompareToSonarWay::getMissingRuleCount)
-      .containsExactly(sonarWay.getKey(), sonarWay.getName(), 0L);
+        .extracting(CompareToSonarWay::getProfile, CompareToSonarWay::getProfileName, CompareToSonarWay::getMissingRuleCount)
+        .containsExactly(sonarWay.getKey(), sonarWay.getName(), 0L);
   }
 
   @Test
@@ -125,7 +121,7 @@ public class QualityProfilesWsTest {
     // Check 'name' parameter is taken into account
     assertThat(tester.wsClient().wsConnector()
       .call(new GetRequest("profiles/export?language=xoo&qualityProfile=empty&format=XooFakeExporter")).content())
-      .isEqualTo("xoo -> empty -> 0");
+        .isEqualTo("xoo -> empty -> 0");
   }
 
   @Test
@@ -137,23 +133,36 @@ public class QualityProfilesWsTest {
       .setOrganization(org.getKey())
       .setLanguage(profile.getLanguage())
       .setQualityProfile(profile.getName()));
-    JSONAssert.assertEquals(EXPECTED_CHANGELOG_EMPTY, changelog, JSONCompareMode.STRICT);
+    assertEquals("{\"total\":0,\"p\":1,\"ps\":50,\"events\":[]}",
+      changelog, JSONCompareMode.STRICT);
 
+    User user = tester.users().generateMember(org);
+    tester.permissions().service().addUser(new AddUserRequest().setOrganization(org.getKey()).setLogin(user.getLogin()).setPermission("profileadmin"));
+    tester.as(user.getLogin()).qProfiles().activateRule(profile, RULE_ONE_ISSUE_PER_LINE);
     tester.qProfiles().activateRule(profile, RULE_ONE_BUG_PER_LINE);
-    tester.qProfiles().activateRule(profile, RULE_ONE_ISSUE_PER_LINE);
 
     String changelog2 = tester.wsClient().qualityprofiles().changelog(new ChangelogRequest()
       .setOrganization(org.getKey())
       .setLanguage(profile.getLanguage())
       .setQualityProfile(profile.getName()));
-    JSONAssert.assertEquals(EXPECTED_CHANGELOG, changelog2, JSONCompareMode.LENIENT);
+    assertEquals("{\"total\":2,\"p\":1,\"ps\":50,\"events\":[" +
+      "{\"authorLogin\":\"" + user.getLogin() + "\",\"authorName\":\"" + user.getName() + "\"," +
+      "\"action\":\"ACTIVATED\"," +
+      "\"ruleKey\":\"xoo:OneIssuePerLine\",\"ruleName\":\"One Issue Per Line\"," +
+      "\"params\":{\"severity\":\"MAJOR\"}}," +
+      "{\"authorLogin\":\"admin\",\"authorName\":\"Administrator\"," +
+      "\"action\":\"ACTIVATED\"," +
+      "\"ruleKey\":\"xoo:OneBugIssuePerLine\",\"ruleName\":\"One Bug Issue Per Line\"," +
+      "\"params\":{\"severity\":\"MAJOR\"}}" + "]}",
+      changelog2, JSONCompareMode.LENIENT);
 
     String changelog3 = tester.wsClient().qualityprofiles().changelog(new ChangelogRequest()
       .setOrganization(org.getKey())
       .setLanguage(profile.getLanguage())
       .setQualityProfile(profile.getName())
       .setSince("2999-12-31T23:59:59+0000"));
-    JSONAssert.assertEquals(EXPECTED_CHANGELOG_EMPTY, changelog3, JSONCompareMode.STRICT);
+    assertEquals("{\"total\":0,\"p\":1,\"ps\":50,\"events\":[]}",
+      changelog3, JSONCompareMode.STRICT);
   }
 
   private SearchWsResponse.QualityProfile getProfile(Organization organization, Predicate<SearchWsResponse.QualityProfile> filter) {
index 2023d0766b1187e283ef4a93ef4e283c738dd7d5..97612a23f3aea674fea08c6dfda383324a614110 100644 (file)
@@ -22,12 +22,14 @@ package org.sonarqube.tests.user;
 import com.sonar.orchestrator.Orchestrator;
 import com.sonar.orchestrator.build.SonarScanner;
 import java.util.List;
+import org.json.JSONException;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.TesterSession;
 import org.sonarqube.ws.Issues;
 import org.sonarqube.ws.Issues.Issue;
 import org.sonarqube.ws.Organizations.Organization;
@@ -39,11 +41,14 @@ import org.sonarqube.ws.client.GetRequest;
 import org.sonarqube.ws.client.issues.AssignRequest;
 import org.sonarqube.ws.client.organizations.AddMemberRequest;
 import org.sonarqube.ws.client.organizations.SearchRequest;
+import org.sonarqube.ws.client.qualityprofiles.ChangelogRequest;
 import org.sonarqube.ws.client.settings.SetRequest;
 import org.sonarqube.ws.client.settings.ValuesRequest;
+import org.sonarqube.ws.client.usertokens.GenerateRequest;
 
 import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
 import static util.ItUtils.projectDir;
 
 public class SonarCloudUpdateLoginDuringAuthenticationTest {
@@ -176,6 +181,59 @@ public class SonarCloudUpdateLoginDuringAuthenticationTest {
         .containsExactlyInAnyOrder(newLogin);
   }
 
+  @Test
+  public void qprofile_changes_after_login_update() throws JSONException {
+    tester.settings().setGlobalSettings("sonar.organizations.anyoneCanCreate", "true");
+    String providerId = tester.users().generateProviderId();
+    String oldLogin = tester.users().generateLogin();
+    authenticate(oldLogin, providerId);
+
+    // Activate a rule on a new quality profile
+    String userToken = tester.wsClient().userTokens().generate(new GenerateRequest().setLogin(oldLogin).setName("token")).getToken();
+    TesterSession userSession = tester.as(userToken, null);
+    Organization organization = userSession.organizations().generate();
+    Qualityprofiles.CreateWsResponse.QualityProfile qProfile = userSession.qProfiles().createXooProfile(organization);
+    userSession.qProfiles().activateRule(qProfile, "xoo:OneIssuePerLine");
+
+    // Check changelog contain user login
+    String changelog = tester.qProfiles().service().changelog(new ChangelogRequest()
+      .setOrganization(organization.getKey())
+      .setQualityProfile(qProfile.getName())
+      .setLanguage(qProfile.getLanguage()));
+    assertEquals(
+      "{\n" +
+        "  \"events\": [\n" +
+        "    {\n" +
+        "      \"ruleKey\": \"xoo:OneIssuePerLine\",\n" +
+        "      \"authorLogin\": \"" + oldLogin + "\",\n" +
+        "      \"action\": \"ACTIVATED\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}",
+      changelog,
+      false);
+
+    // Update login during authentication, check changelog contains new user login
+    String newLogin = tester.users().generateLogin();
+    authenticate(newLogin, providerId);
+    String changelogReloaded = tester.qProfiles().service().changelog(new ChangelogRequest()
+      .setOrganization(organization.getKey())
+      .setQualityProfile(qProfile.getName())
+      .setLanguage(qProfile.getLanguage()));
+    assertEquals(
+      "{\n" +
+        "  \"events\": [\n" +
+        "    {\n" +
+        "      \"ruleKey\": \"xoo:OneIssuePerLine\",\n" +
+        "      \"authorLogin\": \"" + newLogin + "\",\n" +
+        "      \"action\": \"ACTIVATED\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}",
+      changelogReloaded,
+      false);
+  }
+
   private void authenticate(String login, String providerId) {
     tester.settings().setGlobalSettings("sonar.auth.fake-base-id-provider.user", login + "," + providerId + ",fake-" + login + ",John,john@email.com");
     tester.wsClient().wsConnector().call(
index 64011f22d41c433cc37e43ecf8bc3098ca574dc5..fa2d492d6ee2212afb3ff9aa37709a8e0c13223f 100644 (file)
@@ -54,7 +54,6 @@ public class SonarCloudUserSuite {
 
     // reduce memory for Elasticsearch
     .setServerProperty("sonar.search.javaOpts", "-Xms128m -Xmx128m")
-    .setServerProperty("sonar.web.javaAdditionalOpts", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8001")
 
     .build();