]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5209 Add manual rules in /api/components/app
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 18 Jun 2014 12:19:57 +0000 (14:19 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 18 Jun 2014 12:20:07 +0000 (14:20 +0200)
29 files changed:
sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java
sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java
sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml
sonar-server/src/main/java/org/sonar/server/component/persistence/ComponentDao.java
sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java
sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json [deleted file]
sonar-server/src/main/resources/org/sonar/server/component/ws/components-example-app.json [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/component/persistence/ComponentDaoTest.java
sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java
sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java
sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/empty.xml [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/insert-result.xml [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_extension.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_extension_having_permission.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_issues_measures.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_issues_measures_when_period_is_set.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_manual_rules.json [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_measures.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_measures_when_period_is_set.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_periods.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_rules.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_rules_when_period_is_set.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_severities.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_severities_when_period_is_set.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_sub_project_equals_to_project.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_tabs.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_tests_measure.json
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_without_sub_project.json

index a106603b7bfc68bd67b86512a9d3dadf07f1eb40..1460e2034bca754d2418ecabba66640a3e765f86 100644 (file)
@@ -37,6 +37,7 @@ public class ComponentDto extends Dto<String> implements Component {
   private String language;
   private Long projectId;
   private Long subProjectId;
+  private boolean enabled = true;
 
   public Long getId() {
     return id;
@@ -136,6 +137,15 @@ public class ComponentDto extends Dto<String> implements Component {
     return this;
   }
 
+  public boolean isEnabled() {
+    return enabled;
+  }
+
+  public ComponentDto setEnabled(boolean enabled) {
+    this.enabled = enabled;
+    return this;
+  }
+
   @Override
   public String getKey() {
     return kee;
index a95a6b6cf41f423729569cdf5aea2a64d8655c4e..cb8c8dac1a04c65beeaa0a2368fec29c8228ddd3 100644 (file)
@@ -31,4 +31,6 @@ public interface ComponentMapper {
   ComponentDto selectById(long id);
 
   long countById(long id);
+
+  void insert(ComponentDto rule);
 }
index 59bc16ecfbcfe0a363abe9cf14cf3de3040e5049..de9ad8a46b58cee23c6eb678fda9f2770083aa12 100644 (file)
     WHERE p.enabled=${_true} AND p.id=#{id}
   </select>
 
+  <sql id="insertColumns">
+    (kee, name, long_name, qualifier, scope, language, root_id, path, created_at)
+  </sql>
+
+  <insert id="insert" parameterType="Component" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
+    insert into projects <include refid="insertColumns"/>
+    values (#{kee}, #{name}, #{longName}, #{qualifier}, #{scope}, #{language}, #{subProjectId}, #{path}, #{createdAt})
+  </insert>
+
 </mapper>
 
index 6c59c2179c07ac5b93d7193c9ee0164033db423b..cf64e3f423b2b4c253df42fd4ae86d6368e91c6d 100644 (file)
@@ -62,7 +62,8 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String>
 
   @Override
   protected ComponentDto doInsert(DbSession session, ComponentDto item) {
-    throw notImplemented();
+    getMapper(session).insert(item);
+    return item;
   }
 
   @Override
@@ -75,12 +76,12 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String>
     throw notImplemented();
   }
 
-  private static IllegalStateException notImplemented() {
-    throw new IllegalStateException("Not implemented yet");
-  }
-
   @Override
   public void synchronizeAfter(DbSession session, long timestamp) {
+    throw notImplemented();
+  }
+
+  private static IllegalStateException notImplemented() {
     throw new IllegalStateException("Not implemented yet");
   }
 }
index 6852edbd5c924e6f860686891ada02c94d916450..a024c13319d4766c8abe6a957021d0f9e30518f8 100644 (file)
@@ -51,6 +51,12 @@ import org.sonar.server.db.DbClient;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.issue.IssueService;
 import org.sonar.server.issue.RulesAggregation;
+import org.sonar.server.rule.Rule;
+import org.sonar.server.rule.RuleService;
+import org.sonar.server.rule.index.RuleDoc;
+import org.sonar.server.rule.index.RuleQuery;
+import org.sonar.server.search.QueryOptions;
+import org.sonar.server.search.Result;
 import org.sonar.server.ui.ViewProxy;
 import org.sonar.server.ui.Views;
 import org.sonar.server.user.UserSession;
@@ -74,14 +80,16 @@ public class ComponentAppAction implements RequestHandler {
 
   private final IssueService issueService;
   private final Views views;
+  private final RuleService ruleService;
   private final Periods periods;
   private final Durations durations;
   private final I18n i18n;
 
-  public ComponentAppAction(DbClient dbClient, IssueService issueService, Views views, Periods periods, Durations durations, I18n i18n) {
+  public ComponentAppAction(DbClient dbClient, IssueService issueService, Views views, RuleService ruleService, Periods periods, Durations durations, I18n i18n) {
     this.dbClient = dbClient;
     this.issueService = issueService;
     this.views = views;
+    this.ruleService = ruleService;
     this.periods = periods;
     this.durations = durations;
     this.i18n = i18n;
@@ -93,7 +101,7 @@ public class ComponentAppAction implements RequestHandler {
       .setSince("4.4")
       .setInternal(true)
       .setHandler(this)
-      .setResponseExample(Resources.getResource(this.getClass(), "components-app-example-show.json"));
+      .setResponseExample(Resources.getResource(this.getClass(), "components-example-app.json"));
 
     action
       .createParam(PARAM_KEY)
@@ -119,7 +127,7 @@ public class ComponentAppAction implements RequestHandler {
     try {
       ComponentDto component = dbClient.componentDao().getNullableByKey(session, fileKey);
       if (component == null) {
-        throw new NotFoundException(String.format("Component '%s' does not exists.", fileKey));
+        throw new NotFoundException(String.format("Component '%s' does not exist", fileKey));
       }
       userSession.checkComponentPermission(UserRole.USER, fileKey);
 
@@ -138,6 +146,7 @@ public class ComponentAppAction implements RequestHandler {
       appendMeasures(json, measuresByMetricKey, severitiesAggregation, periodIndex);
       appendTabs(json, measuresByMetricKey);
       appendExtensions(json, component, userSession);
+      appendManualRules(json);
     } finally {
       MyBatis.closeQuietly(session);
     }
@@ -148,12 +157,12 @@ public class ComponentAppAction implements RequestHandler {
 
   private void appendComponent(JsonWriter json, ComponentDto component, UserSession userSession, DbSession session) {
     List<PropertyDto> propertyDtos = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
-        .setKey("favourite")
-        .setComponentId(component.getId())
-        .setUserId(userSession.userId())
-        .build(),
+      .setKey("favourite")
+      .setComponentId(component.getId())
+      .setUserId(userSession.userId())
+      .build(),
       session
-    );
+      );
     boolean isFavourite = propertyDtos.size() == 1;
 
     json.prop("key", component.key());
@@ -190,8 +199,10 @@ public class ComponentAppAction implements RequestHandler {
   }
 
   private void appendPermissions(JsonWriter json, ComponentDto component, UserSession userSession) {
-    json.prop("canMarkAsFavourite", userSession.isLoggedIn() && userSession.hasComponentPermission(UserRole.USER, component.key()));
+    boolean hasBrowsePermission = userSession.hasComponentPermission(UserRole.USER, component.key());
+    json.prop("canMarkAsFavourite", userSession.isLoggedIn() && hasBrowsePermission);
     json.prop("canBulkChange", userSession.isLoggedIn());
+    json.prop("canCreateManualIssue", userSession.isLoggedIn() && hasBrowsePermission);
   }
 
   private void appendMeasures(JsonWriter json, Map<String, MeasureDto> measuresByMetricKey, Multiset<String> severitiesAggregation, Integer periodIndex) {
@@ -245,6 +256,21 @@ public class ComponentAppAction implements RequestHandler {
     json.endArray();
   }
 
+  private void appendManualRules(JsonWriter json) {
+    Result<Rule> result = ruleService.search(new RuleQuery().setRepositories(newArrayList(RuleDoc.MANUAL_REPOSITORY)), new QueryOptions().setMaxLimit());
+    if (result != null && !result.getHits().isEmpty()) {
+      List<Rule> manualRules = result.getHits();
+      json.name("manual_rules").beginArray();
+      for (Rule manualRule : manualRules) {
+        json.beginObject()
+          .prop("key", manualRule.key().toString())
+          .prop("name", manualRule.name())
+        .endObject();
+      }
+      json.endArray();
+    }
+  }
+
   private void appendExtensions(JsonWriter json, ComponentDto component, UserSession userSession) {
     List<ViewProxy<Page>> extensionPages = views.getPages(NavigationSection.RESOURCE_TAB, component.scope(), component.qualifier(), component.language(), null);
     Map<String, String> extensions = extensions(extensionPages, component, userSession);
@@ -311,7 +337,6 @@ public class ComponentAppAction implements RequestHandler {
     return measuresByMetricKey;
   }
 
-
   @CheckForNull
   private Date periodDate(@Nullable final Integer periodIndex, List<Period> periodList) {
     if (periodIndex != null) {
diff --git a/sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json b/sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json
deleted file mode 100644 (file)
index 0380387..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-{
-  "key": "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/Plugin.java",
-  "path": "src/main/java/org/sonar/api/Plugin.java",
-  "name": "Plugin.java",
-  "q": "FIL",
-  "subProjectName": "SonarQube :: Plugin API",
-  "projectName": "SonarQube",
-  "fav": true,
-  "canMarkAsFavourite": true,
-  "canBulkChange": false,
-  "periods": [
-    [
-      1,
-      "since previous analysis (2014 May 08)",
-      "2014-05-08T23:40:12+0200"
-    ],
-    [
-      2,
-      "over 365 days (2013 May 17)",
-      "2013-05-17T23:52:45+0200"
-    ],
-    [
-      3,
-      "since previous version (4.3 - 2014 Apr 17)",
-      "2014-04-17T23:34:08+0200"
-    ]
-  ],
-  "severities": [
-    [
-      "INFO",
-      "Info",
-      4
-    ]
-  ],
-  "rules": [
-    [
-      "squid:S1133",
-      "Deprecated code should be removed eventually",
-      4
-    ]
-  ],
-  "measures": {
-    "fNcloc": "12",
-    "fDebt": "4h",
-    "fIssues": "4",
-    "fInfoIssues": "4"
-  },
-  "extensions": [
-    ["metricsTab", "Metrics"]
-  ],
-  "tabs": [
-    "scn", "coverage", "duplications"
-  ]
-}
diff --git a/sonar-server/src/main/resources/org/sonar/server/component/ws/components-example-app.json b/sonar-server/src/main/resources/org/sonar/server/component/ws/components-example-app.json
new file mode 100644 (file)
index 0000000..11e05d3
--- /dev/null
@@ -0,0 +1,58 @@
+{
+  "key": "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/Plugin.java",
+  "path": "src/main/java/org/sonar/api/Plugin.java",
+  "name": "Plugin.java",
+  "q": "FIL",
+  "subProjectName": "SonarQube :: Plugin API",
+  "projectName": "SonarQube",
+  "fav": true,
+  "canMarkAsFavourite": true,
+  "canBulkChange": false,
+  "canCreateManualIssue": false,
+  "periods": [
+    [
+      1,
+      "since previous analysis (2014 May 08)",
+      "2014-05-08T23:40:12+0200"
+    ],
+    [
+      2,
+      "over 365 days (2013 May 17)",
+      "2013-05-17T23:52:45+0200"
+    ],
+    [
+      3,
+      "since previous version (4.3 - 2014 Apr 17)",
+      "2014-04-17T23:34:08+0200"
+    ]
+  ],
+  "severities": [
+    [
+      "INFO",
+      "Info",
+      4
+    ]
+  ],
+  "rules": [
+    [
+      "squid:S1133",
+      "Deprecated code should be removed eventually",
+      4
+    ]
+  ],
+  "measures": {
+    "fNcloc": "12",
+    "fDebt": "4h",
+    "fIssues": "4",
+    "fInfoIssues": "4"
+  },
+  "extensions": [
+    ["metricsTab", "Metrics"]
+  ],
+  "tabs": [
+    "scn", "coverage", "duplications"
+  ],
+  "manual_rules": [
+    {"key": "manual:API", "name": "API"}
+  ]
+}
index 9d1cc0fcecf0fc864b390be58a5d32a7cbb13238..e05ac4ca6b0bc8f125ba8b868dac340d472b574e 100644 (file)
@@ -22,11 +22,15 @@ package org.sonar.server.component.persistence;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
 import org.sonar.core.component.ComponentDto;
 import org.sonar.core.persistence.AbstractDaoTestCase;
 import org.sonar.core.persistence.DbSession;
 
 import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class ComponentDaoTest extends AbstractDaoTestCase {
 
@@ -34,10 +38,13 @@ public class ComponentDaoTest extends AbstractDaoTestCase {
 
   ComponentDao dao;
 
+  System2 system2;
+
   @Before
   public void createDao() throws Exception {
     session = getMyBatis().openSession(false);
-    dao = new ComponentDao();
+    system2 = mock(System2.class);
+    dao = new ComponentDao(system2);
   }
 
   @After
@@ -96,4 +103,49 @@ public class ComponentDaoTest extends AbstractDaoTestCase {
     assertThat(dao.existsById(4L, session)).isTrue();
     assertThat(dao.existsById(111L, session)).isFalse();
   }
+
+  @Test
+  public void insert() {
+    when(system2.now()).thenReturn(DateUtils.parseDate("2014-06-18").getTime());
+    setupData("empty");
+
+    ComponentDto componentDto = new ComponentDto()
+      .setId(1L)
+      .setKey("org.struts:struts-core:src/org/struts/RequestContext.java")
+      .setName("RequestContext.java")
+      .setLongName("org.struts.RequestContext")
+      .setQualifier("FIL")
+      .setScope("FIL")
+      .setLanguage("java")
+      .setPath("src/org/struts/RequestContext.java")
+      .setSubProjectId(3L)
+      .setEnabled(true);
+
+    dao.insert(session, componentDto);
+    session.commit();
+
+    assertThat(componentDto.getId()).isNotNull();
+    checkTables("insert", "projects");
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void update() {
+    dao.update(session, new ComponentDto()
+      .setId(1L)
+      .setKey("org.struts:struts-core:src/org/struts/RequestContext.java")
+      );
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void delete() {
+    dao.delete(session, new ComponentDto()
+      .setId(1L)
+      .setKey("org.struts:struts-core:src/org/struts/RequestContext.java")
+      );
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void synchronize_after() {
+    dao.synchronizeAfter(session, 1L);
+  }
 }
index dafbe40c2901468e2e821b00fe214256995324eb..526cf38ad11b7a4afe0a69fc26f9c0743feec6c9 100644 (file)
@@ -32,6 +32,7 @@ import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 import org.sonar.api.i18n.I18n;
 import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.rule.RuleKey;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.Duration;
 import org.sonar.api.utils.Durations;
@@ -54,6 +55,11 @@ import org.sonar.server.db.DbClient;
 import org.sonar.server.issue.IssueService;
 import org.sonar.server.issue.RulesAggregation;
 import org.sonar.server.measure.persistence.MeasureDao;
+import org.sonar.server.rule.Rule;
+import org.sonar.server.rule.RuleService;
+import org.sonar.server.rule.index.RuleQuery;
+import org.sonar.server.search.QueryOptions;
+import org.sonar.server.search.Result;
 import org.sonar.server.ui.ViewProxy;
 import org.sonar.server.ui.Views;
 import org.sonar.server.user.MockUserSession;
@@ -99,6 +105,9 @@ public class ComponentAppActionTest {
   @Mock
   Views views;
 
+  @Mock
+  RuleService ruleService;
+
   @Mock
   Periods periods;
 
@@ -128,7 +137,7 @@ public class ComponentAppActionTest {
     when(issueService.findRulesByComponent(anyString(), any(Date.class), eq(session))).thenReturn(mock(RulesAggregation.class));
     when(measureDao.findByComponentKeyAndMetricKeys(anyString(), anyListOf(String.class), eq(session))).thenReturn(measures);
 
-    tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, issueService, views, periods, durations, i18n)));
+    tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, issueService, views, ruleService, periods, durations, i18n)));
   }
 
   @Test
@@ -409,6 +418,22 @@ public class ComponentAppActionTest {
     request.execute().assertJson(getClass(), "app_with_extension_having_permission.json");
   }
 
+  @Test
+  public void app_with_manual_rules() throws Exception {
+    MockUserSession.set().addComponentPermission(UserRole.USER, PROJECT_KEY, COMPONENT_KEY);
+    addComponent();
+
+    Result<Rule> result = mock(Result.class);
+    Rule rule = mock(Rule.class);
+    when(rule.key()).thenReturn(RuleKey.of("manual", "API"));
+    when(rule.name()).thenReturn("API");
+    when(result.getHits()).thenReturn(newArrayList(rule));
+    when(ruleService.search(any(RuleQuery.class), any(QueryOptions.class))).thenReturn(result);
+
+    WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY);
+    request.execute().assertJson(getClass(), "app_with_manual_rules.json");
+  }
+
   private void addComponent() {
     ComponentDto file = new ComponentDto().setId(10L).setQualifier("FIL").setKey(COMPONENT_KEY).setName("Plugin.java")
       .setPath("src/main/java/org/sonar/api/Plugin.java").setSubProjectId(5L).setProjectId(1L);
index 3f7580987dbb99c28d6f91aed8213d8017139260..c56203962aab5895909856a1f6810b0948e891cd 100644 (file)
@@ -29,6 +29,7 @@ import org.sonar.api.utils.Durations;
 import org.sonar.core.timemachine.Periods;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.issue.IssueService;
+import org.sonar.server.rule.RuleService;
 import org.sonar.server.ui.Views;
 import org.sonar.server.ws.WsTester;
 
@@ -42,7 +43,7 @@ public class ComponentsWsTest {
   @Before
   public void setUp() throws Exception {
     WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(IssueService.class), mock(Views.class),
-      mock(Periods.class), mock(Durations.class), mock(I18n.class))));
+      mock(RuleService.class), mock(Periods.class), mock(Durations.class), mock(I18n.class))));
     controller = tester.controller("api/components");
   }
 
diff --git a/sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/empty.xml b/sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/empty.xml
new file mode 100644 (file)
index 0000000..871dedc
--- /dev/null
@@ -0,0 +1,3 @@
+<dataset>
+
+</dataset>
diff --git a/sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/insert-result.xml b/sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/insert-result.xml
new file mode 100644 (file)
index 0000000..73f3732
--- /dev/null
@@ -0,0 +1,9 @@
+<dataset>
+
+  <projects id="1" kee="org.struts:struts-core:src/org/struts/RequestContext.java" name="RequestContext.java" long_name="org.struts.RequestContext"
+      qualifier="FIL" scope="FIL" language="java" path="src/org/struts/RequestContext.java" root_id="3"
+      description="[null]" enabled="[true]" copy_resource_id="[null]" person_id="[null]" deprecated_kee="[null]"
+      created_at="2014-06-18"
+      />
+
+</dataset>
index 9d71aad91ca2df628a42689ed1399597302e5cd4..30715cd97d28a16d656959e5f137cbdadf8ab7a6 100644 (file)
@@ -8,6 +8,7 @@
   "fav": true,
   "canMarkAsFavourite": true,
   "canBulkChange": true,
+  "canCreateManualIssue": true,
   "periods": [],
   "severities": [],
   "rules": [],
index 614c658f4d938ce137573aad75a65b895a07910b..015c2e99a4b89e3c494e2bd4eb0052182056e03f 100644 (file)
@@ -8,6 +8,7 @@
   "fav": false,
   "canMarkAsFavourite": false,
   "canBulkChange": false,
+  "canCreateManualIssue": false,
   "periods": [],
   "severities": [],
   "rules": [],
index 4ff6b556c6762ea0becc50e2284ccec55144c36c..5d645e3c4c69b427cf56d3d09737f4919a660117 100644 (file)
@@ -8,6 +8,7 @@
   "fav": false,
   "canMarkAsFavourite": false,
   "canBulkChange": false,
+  "canCreateManualIssue": false,
   "periods": [],
   "severities": [
     ["BLOCKER", "Blocker", 1],
index 82200db87a8c4d002fad5bd8eb3dac06da89f1b1..31da0b9b1dcb973029e69604767cc564237db680 100644 (file)
@@ -8,6 +8,7 @@
   "fav": false,
   "canMarkAsFavourite": false,
   "canBulkChange": false,
+  "canCreateManualIssue": false,
   "periods": [
     [1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"]
   ],
diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_manual_rules.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_manual_rules.json
new file mode 100644 (file)
index 0000000..60a000a
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "key": "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/Plugin.java",
+  "path": "src/main/java/org/sonar/api/Plugin.java",
+  "name": "Plugin.java",
+  "q": "FIL",
+  "subProjectName": "SonarQube :: Plugin API",
+  "projectName": "SonarQube",
+  "fav": false,
+  "canMarkAsFavourite": false,
+  "canBulkChange": false,
+  "canCreateManualIssue": false,
+  "periods": [],
+  "severities": [],
+  "rules": [],
+  "measures": {},
+  "manual_rules": [
+    {"key": "manual:API", "name": "API"}
+  ]
+}
index a3d7bf45ae2a5dcf0954294b1e803aae1580b124..efa46c28829dc495650bc959bb474aad181d9da6 100644 (file)
@@ -8,6 +8,7 @@
   "fav": false,
   "canMarkAsFavourite": false,
   "canBulkChange": false,
+  "canCreateManualIssue": false,
   "periods": [],
   "severities": [],
   "rules": [],
index 671e609f2669220f4efe04cf55d356b9fcf5dce3..cc34de25f9d0c0db6bd68a91c4c3aa8f1218b516 100644 (file)
@@ -8,6 +8,7 @@
   "fav": false,
   "canMarkAsFavourite": false,
   "canBulkChange": false,
+  "canCreateManualIssue": false,
   "periods": [
     [1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"]
   ],
index 646cc2464e3dc78f80b1f60ffbbd92383a7ff5a1..ce54c1e87ce195195154ae3b19357f63bccf33f0 100644 (file)
@@ -8,6 +8,7 @@
   "fav": false,
   "canMarkAsFavourite": false,
   "canBulkChange": false,
+  "canCreateManualIssue": false,
   "periods": [
     [1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"]
   ],
index 78afaae54041d242c8a3f29ab8d95656049b3e3d..d9172f38383b0a312cbcc96da1304126df035f64 100644 (file)
@@ -8,6 +8,7 @@
   "fav": false,
   "canMarkAsFavourite": false,
   "canBulkChange": false,
+  "canCreateManualIssue": false,
   "periods": [],
   "severities": [],
   "rules": [
index a2531e439e4155aa4a4dd43801f1bbbc78aa9c54..ad8444c8ef24aaf0baaa552f1377a0d6c7b6b534 100644 (file)
@@ -8,6 +8,7 @@
   "fav": false,
   "canMarkAsFavourite": false,
   "canBulkChange": false,
+  "canCreateManualIssue": false,
   "periods": [
     [1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"]
   ],
index 1dcbac620c7f90cfb93c6002167270dbf2d0cb76..807a16712579aa5e7514406f0f6d57d5553558d9 100644 (file)
@@ -8,6 +8,7 @@
   "fav": false,
   "canMarkAsFavourite": false,
   "canBulkChange": false,
+  "canCreateManualIssue": false,
   "periods": [],
   "severities": [
     ["MAJOR", "Major", 5]
index 2c8c24cd82e479f4b73faeb13922ad9c2dba76ab..53401307052681d5e3da5df0b91389ee32b3e5de 100644 (file)
@@ -8,6 +8,7 @@
   "fav": false,
   "canMarkAsFavourite": false,
   "canBulkChange": false,
+  "canCreateManualIssue": false,
   "periods": [
     [1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"]
   ],
index 8b5d5998ff416be0d847f50966302414673aeca4..9b3f2515f80a1ef6498fe46c075fa342c5e562a7 100644 (file)
@@ -8,6 +8,7 @@
   "fav": false,
   "canMarkAsFavourite": false,
   "canBulkChange": false,
+  "canCreateManualIssue": false,
   "periods": [],
   "severities": [],
   "rules": [],
index 45bf9e20115523650327ebdf708093f4b3963d52..385d2ba2321532c8aa134f5464205e2e1c76b5c7 100644 (file)
@@ -8,6 +8,7 @@
   "fav": false,
   "canMarkAsFavourite": false,
   "canBulkChange": false,
+  "canCreateManualIssue": false,
   "periods": [],
   "severities": [],
   "rules": [],
index 9d6f9806489f9d40705ad71f9b112597027e13b5..709353d023b14c7c6138a6b3c40f87cc09b0b368 100644 (file)
@@ -6,6 +6,7 @@
   "fav": true,
   "canMarkAsFavourite": true,
   "canBulkChange": true,
+  "canCreateManualIssue": true,
   "periods": [],
   "severities": [],
   "rules": [],