]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6308 WS to show a profile's changelog
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Wed, 8 Apr 2015 13:16:27 +0000 (15:16 +0200)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Tue, 14 Apr 2015 08:05:02 +0000 (10:05 +0200)
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileChangelogAction.java [new file with mode: 0644]
server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/example-changelog.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest.java [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_empty.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_nominal.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_page1.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_page2.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_page3.json [new file with mode: 0644]

index 43f1691fb9f53d50048de8f6e08fb9974fb8e104..f3b490d007bdc284e917c5fdcb84179e931644f3 100644 (file)
@@ -391,6 +391,7 @@ class ServerComponents {
     pico.addSingleton(QProfileImportersAction.class);
     pico.addSingleton(QProfileInheritanceAction.class);
     pico.addSingleton(QProfileChangeParentAction.class);
+    pico.addSingleton(QProfileChangelogAction.class);
     pico.addSingleton(QProfilesWs.class);
     pico.addSingleton(ProfilesWs.class);
     pico.addSingleton(RuleActivationActions.class);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileChangelogAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileChangelogAction.java
new file mode 100644 (file)
index 0000000..6869e82
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.ws;
+
+import org.sonar.api.resources.Languages;
+import org.sonar.api.server.ws.*;
+import org.sonar.api.server.ws.WebService.NewAction;
+import org.sonar.api.server.ws.WebService.NewController;
+import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.Paging;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.es.SearchOptions;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.qualityprofile.QProfileActivity;
+import org.sonar.server.qualityprofile.QProfileActivityQuery;
+import org.sonar.server.qualityprofile.QProfileFactory;
+import org.sonar.server.qualityprofile.QProfileService;
+import org.sonar.server.search.Result;
+
+import java.util.Date;
+import java.util.Map.Entry;
+
+public class QProfileChangelogAction implements BaseQProfileWsAction {
+
+  private static final String PARAM_SINCE = "since";
+  private static final String PARAM_TO = "to";
+
+  private QProfileService service;
+  private DbClient dbClient;
+  private QProfileFactory profileFactory;
+  private Languages languages;
+
+  public QProfileChangelogAction(QProfileService service, DbClient dbClient, QProfileFactory profileFactory, Languages languages) {
+    this.service = service;
+    this.dbClient = dbClient;
+    this.profileFactory = profileFactory;
+    this.languages = languages;
+  }
+
+  @Override
+  public void define(NewController context) {
+    NewAction changelog = context.createAction("changelog")
+      .setSince("5.2")
+      .setDescription("Get the history of changes on a quality profile: rule activation/deactivation, change in parameters/severity. " +
+        "Events are ordered by date in descending order (most recent first).")
+      .setHandler(this)
+      .setResponseExample(getClass().getResource("example-changelog.json"));
+
+    QProfileIdentificationParamUtils.defineProfileParams(changelog, languages);
+
+    changelog.addPagingParams(50);
+
+    changelog.createParam(PARAM_SINCE)
+      .setDescription("Start date for the changelog.")
+      .setExampleValue("2011-04-25");
+
+    changelog.createParam(PARAM_TO)
+      .setDescription("End date for the changelog.")
+      .setExampleValue("2013-07-25");
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    DbSession session = dbClient.openSession(false);
+    try {
+      String profileKey = QProfileIdentificationParamUtils.getProfileKeyFromParameters(request, profileFactory, session);
+      if (dbClient.qualityProfileDao().getByKey(session, profileKey) == null) {
+        throw new NotFoundException(String.format("Could not find a profile with key '%s'", profileKey));
+      }
+
+      QProfileActivityQuery query = new QProfileActivityQuery().setQprofileKey(profileKey);
+      Date since = request.paramAsDate(PARAM_SINCE);
+      if (since != null) {
+        query.setSince(since);
+      }
+      Date to = request.paramAsDate(PARAM_TO);
+      if (to != null) {
+        query.setTo(to);
+      }
+      SearchOptions options = new SearchOptions();
+
+      int page = request.mandatoryParamAsInt(Param.PAGE);
+      options.setPage(page, request.mandatoryParamAsInt(Param.PAGE_SIZE));
+
+      Result<QProfileActivity> result = service.searchActivities(query, options);
+      writeResponse(response.newJsonWriter(), result, Paging.create(options.getLimit(), page, (int) result.getTotal()));
+    } finally {
+      session.close();
+    }
+  }
+
+  private void writeResponse(JsonWriter json, Result<QProfileActivity> result, Paging paging) {
+    json.beginObject();
+    json.prop("total", result.getTotal());
+    json.prop(Param.PAGE, paging.pageIndex());
+    json.prop(Param.PAGE_SIZE, paging.pageSize());
+    json.name("events").beginArray();
+    for (QProfileActivity event : result.getHits()) {
+      json.beginObject()
+        .prop("date", DateUtils.formatDateTime(event.getCreatedAt()))
+        .prop("authorLogin", event.getLogin())
+        .prop("authorName", event.authorName())
+        .prop("action", event.getAction())
+        .prop("ruleKey", event.ruleKey().toString())
+        .prop("ruleName", event.ruleName());
+      writeParameters(json, event);
+      json.endObject();
+    }
+    json.endArray();
+    json.endObject().close();
+  }
+
+  private void writeParameters(JsonWriter json, QProfileActivity event) {
+    json.name("params").beginObject()
+      .prop("severity", event.severity());
+    for (Entry<String, String> param : event.parameters().entrySet()) {
+      json.prop(param.getKey(), param.getValue());
+    }
+    json.endObject();
+  }
+
+}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/example-changelog.json b/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/example-changelog.json
new file mode 100644 (file)
index 0000000..50ba1e5
--- /dev/null
@@ -0,0 +1,38 @@
+{
+  "total": 3,
+  "ps": 10,
+  "p": 1,
+  "events": [
+    {
+      "date" : "2015-02-23T17:58:39+0100",
+      "action" : "ACTIVATED",
+      "authorLogin" : "anakin.skywalker",
+      "authorName" : "Anakin Skywalker",
+      "ruleKey" : "squid:S2438",
+      "ruleName" : "\"Threads\" should not be used where \"Runnables\" are expected",
+      "params" : {
+        "severity" : "CRITICAL"
+      }
+    },
+    {
+      "date" : "2015-02-23T17:58:18+0100",
+      "action" : "DEACTIVATED",
+      "authorLogin" : "padme.amidala",
+      "authorName" : "Padme Amidala",
+      "ruleKey" : "squid:S2162",
+      "ruleName" : "\"equals\" methods should be symmetric and work for subclasses"
+    },
+    {
+      "action" : "ACTIVATED",
+      "authorLogin" : "obiwan.kenobi",
+      "authorName" : "Obiwan Kenobi",
+      "ruleKey" : "squid:S00101",
+      "ruleName" : "Class names should comply with a naming convention",
+      "date" : "2014-09-12T15:20:46+0200",
+      "params" : {
+        "severity" : "MAJOR",
+        "format" : "^[A-Z][a-zA-Z0-9]*$"
+      }
+    }
+  ]
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest.java
new file mode 100644 (file)
index 0000000..d7d7b43
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.ws;
+
+import org.joda.time.DateTime;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.qualityprofile.db.ActiveRuleKey;
+import org.sonar.core.rule.RuleDto;
+import org.sonar.core.user.UserDto;
+import org.sonar.server.activity.ActivityService;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.qualityprofile.ActiveRuleChange;
+import org.sonar.server.qualityprofile.QProfileTesting;
+import org.sonar.server.rule.RuleTesting;
+import org.sonar.server.tester.ServerTester;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.ws.WsTester;
+
+import java.util.Date;
+
+import static org.sonar.server.qualityprofile.QProfileTesting.XOO_P1_KEY;
+
+public class QProfileChangelogActionMediumTest {
+
+  @ClassRule
+  public static ServerTester tester = new ServerTester();
+
+  private DbClient db;
+  private DbSession dbSession;
+  private WsTester wsTester;
+
+  @Before
+  public void before() {
+    tester.clearDbAndIndexes();
+    db = tester.get(DbClient.class);
+    dbSession = db.openSession(false);
+
+    // create pre-defined rules
+    RuleDto xooRule1 = RuleTesting.newXooX1().setSeverity("MINOR");
+    db.ruleDao().insert(dbSession, xooRule1);
+
+    // create pre-defined profiles P1 and P2
+    db.qualityProfileDao().insert(dbSession, QProfileTesting.newXooP1(), QProfileTesting.newXooP2());
+
+    // create a user for activity author
+    UserDto user = new UserDto().setLogin("david").setName("David").setEmail("dav@id.com").setCreatedAt(System.currentTimeMillis()).setUpdatedAt(System.currentTimeMillis());
+    db.userDao().insert(dbSession, user);
+
+    dbSession.commit();
+    dbSession.clearCache();
+
+    wsTester = new WsTester(tester.get(QProfilesWs.class));
+  }
+
+  @After
+  public void after() throws Exception {
+    dbSession.close();
+  }
+
+  @Test
+  public void changelog_nominal() throws Exception {
+    MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN).setLogin("david");
+    tester.get(ActivityService.class).save(ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1))
+      .setSeverity(Severity.MAJOR)
+      .setParameter("max", "10").toActivity());
+
+    wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "changelog").setParam("profileKey", XOO_P1_KEY)
+      .execute().assertJson(getClass(), "changelog_nominal.json");
+  }
+
+  @Test
+  public void changelog_with_dates() throws Exception {
+    Date yesterday = DateTime.now().minusDays(1).toDate();
+    Date tomorrow = DateTime.now().plusDays(1).toDate();
+
+    MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN).setLogin("david");
+    tester.get(ActivityService.class).save(ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1))
+      .setSeverity(Severity.MAJOR)
+      .setParameter("max", "10").toActivity());
+
+    // Tests with "since"
+    wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "changelog").setParam("profileKey", XOO_P1_KEY).setParam("since", DateUtils.formatDate(yesterday))
+      .execute().assertJson(getClass(), "changelog_nominal.json");
+    wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "changelog").setParam("profileKey", XOO_P1_KEY).setParam("since", DateUtils.formatDate(tomorrow))
+      .execute().assertJson(getClass(), "changelog_empty.json");
+
+    // Tests with "to"
+    wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "changelog").setParam("profileKey", XOO_P1_KEY).setParam("to", DateUtils.formatDate(yesterday))
+      .execute().assertJson(getClass(), "changelog_empty.json");
+    wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "changelog").setParam("profileKey", XOO_P1_KEY).setParam("to", DateUtils.formatDate(tomorrow))
+      .execute().assertJson(getClass(), "changelog_nominal.json");
+  }
+
+  @Test
+  public void changelog_with_pagination() throws Exception {
+    MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN).setLogin("david");
+    tester.get(ActivityService.class).save(ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1))
+      .setSeverity(Severity.MAJOR)
+      .setParameter("max", "10").toActivity());
+    tester.get(ActivityService.class).save(ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1))
+      .setSeverity(Severity.CRITICAL)
+      .setParameter("max", "20").toActivity());
+
+    wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "changelog").setParam("profileKey", XOO_P1_KEY).setParam("ps", "1")
+      .execute().assertJson(getClass(), "changelog_page1.json");
+    wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "changelog").setParam("profileKey", XOO_P1_KEY).setParam("ps", "1").setParam("p", "2")
+      .execute().assertJson(getClass(), "changelog_page2.json");
+    wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "changelog").setParam("profileKey", XOO_P1_KEY).setParam("ps", "1").setParam("p", "3")
+      .execute().assertJson(getClass(), "changelog_page3.json");
+  }
+
+  @Test(expected = NotFoundException.class)
+  public void fail_on_unknown_profile() throws Exception {
+    wsTester.newGetRequest(QProfilesWs.API_ENDPOINT, "changelog").setParam("profileKey", "unknown-profile").execute();
+  }
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_empty.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_empty.json
new file mode 100644 (file)
index 0000000..22d12b0
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "total": 0,
+  "p": 1,
+  "ps": 50,
+  "events": []
+}
\ No newline at end of file
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_nominal.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_nominal.json
new file mode 100644 (file)
index 0000000..b0262c2
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "total": 1,
+  "p": 1,
+  "ps": 50,
+  "events": [
+    {
+      "action" : "ACTIVATED",
+      "authorLogin" : "david",
+      "authorName" : "David",
+      "ruleKey" : "xoo:x1",
+      "ruleName" : "Rule x1",
+      "params" : {
+        "severity" : "MAJOR",
+        "max" : "10"
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_page1.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_page1.json
new file mode 100644 (file)
index 0000000..adc4088
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "total": 2,
+  "p": 1,
+  "ps": 1,
+  "events": [
+    {
+      "action" : "ACTIVATED",
+      "authorLogin" : "david",
+      "authorName" : "David",
+      "ruleKey" : "xoo:x1",
+      "ruleName" : "Rule x1",
+      "params" : {
+        "severity" : "CRITICAL",
+        "max" : "20"
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_page2.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_page2.json
new file mode 100644 (file)
index 0000000..728705a
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "total": 2,
+  "p": 2,
+  "ps": 1,
+  "events": [
+    {
+      "action" : "ACTIVATED",
+      "authorLogin" : "david",
+      "authorName" : "David",
+      "ruleKey" : "xoo:x1",
+      "ruleName" : "Rule x1",
+      "params" : {
+        "severity" : "MAJOR",
+        "max" : "10"
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_page3.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileChangelogActionMediumTest/changelog_page3.json
new file mode 100644 (file)
index 0000000..06536dd
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "total": 2,
+  "p": 3,
+  "ps": 1,
+  "events": []
+}
\ No newline at end of file