From ac68715f45043ec0461e074569cbb902fed8031e Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Fri, 28 Mar 2014 16:04:32 +0100 Subject: [PATCH] SONAR-5174 Rules WS : search by languages, severities, statuses, tags and characteristics --- .../java/org/sonar/server/rule/RuleQuery.java | 70 ++++++++++---- .../org/sonar/server/rule/RuleRegistry.java | 42 ++++++--- .../server/rule/ws/RuleSearchWsHandler.java | 23 ++++- .../org/sonar/server/rule/ws/RulesWs.java | 1 + .../java/org/sonar/server/util/RubyUtils.java | 1 + .../sonar/server/rule/RuleRegistryTest.java | 94 +++++++++++-------- .../rule/ws/RuleSearchWsHandlerTest.java | 52 ++++------ .../rule/RuleRegistryTest/shared/rule1.json | 11 ++- .../rule/RuleRegistryTest/shared/rule2.json | 2 +- .../RuleSearchWsHandlerTest/search_rules.json | 17 ++++ 10 files changed, 203 insertions(+), 110 deletions(-) create mode 100644 sonar-server/src/test/resources/org/sonar/server/rule/ws/RuleSearchWsHandlerTest/search_rules.json diff --git a/sonar-server/src/main/java/org/sonar/server/rule/RuleQuery.java b/sonar-server/src/main/java/org/sonar/server/rule/RuleQuery.java index c6fa805af38..30796ea2850 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/RuleQuery.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/RuleQuery.java @@ -32,14 +32,17 @@ import java.util.Collections; */ public class RuleQuery { - public static final int DEFAULT_PAGE_INDEX = 1; + private static final int DEFAULT_PAGE_INDEX = 1; private static final int DEFAULT_PAGE_SIZE = 25; private String key; private String query; - private String characteristic; - private String language; + private Collection languages; private Collection repositories; + private Collection severities; + private Collection statuses; + private Collection tags; + private Collection debtCharacteristics; private int pageSize; private int pageIndex; @@ -47,9 +50,12 @@ public class RuleQuery { private RuleQuery(Builder builder) { this.key = builder.key; this.query = builder.query; - this.language = builder.language; + this.languages = defaultCollection(builder.languages); this.repositories = defaultCollection(builder.repositories); - this.characteristic = builder.characteristic; + this.severities = defaultCollection(builder.severities); + this.statuses = defaultCollection(builder.statuses); + this.tags = defaultCollection(builder.tags); + this.debtCharacteristics = defaultCollection(builder.debtCharacteristics); this.pageSize = builder.pageSize; this.pageIndex = builder.pageIndex; } @@ -64,18 +70,28 @@ public class RuleQuery { return query; } - @CheckForNull - public String language() { - return language; + public Collection languages() { + return languages; } public Collection repositories() { return repositories; } - @CheckForNull - public String characteristic() { - return characteristic; + public Collection severities() { + return severities; + } + + public Collection statuses() { + return statuses; + } + + public Collection tags() { + return tags; + } + + public Collection debtCharacteristics() { + return debtCharacteristics; } public int pageSize() { @@ -94,9 +110,12 @@ public class RuleQuery { private String key; private String query; - private String characteristic; - private String language; + private Collection languages; private Collection repositories; + private Collection severities; + private Collection statuses; + private Collection tags; + private Collection debtCharacteristics; private Integer pageSize; private Integer pageIndex; @@ -111,18 +130,33 @@ public class RuleQuery { return this; } - public Builder language(@Nullable String language) { - this.language = language; + public Builder languages(@Nullable Collection languages) { + this.languages = languages; return this; } - public Builder repositories(Collection repositories) { + public Builder repositories(@Nullable Collection repositories) { this.repositories = repositories; return this; } - public Builder characteristic(@Nullable String characteristic) { - this.characteristic = characteristic; + public Builder severities(@Nullable Collection severities) { + this.severities = severities; + return this; + } + + public Builder statuses(@Nullable Collection statuses) { + this.statuses = statuses; + return this; + } + + public Builder tags(@Nullable Collection tags) { + this.tags = tags; + return this; + } + + public Builder debtCharacteristics(@Nullable Collection debtCharacteristics) { + this.debtCharacteristics = debtCharacteristics; return this; } diff --git a/sonar-server/src/main/java/org/sonar/server/rule/RuleRegistry.java b/sonar-server/src/main/java/org/sonar/server/rule/RuleRegistry.java index ea653871e66..4c9a0e570c8 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/RuleRegistry.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/RuleRegistry.java @@ -31,6 +31,7 @@ import org.elasticsearch.common.io.BytesStream; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.query.BoolFilterBuilder; +import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.FilterBuilders; import org.elasticsearch.index.query.MatchQueryBuilder.Operator; import org.elasticsearch.index.query.QueryBuilders; @@ -137,21 +138,21 @@ public class RuleRegistry { mainFilter.must(FilterBuilders.queryFilter( QueryBuilders.multiMatchQuery(query.query(), RuleDocument.FIELD_NAME + ".search", RuleDocument.FIELD_KEY).operator(Operator.AND))); } - if (query.characteristic() != null) { + addMustTermOrTerms(mainFilter, RuleDocument.FIELD_LANGUAGE, query.languages()); + addMustTermOrTerms(mainFilter, RuleDocument.FIELD_REPOSITORY_KEY, query.repositories()); + addMustTermOrTerms(mainFilter, RuleDocument.FIELD_SEVERITY, query.severities()); + addMustTermOrTerms(mainFilter, RuleDocument.FIELD_STATUS, query.statuses()); + if (!query.debtCharacteristics().isEmpty()) { mainFilter.must(FilterBuilders.queryFilter( - QueryBuilders.multiMatchQuery(query.characteristic(), RuleDocument.FIELD_CHARACTERISTIC_KEY, RuleDocument.FIELD_SUB_CHARACTERISTIC_KEY).operator(Operator.OR)) + QueryBuilders.multiMatchQuery(query.debtCharacteristics(), RuleDocument.FIELD_CHARACTERISTIC_KEY, RuleDocument.FIELD_SUB_CHARACTERISTIC_KEY).operator(Operator.OR)) ); } - if (query.language() != null) { - mainFilter.must(termFilter(RuleDocument.FIELD_LANGUAGE, query.language())); - } - if (!query.repositories().isEmpty()) { - if (query.repositories().size() == 1) { - mainFilter.must(termFilter(RuleDocument.FIELD_REPOSITORY_KEY, query.repositories().iterator().next())); - } else { - mainFilter.must(termsFilter(RuleDocument.FIELD_REPOSITORY_KEY, query.repositories().toArray())); - } + if (!query.tags().isEmpty()) { + mainFilter.must(FilterBuilders.queryFilter( + QueryBuilders.multiMatchQuery(query.tags(), RuleDocument.FIELD_ADMIN_TAGS, RuleDocument.FIELD_SYSTEM_TAGS).operator(Operator.OR)) + ); } + Paging paging = Paging.create(query.pageSize(), query.pageIndex()); SearchHits hits = searchIndex.executeRequest( searchIndex.client().prepareSearch(INDEX_RULES).setTypes(TYPE_RULE) @@ -168,6 +169,25 @@ public class RuleRegistry { return new PagedResult(rulesBuilder.build(), PagingResult.create(paging.pageSize(), paging.pageIndex(), hits.getTotalHits())); } + private static void addMustTermOrTerms(BoolFilterBuilder filter, String field, Collection terms) { + FilterBuilder termOrTerms = getTermOrTerms(field, terms); + if (termOrTerms != null) { + filter.must(termOrTerms); + } + } + + private static FilterBuilder getTermOrTerms(String field, Collection terms) { + if (terms.isEmpty()) { + return null; + } else { + if (terms.size() == 1) { + return termFilter(field, terms.iterator().next()); + } else { + return termsFilter(field, terms.toArray()); + } + } + } + /** * Create or update definition of rule identified by ruleId */ diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleSearchWsHandler.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleSearchWsHandler.java index 0fc33b2b4a3..17bb13c4951 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleSearchWsHandler.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleSearchWsHandler.java @@ -23,6 +23,7 @@ package org.sonar.server.rule.ws; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.RequestHandler; import org.sonar.api.server.ws.Response; @@ -31,6 +32,7 @@ import org.sonar.server.paging.PagedResult; import org.sonar.server.rule.Rule; import org.sonar.server.rule.RuleQuery; import org.sonar.server.rule.Rules; +import org.sonar.server.util.RubyUtils; import javax.annotation.CheckForNull; @@ -56,8 +58,12 @@ public class RuleSearchWsHandler implements RequestHandler { if (ruleKeyParam == null) { PagedResult searchResult = rules.find(RuleQuery.builder() .searchQuery(request.param("s")) - .language(request.param("language")) - .characteristic(request.param("characteristic")) + .languages(RubyUtils.toStrings(request.param("languages"))) + .repositories(RubyUtils.toStrings(request.param("repositories"))) + .severities(RubyUtils.toStrings(request.param("severities"))) + .statuses(RubyUtils.toStrings(request.param("statuses"))) + .tags(RubyUtils.toStrings(request.param("tags"))) + .debtCharacteristics(RubyUtils.toStrings(request.param("debtCharacteristics"))) .pageSize(request.paramAsInt("ps")) .pageIndex(request.paramAsInt("p")) .build()); @@ -104,6 +110,19 @@ public class RuleSearchWsHandler implements RequestHandler { .prop("key", rule.ruleKey().toString()) .prop("name", rule.name()) .prop("language", languageName) + .prop("status", rule.status()) ; + DebtRemediationFunction function = rule.debtRemediationFunction(); + if (function != null) { + json + .prop("debtCharacteristic", rule.debtCharacteristicKey()) + .prop("debtCharacteristicName", rule.debtCharacteristicName()) + .prop("debtSubCharacteristic", rule.debtSubCharacteristicKey()) + .prop("debtSubCharacteristicName", rule.debtSubCharacteristicName()) + .prop("debtRemediationFunction", function.type().name()) + .prop("debtRemediationCoefficient", function.coefficient()) + .prop("debtRemediationOffset", function.offset()) + ; + } } } diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWs.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWs.java index 1fba1f5f6ce..294f2b7d141 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWs.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWs.java @@ -46,6 +46,7 @@ public class RulesWs implements WebService { .setHandler(searchHandler) .createParam("s", "An optional query that will be matched against rule titles.") .createParam("k", "An optional query that will be matched exactly agains rule keys.") + // TODO add description for languages, repositories, etc. params .createParam("ps", "Optional page size (default is 25).") .createParam("p", "Optional page number (default is 0)."); diff --git a/sonar-server/src/main/java/org/sonar/server/util/RubyUtils.java b/sonar-server/src/main/java/org/sonar/server/util/RubyUtils.java index 5004f600ac6..db33e56e8ea 100644 --- a/sonar-server/src/main/java/org/sonar/server/util/RubyUtils.java +++ b/sonar-server/src/main/java/org/sonar/server/util/RubyUtils.java @@ -40,6 +40,7 @@ public class RubyUtils { // only static methods } + @CheckForNull public static List toStrings(@Nullable Object o) { List result = null; if (o != null) { diff --git a/sonar-server/src/test/java/org/sonar/server/rule/RuleRegistryTest.java b/sonar-server/src/test/java/org/sonar/server/rule/RuleRegistryTest.java index b54a81f151c..eb57b4edef2 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/RuleRegistryTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/RuleRegistryTest.java @@ -226,8 +226,7 @@ public class RuleRegistryTest { registry.bulkRegisterRules(rules, Maps.newHashMap(), params, tags); assertThat(registry.findIds(ImmutableMap.of("repositoryKey", "repo"))).hasSize(2); - Map rule2Document = esSetup.client().prepareGet("rules", "rule", Integer.toString(ruleId2)) - .execute().actionGet().getSourceAsMap(); + Map rule2Document = esSetup.client().prepareGet("rules", "rule", Integer.toString(ruleId2)).execute().actionGet().getSourceAsMap(); assertThat((List) rule2Document.get(RuleDocument.FIELD_SYSTEM_TAGS)).hasSize(2); assertThat((List) rule2Document.get(RuleDocument.FIELD_ADMIN_TAGS)).hasSize(1); } @@ -277,34 +276,6 @@ public class RuleRegistryTest { assertThat(esSetup.exists("rules", "rule", "3")).isFalse(); } - @Test - public void find_rules_by_name() { - // Removed rule should not appear - assertThat(registry.find(RuleQuery.builder().build()).results()).hasSize(2); - - // Search is case insensitive - assertThat(registry.find(RuleQuery.builder().searchQuery("one issue per line").build()).results()).hasSize(1); - - // Search is ngram based - assertThat(registry.find(RuleQuery.builder().searchQuery("with param").build()).results()).hasSize(1); - - // Search works also with key - assertThat(registry.find(RuleQuery.builder().searchQuery("OneIssuePerLine").build()).results()).hasSize(1); - } - - @Test - public void find_rules_by_language() { - assertThat(registry.find(RuleQuery.builder().language("xoo").build()).results()).hasSize(2); - assertThat(registry.find(RuleQuery.builder().language("unknown").build()).results()).isEmpty(); - } - - @Test - public void find_rules_by_rule_repositories() { - assertThat(registry.find(RuleQuery.builder().repositories(newArrayList("xoo")).build()).results()).hasSize(1); - assertThat(registry.find(RuleQuery.builder().repositories(newArrayList("xoo", "xoo2")).build()).results()).hasSize(2); - assertThat(registry.find(RuleQuery.builder().repositories(newArrayList("unknown")).build()).results()).isEmpty(); - } - @Test public void index_debt_definitions() { Map characteristics = newHashMap(); @@ -356,20 +327,61 @@ public class RuleRegistryTest { } @Test - public void find_rules_by_characteristic_or_sub_characteristic() { - Map characteristics = newHashMap(); - characteristics.put(10, new CharacteristicDto().setId(10).setKey("REUSABILITY").setName("Reusability")); - characteristics.put(11, new CharacteristicDto().setId(11).setKey("MODULARITY").setName("Modularity").setParentId(10)); + public void find_rules_by_name() { + // Removed rule should not appear + assertThat(registry.find(RuleQuery.builder().searchQuery("Removed rule").build()).results()).isEmpty(); - List rules = ImmutableList.of(new RuleDto().setId(10).setRepositoryKey("repo").setRuleKey("key1").setSeverity(Severity.MINOR) - .setDefaultSubCharacteristicId(11).setDefaultRemediationFunction("LINEAR").setDefaultRemediationCoefficient("2h")); + // Search is case insensitive + assertThat(registry.find(RuleQuery.builder().searchQuery("one issue per line").build()).results()).hasSize(1); + + // Search is ngram based + assertThat(registry.find(RuleQuery.builder().searchQuery("with param").build()).results()).hasSize(1); + + // Search works also with key + assertThat(registry.find(RuleQuery.builder().searchQuery("OneIssuePerLine").build()).results()).hasSize(1); + } + + @Test + public void find_rules_by_languages() { + assertThat(registry.find(RuleQuery.builder().languages(newArrayList("xoo")).build()).results()).hasSize(2); + assertThat(registry.find(RuleQuery.builder().languages(newArrayList("unknown")).build()).results()).isEmpty(); + } + + @Test + public void find_rules_by_repositories() { + assertThat(registry.find(RuleQuery.builder().repositories(newArrayList("xoo")).build()).results()).hasSize(1); + assertThat(registry.find(RuleQuery.builder().repositories(newArrayList("xoo", "xoo2")).build()).results()).hasSize(2); + assertThat(registry.find(RuleQuery.builder().repositories(newArrayList("unknown")).build()).results()).isEmpty(); + } + + @Test + public void find_rules_by_severities() { + assertThat(registry.find(RuleQuery.builder().severities(newArrayList("MAJOR")).build()).results()).hasSize(1); + assertThat(registry.find(RuleQuery.builder().severities(newArrayList("MAJOR", "MINOR")).build()).results()).hasSize(2); + assertThat(registry.find(RuleQuery.builder().severities(newArrayList("unknown")).build()).results()).isEmpty(); + } - registry.bulkRegisterRules(rules, characteristics, ArrayListMultimap.create(), - ArrayListMultimap.create()); + @Test + public void find_rules_by_statuses() { + assertThat(registry.find(RuleQuery.builder().statuses(newArrayList("READY")).build()).results()).hasSize(1); + assertThat(registry.find(RuleQuery.builder().statuses(newArrayList("READY", "BETA")).build()).results()).hasSize(2); + assertThat(registry.find(RuleQuery.builder().statuses(newArrayList("unknown")).build()).results()).isEmpty(); + } - assertThat(registry.find(RuleQuery.builder().characteristic("MODULARITY").build()).results()).hasSize(1); - assertThat(registry.find(RuleQuery.builder().characteristic("REUSABILITY").build()).results()).hasSize(1); - assertThat(registry.find(RuleQuery.builder().characteristic("Unknown").build()).results()).isEmpty(); + @Test + public void find_rules_by_tags() { + assertThat(registry.find(RuleQuery.builder().tags(newArrayList("has-params")).build()).results()).hasSize(1); + assertThat(registry.find(RuleQuery.builder().tags(newArrayList("keep-enabled")).build()).results()).hasSize(1); + assertThat(registry.find(RuleQuery.builder().tags(newArrayList("has-params", "keep-enabled")).build()).results()).hasSize(1); + assertThat(registry.find(RuleQuery.builder().tags(newArrayList("unknown")).build()).results()).isEmpty(); + } + + @Test + public void find_rules_by_characteristics() { + assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("MODULARITY")).build()).results()).hasSize(1); + assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("REUSABILITY")).build()).results()).hasSize(1); + assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("MODULARITY", "REUSABILITY")).build()).results()).hasSize(1); + assertThat(registry.find(RuleQuery.builder().debtCharacteristics(newArrayList("unknown")).build()).results()).isEmpty(); } private String testFileAsString(String testFile) throws Exception { diff --git a/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleSearchWsHandlerTest.java b/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleSearchWsHandlerTest.java index 1782b12bed7..46050c53bc9 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleSearchWsHandlerTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleSearchWsHandlerTest.java @@ -59,10 +59,11 @@ public class RuleSearchWsHandlerTest { .setName("Avoid cycle") .setDescription("Avoid cycle between packages") .setLanguage("java") + .setStatus("READY") .setDebtCharacteristicKey("REUSABILITY") .setDebtCharacteristicName("Reusability") - .setDebtCharacteristicKey("MODULARITY") - .setDebtCharacteristicKey("Modularity") + .setDebtSubCharacteristicKey("MODULARITY") + .setDebtSubCharacteristicName("Modularity") .setDebtRemediationFunction(new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET, "1h", "15min")); WsTester tester; @@ -86,9 +87,7 @@ public class RuleSearchWsHandlerTest { MockUserSession.set(); WsTester.TestRequest request = tester.newRequest("list").setParam("ps", "10").setParam("p", "2"); - request.execute().assertJson("{'more':false,'total':1,'results':[" - + "{'key':'squid:AvoidCycle','name':'Avoid cycle','language':'Java'}" - + "]}"); + request.execute().assertJson(getClass(), "search_rules.json"); } @Test @@ -103,46 +102,35 @@ public class RuleSearchWsHandlerTest { MockUserSession.set(); WsTester.TestRequest request = tester.newRequest("list").setParam("k", ruleKey); - request.execute().assertJson("{'more':false,'total':1,'results':[" - + "{'key':'squid:AvoidCycle','name':'Avoid cycle','language':'Java'}" - + "]}"); + request.execute().assertJson(getClass(), "search_rules.json"); } @Test - public void search_rule_by_language() throws Exception { + public void verify_rule_query_filters() throws Exception { Rule rule = ruleBuilder.build(); when(rules.find(any(RuleQuery.class))).thenReturn( new PagedResult(ImmutableList.of(rule), PagingResult.create(10, 1, 1))); MockUserSession.set(); - WsTester.TestRequest request = tester.newRequest("list").setParam("language", "java"); - request.execute().assertJson("{'more':false,'total':1,'results':[" - + "{'key':'squid:AvoidCycle','name':'Avoid cycle','language':'java'}" - + "]}"); + tester.newRequest("list") + .setParam("languages", "java,js") + .setParam("repositories", "squid,pmd") + .setParam("severities", "MAJOR,MINOR") + .setParam("statuses", "READY,BETA") + .setParam("tags", "has-params,integration-tests") + .setParam("debtCharacteristics", "MODULARITY,REUSABILITY") + .execute(); ArgumentCaptor ruleQueryCaptor = ArgumentCaptor.forClass(RuleQuery.class); verify(rules).find(ruleQueryCaptor.capture()); - assertThat(ruleQueryCaptor.getValue().language()).isEqualTo("java"); + assertThat(ruleQueryCaptor.getValue().languages()).containsOnly("java", "js"); + assertThat(ruleQueryCaptor.getValue().repositories()).containsOnly("squid", "pmd"); + assertThat(ruleQueryCaptor.getValue().severities()).containsOnly("MAJOR", "MINOR"); + assertThat(ruleQueryCaptor.getValue().statuses()).containsOnly("READY", "BETA"); + assertThat(ruleQueryCaptor.getValue().tags()).containsOnly("has-params", "integration-tests"); + assertThat(ruleQueryCaptor.getValue().debtCharacteristics()).containsOnly("MODULARITY", "REUSABILITY"); } - @Test - public void search_rule_by_characteristic() throws Exception { - Rule rule = ruleBuilder.build(); - - when(rules.find(any(RuleQuery.class))).thenReturn( - new PagedResult(ImmutableList.of(rule), PagingResult.create(10, 1, 1))); - - MockUserSession.set(); - WsTester.TestRequest request = tester.newRequest("list").setParam("characteristic", "MODULARITY"); - request.execute().assertJson("{'more':false,'total':1,'results':[" - + "{'key':'squid:AvoidCycle','name':'Avoid cycle','language':'java'}" - + "]}"); - - ArgumentCaptor ruleQueryCaptor = ArgumentCaptor.forClass(RuleQuery.class); - verify(rules).find(ruleQueryCaptor.capture()); - - assertThat(ruleQueryCaptor.getValue().characteristic()).isEqualTo("MODULARITY"); - } } diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistryTest/shared/rule1.json b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistryTest/shared/rule1.json index f7bc51f168b..134ddd71a08 100644 --- a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistryTest/shared/rule1.json +++ b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistryTest/shared/rule1.json @@ -50,12 +50,13 @@ "description": "" } ], - "defaultCharacteristicId": 1, - "characteristicId": 2, - "defaultRemediationFunction": "LINEAR", + "characteristicId": 1, + "characteristicKey": "REUSABILITY", + "characteristicName": "Reusability", + "subCharacteristicId": 2, + "subCharacteristicKey": "MODULARITY", + "subCharacteristicName": "Modularity", "remediationFunction": "LINEAR_OFFSET", - "defaultRemediationCoefficient": "2h", "remediationCoefficient": "1h", - "defaultRemediationOffset": null, "remediationOffset": "15min" } diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistryTest/shared/rule2.json b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistryTest/shared/rule2.json index 3879ecccdfc..a29bfa5fde3 100644 --- a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistryTest/shared/rule2.json +++ b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistryTest/shared/rule2.json @@ -6,7 +6,7 @@ "description": "Generate an issue on each line of a file. It requires the metric \"lines\".", "parentKey": null, "repositoryKey": "xoo2", - "severity": "MAJOR", + "severity": "MINOR", "status": "BETA", "createdAt": "2013-10-28T13:07:26.339Z", "updatedAt": "2013-11-08T10:52:53.487Z" diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/ws/RuleSearchWsHandlerTest/search_rules.json b/sonar-server/src/test/resources/org/sonar/server/rule/ws/RuleSearchWsHandlerTest/search_rules.json new file mode 100644 index 00000000000..6a29764422f --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/rule/ws/RuleSearchWsHandlerTest/search_rules.json @@ -0,0 +1,17 @@ +{"more": false, "total": 1, + "results": [ + { + "key": "squid:AvoidCycle", + "name": "Avoid cycle", + "language": "Java", + "status": "READY", + "debtCharacteristic": "REUSABILITY", + "debtCharacteristicName": "Reusability", + "debtSubCharacteristic": "MODULARITY", + "debtSubCharacteristicName": "Modularity", + "debtRemediationFunction": "LINEAR_OFFSET", + "debtRemediationCoefficient": "1h", + "debtRemediationOffset": "15min" + } + ] +} -- 2.39.5