From 0e3a5a4e118e66bf4d2d17a1a745859c4351225a Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Thu, 12 Apr 2018 15:35:53 +0200 Subject: [PATCH] SONAR-10575 Index and search external rules --- .../container/ComputeEngineContainerImpl.java | 4 +++ .../ComputeEngineContainerImplTest.java | 2 +- .../main/java/org/sonar/db/rule/RuleDto.java | 5 ++++ .../org/sonar/db/rule/RuleForIndexingDto.java | 5 ++++ .../org/sonar/db/rule/RuleMapper.xml | 1 + .../java/org/sonar/db/rule/RuleDaoTest.java | 12 ++++++-- .../java/org/sonar/db/rule/RuleTesting.java | 4 +++ ...ProjectAnalysisTaskContainerPopulator.java | 2 -- .../issue/RuleRepositoryImpl.java | 2 +- .../server/rule/ExternalRuleCreator.java | 23 ++++++++++---- .../org/sonar/server/rule/RegisterRules.java | 3 +- .../org/sonar/server/rule/index/RuleDoc.java | 10 +++++++ .../sonar/server/rule/index/RuleIndex.java | 20 ++++++++----- .../rule/index/RuleIndexDefinition.java | 2 ++ .../sonar/server/rule/index/RuleQuery.java | 11 +++++++ .../org/sonar/server/rule/ws/RuleMapper.java | 8 +++++ .../server/rule/ws/RuleQueryFactory.java | 2 ++ .../server/rule/ws/RulesWsParameters.java | 6 ++-- .../sonar/server/rule/ws/SearchAction.java | 11 ++++++- .../sonar/server/rule/ws/example-show.json | 1 + .../sonar/server/rule/ws/search-example.json | 5 ++++ .../issue/RuleRepositoryImplTest.java | 25 ++++++---------- .../step/PersistExternalRulesStepTest.java | 30 ++++++++++++++++--- .../step/PersistIssuesStepTest.java | 2 +- .../server/rule/ExternalRuleCreatorTest.java | 14 ++++++--- .../server/rule/index/RuleIndexTest.java | 30 ++++++++++++++++++- .../server/rule/ws/RuleQueryFactoryTest.java | 10 +++++-- sonar-ws/src/main/protobuf/ws-rules.proto | 1 + 28 files changed, 199 insertions(+), 52 deletions(-) diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java index 58c0c97ec50..f062fee4298 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java @@ -146,8 +146,10 @@ import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; import org.sonar.server.rule.CommonRuleDefinitionsImpl; import org.sonar.server.rule.DefaultRuleFinder; import org.sonar.server.rule.DeprecatedRulesDefinitionLoader; +import org.sonar.server.rule.ExternalRuleCreator; import org.sonar.server.rule.RuleDefinitionsLoader; import org.sonar.server.rule.index.RuleIndex; +import org.sonar.server.rule.index.RuleIndexer; import org.sonar.server.setting.DatabaseSettingLoader; import org.sonar.server.setting.DatabaseSettingsEnabler; import org.sonar.server.setting.ThreadLocalSettings; @@ -343,6 +345,8 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer { CommonRuleDefinitionsImpl.class, RuleDefinitionsLoader.class, RulesDefinitionXmlLoader.class, + ExternalRuleCreator.class, + RuleIndexer.class, // languages Languages.class, // used by CommonRuleDefinitionsImpl diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index 158f9ebe545..af333c9683d 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -93,7 +93,7 @@ public class ComputeEngineContainerImplTest { assertThat(picoContainer.getComponentAdapters()) .hasSize( CONTAINER_ITSELF - + 81 // level 4 + + 83 // level 4 + 21 // content of QualityGateModule + 6 // content of CeConfigurationModule + 4 // content of CeQueueModule diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java index 38075e93fc5..6d5d64c9eca 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java @@ -181,6 +181,11 @@ public class RuleDto { return definition.isExternal(); } + public RuleDto setIsExternal(boolean isExternal) { + definition.setIsExternal(isExternal); + return this; + } + public boolean isTemplate() { return definition.isTemplate(); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleForIndexingDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleForIndexingDto.java index 9840e9207d8..0a42c354613 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleForIndexingDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleForIndexingDto.java @@ -42,6 +42,7 @@ public class RuleForIndexingDto { private String templateRepository; private String internalKey; private String language; + private boolean isExternal; private int type; private long createdAt; private long updatedAt; @@ -107,6 +108,10 @@ public class RuleForIndexingDto { public int getType() { return type; } + + public boolean isExternal() { + return isExternal; + } public long getCreatedAt() { return createdAt; diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml index 0d5acbf4bba..3aa294ff91d 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml @@ -252,6 +252,7 @@ r.priority as "severity", r.status as "status", r.is_template as "isTemplate", + r.is_external as "isExternal", r.system_tags as "systemTags", t.plugin_rule_key as "templateRuleKey", t.plugin_name as "templateRepository", diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java index 44e469219f9..99d2e4555c4 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java @@ -23,6 +23,7 @@ import com.google.common.collect.Iterables; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Optional; import java.util.Set; @@ -360,6 +361,7 @@ public class RuleDaoTest { assertThat(actual.getConfigKey()).isEqualTo(expected.getConfigKey()); assertThat(actual.getSeverity()).isEqualTo(expected.getSeverity()); assertThat(actual.getSeverityString()).isEqualTo(expected.getSeverityString()); + assertThat(actual.isExternal()).isEqualTo(expected.isExternal()); assertThat(actual.isTemplate()).isEqualTo(expected.isTemplate()); assertThat(actual.getLanguage()).isEqualTo(expected.getLanguage()); assertThat(actual.getTemplateId()).isEqualTo(expected.getTemplateId()); @@ -809,14 +811,15 @@ public class RuleDaoTest { public void scrollIndexingRules() { Accumulator accumulator = new Accumulator<>(); RuleDefinitionDto r1 = db.rules().insert(); - RuleDefinitionDto r2 = db.rules().insert(); + RuleDefinitionDto r2 = db.rules().insert(r -> r.setIsExternal(true)); underTest.scrollIndexingRules(db.getSession(), accumulator); assertThat(accumulator.list) .extracting(RuleForIndexingDto::getId, RuleForIndexingDto::getRuleKey) .containsExactlyInAnyOrder(tuple(r1.getId(), r1.getKey()), tuple(r2.getId(), r2.getKey())); - RuleForIndexingDto firstRule = accumulator.list.iterator().next(); + Iterator it = accumulator.list.iterator(); + RuleForIndexingDto firstRule = it.next(); assertThat(firstRule.getRepository()).isEqualTo(r1.getRepositoryKey()); assertThat(firstRule.getPluginRuleKey()).isEqualTo(r1.getRuleKey()); @@ -825,6 +828,7 @@ public class RuleDaoTest { assertThat(firstRule.getDescriptionFormat()).isEqualTo(r1.getDescriptionFormat()); assertThat(firstRule.getSeverity()).isEqualTo(r1.getSeverity()); assertThat(firstRule.getStatus()).isEqualTo(r1.getStatus()); + assertThat(firstRule.isExternal()).isFalse(); assertThat(firstRule.isTemplate()).isEqualTo(r1.isTemplate()); assertThat(firstRule.getSystemTagsAsSet()).isEqualTo(r1.getSystemTags()); assertThat(firstRule.getTemplateRuleKey()).isNull(); @@ -834,6 +838,10 @@ public class RuleDaoTest { assertThat(firstRule.getType()).isEqualTo(r1.getType()); assertThat(firstRule.getCreatedAt()).isEqualTo(r1.getCreatedAt()); assertThat(firstRule.getUpdatedAt()).isEqualTo(r1.getUpdatedAt()); + + RuleForIndexingDto secondRule = it.next(); + assertThat(secondRule.isExternal()).isTrue(); + } @Test diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java index 3339cc08120..77b70a3ecff 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java @@ -305,6 +305,10 @@ public class RuleTesting { public static Consumer setType(RuleType type) { return rule -> rule.setType(type); } + + public static Consumer setIsExternal(boolean isExternal) { + return rule -> rule.setIsExternal(isExternal); + } public static Consumer setIsTemplate(boolean isTemplate) { return rule -> rule.setIsTemplate(isTemplate); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java index b7692f5cb56..f40d4dece13 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java @@ -122,7 +122,6 @@ import org.sonar.server.computation.task.projectanalysis.webhook.WebhookPostTask import org.sonar.server.computation.task.step.ComputationStepExecutor; import org.sonar.server.computation.task.step.ComputationSteps; import org.sonar.server.computation.taskprocessor.MutableTaskResultHolderImpl; -import org.sonar.server.rule.ExternalRuleCreator; import org.sonar.server.view.index.ViewIndex; public final class ProjectAnalysisTaskContainerPopulator implements ContainerPopulator { @@ -197,7 +196,6 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop DuplicationRepositoryImpl.class, // issues - ExternalRuleCreator.class, RuleRepositoryImpl.class, ScmAccountToUserLoader.class, ScmAccountToUser.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/RuleRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/RuleRepositoryImpl.java index 10eb88f7e09..b29dea960cd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/RuleRepositoryImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/RuleRepositoryImpl.java @@ -72,7 +72,7 @@ public class RuleRepositoryImpl implements RuleRepository { } private void persistAndIndex(DbSession dbSession, NewExternalRule external) { - Rule rule = creator.create(dbSession, external); + Rule rule = creator.persistAndIndex(dbSession, external); rulesById.put(rule.getId(), rule); rulesByKey.put(external.getKey(), rule); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ExternalRuleCreator.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ExternalRuleCreator.java index 2cbef97cbd5..31f4467ff18 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ExternalRuleCreator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ExternalRuleCreator.java @@ -19,7 +19,7 @@ */ package org.sonar.server.rule; -import org.sonar.api.server.ServerSide; +import org.sonar.api.rule.RuleStatus; import org.sonar.api.utils.System2; import org.sonar.db.DbClient; import org.sonar.db.DbSession; @@ -28,34 +28,45 @@ import org.sonar.db.rule.RuleDefinitionDto; import org.sonar.server.computation.task.projectanalysis.issue.NewExternalRule; import org.sonar.server.computation.task.projectanalysis.issue.Rule; import org.sonar.server.computation.task.projectanalysis.issue.RuleImpl; +import org.sonar.server.rule.index.RuleIndexer; import static org.sonar.db.rule.RuleDto.Scope.ALL; -@ServerSide public class ExternalRuleCreator { private final DbClient dbClient; private final System2 system2; + private final RuleIndexer ruleIndexer; - public ExternalRuleCreator(DbClient dbClient, System2 system2) { + public ExternalRuleCreator(DbClient dbClient, System2 system2, RuleIndexer ruleIndexer) { this.dbClient = dbClient; this.system2 = system2; + this.ruleIndexer = ruleIndexer; } - public Rule create(DbSession dbSession, NewExternalRule external) { + /** + * Persists a rule in the DB and indexes it. + * @return the rule that was inserted in the DB, which includes the generated ID. + */ + public Rule persistAndIndex(DbSession dbSession, NewExternalRule external) { RuleDao dao = dbClient.ruleDao(); dao.insert(dbSession, new RuleDefinitionDto() .setRuleKey(external.getKey()) .setPluginKey(external.getPluginKey()) - .setIsExternal(true) + .setIsExternal(external.isExternal()) .setName(external.getName()) .setDescriptionURL(external.getDescriptionUrl()) .setType(external.getType()) .setScope(ALL) + .setStatus(RuleStatus.READY) .setSeverity(external.getSeverity()) .setCreatedAt(system2.now()) .setUpdatedAt(system2.now())); - return new RuleImpl(dao.selectOrFailByKey(dbSession, external.getKey())); + + Rule newRule = new RuleImpl(dao.selectOrFailByKey(dbSession, external.getKey())); + // TODO write rule repository if needed + ruleIndexer.commitAndIndex(dbSession, newRule.getId()); + return newRule; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java index 3ffb85b521f..9f9d1ce3764 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java @@ -391,6 +391,7 @@ public class RegisterRules implements Startable { .setSystemTags(ruleDef.tags()) .setType(RuleType.valueOf(ruleDef.type().name())) .setScope(toDtoScope(ruleDef.scope())) + .setIsExternal(false) .setCreatedAt(system2.now()) .setUpdatedAt(system2.now()); if (ruleDef.htmlDescription() != null) { @@ -633,7 +634,7 @@ public class RegisterRules implements Startable { recorder.getRemaining().forEach(rule -> { if (rule.isCustomRule()) { customRules.add(rule); - } else if (rule.getStatus() != RuleStatus.REMOVED) { + } else if (!rule.isExternal() && rule.getStatus() != RuleStatus.REMOVED) { removeRule(dbSession, recorder, rule); } }); diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java index 4645f7ce6d8..f413798dc89 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java @@ -181,6 +181,15 @@ public class RuleDoc extends BaseDoc { return this; } + public boolean isExternal() { + return getField(RuleIndexDefinition.FIELD_RULE_IS_EXTERNAL); + } + + public RuleDoc setIsExternal(boolean b) { + setField(RuleIndexDefinition.FIELD_RULE_IS_EXTERNAL, b); + return this; + } + public RuleType type() { return RuleType.valueOf(getField(RuleIndexDefinition.FIELD_RULE_TYPE)); } @@ -220,6 +229,7 @@ public class RuleDoc extends BaseDoc { .setRepository(dto.getRepository()) .setInternalKey(dto.getInternalKey()) .setIsTemplate(dto.isTemplate()) + .setIsExternal(dto.isExternal()) .setLanguage(dto.getLanguage()) .setName(dto.getName()) .setRuleKey(dto.getPluginRuleKey()) diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java index 25f98b5098f..d93d6fc5893 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java @@ -93,6 +93,7 @@ import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_EXTENSI import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_EXTENSION_TAGS; import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_HTML_DESCRIPTION; import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_INTERNAL_KEY; +import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_IS_EXTERNAL; import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_IS_TEMPLATE; import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_KEY; import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_LANGUAGE; @@ -205,13 +206,12 @@ public class RuleIndex { .stream().map(token -> boolQuery().should( matchQuery( SEARCH_GRAMS_ANALYZER.subField(FIELD_RULE_NAME), - StringUtils.left(token, DefaultIndexSettings.MAXIMUM_NGRAM_LENGTH) - ).boost(20f)).should( - matchPhraseQuery( - ENGLISH_HTML_ANALYZER.subField(FIELD_RULE_HTML_DESCRIPTION), - token - ).boost(3f)) - ).forEach(textQuery::must); + StringUtils.left(token, DefaultIndexSettings.MAXIMUM_NGRAM_LENGTH)).boost(20f)) + .should( + matchPhraseQuery( + ENGLISH_HTML_ANALYZER.subField(FIELD_RULE_HTML_DESCRIPTION), + token).boost(3f))) + .forEach(textQuery::must); qb.should(textQuery.boost(20f)); } @@ -302,6 +302,12 @@ public class RuleIndex { QueryBuilders.termQuery(FIELD_RULE_IS_TEMPLATE, Boolean.toString(isTemplate))); } + Boolean isExternal = query.isExternal(); + if (isExternal != null) { + filters.put(FIELD_RULE_IS_EXTERNAL, + QueryBuilders.termQuery(FIELD_RULE_IS_EXTERNAL, Boolean.toString(isExternal))); + } + String template = query.templateKey(); if (template != null) { filters.put(FIELD_RULE_TEMPLATE_KEY, diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java index 38e54fcb4b0..63567d83026 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java @@ -53,6 +53,7 @@ public class RuleIndexDefinition implements IndexDefinition { public static final String FIELD_RULE_STATUS = "status"; public static final String FIELD_RULE_LANGUAGE = "lang"; public static final String FIELD_RULE_IS_TEMPLATE = "isTemplate"; + public static final String FIELD_RULE_IS_EXTERNAL = "isExternal"; public static final String FIELD_RULE_TEMPLATE_KEY = "templateKey"; public static final String FIELD_RULE_TYPE = "type"; public static final String FIELD_RULE_CREATED_AT = "createdAt"; @@ -154,6 +155,7 @@ public class RuleIndexDefinition implements IndexDefinition { ruleMapping.keywordFieldBuilder(FIELD_RULE_LANGUAGE).disableNorms().build(); ruleMapping.createBooleanField(FIELD_RULE_IS_TEMPLATE); + ruleMapping.createBooleanField(FIELD_RULE_IS_EXTERNAL); ruleMapping.keywordFieldBuilder(FIELD_RULE_TEMPLATE_KEY).disableNorms().build(); ruleMapping.keywordFieldBuilder(FIELD_RULE_TYPE).disableNorms().build(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleQuery.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleQuery.java index e0b6f3040a4..4b79ffcfcd9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleQuery.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleQuery.java @@ -54,6 +54,7 @@ public class RuleQuery { private String internalKey; private String ruleKey; private OrganizationDto organization; + private Boolean isExternal; @CheckForNull public QProfileDto getQProfile() { @@ -205,6 +206,16 @@ public class RuleQuery { return this; } + @CheckForNull + public Boolean isExternal() { + return isExternal; + } + + public RuleQuery setIsExternal(@Nullable Boolean b) { + this.isExternal = b; + return this; + } + @CheckForNull public String templateKey() { return templateKey; diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapper.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapper.java index 09790900602..9a70c738b52 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapper.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapper.java @@ -52,6 +52,7 @@ import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_EFFORT_TO_FIX_DES import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_GAP_DESCRIPTION; import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_HTML_DESCRIPTION; import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_INTERNAL_KEY; +import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_IS_EXTERNAL; import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_IS_TEMPLATE; import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_LANGUAGE; import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_LANGUAGE_NAME; @@ -115,6 +116,7 @@ public class RuleMapper { setLanguage(ruleResponse, ruleDefinitionDto, fieldsToReturn); setLanguageName(ruleResponse, ruleDefinitionDto, fieldsToReturn); setIsTemplate(ruleResponse, ruleDefinitionDto, fieldsToReturn); + setIsExternal(ruleResponse, ruleDefinitionDto, fieldsToReturn); setTemplateKey(ruleResponse, ruleDefinitionDto, result, fieldsToReturn); setDefaultDebtRemediationFunctionFields(ruleResponse, ruleDefinitionDto, fieldsToReturn); setEffortToFixDescription(ruleResponse, ruleDefinitionDto, fieldsToReturn); @@ -323,6 +325,12 @@ public class RuleMapper { ruleResponse.setIsTemplate(ruleDto.isTemplate()); } } + + private static void setIsExternal(Rules.Rule.Builder ruleResponse, RuleDefinitionDto ruleDto, Set fieldsToReturn) { + if (shouldReturnField(fieldsToReturn, FIELD_IS_EXTERNAL)) { + ruleResponse.setIsExternal(ruleDto.isExternal()); + } + } private static void setTemplateKey(Rules.Rule.Builder ruleResponse, RuleDefinitionDto ruleDto, SearchResult result, Set fieldsToReturn) { if (shouldReturnField(fieldsToReturn, FIELD_TEMPLATE_KEY) && ruleDto.isCustomRule()) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java index 75baf8a2e4d..a197a0ee8b9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java @@ -43,6 +43,7 @@ import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_AVAILABLE_SINCE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_COMPARE_TO_PROFILE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_INHERITANCE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_IS_TEMPLATE; +import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_IS_EXTERNAL; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_LANGUAGES; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ORGANIZATION; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_QPROFILE; @@ -91,6 +92,7 @@ public class RuleQueryFactory { query.setInheritance(request.paramAsStrings(PARAM_INHERITANCE)); query.setActiveSeverities(request.paramAsStrings(PARAM_ACTIVE_SEVERITIES)); query.setIsTemplate(request.paramAsBoolean(PARAM_IS_TEMPLATE)); + query.setIsExternal(request.paramAsBoolean(PARAM_IS_EXTERNAL)); query.setTemplateKey(request.param(PARAM_TEMPLATE_KEY)); query.setTypes(toEnums(request.paramAsStrings(PARAM_TYPES), RuleType.class)); query.setKey(request.param(PARAM_RULE_KEY)); diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWsParameters.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWsParameters.java index 9b87af2679f..06ee60ec50b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWsParameters.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWsParameters.java @@ -36,6 +36,7 @@ public class RulesWsParameters { public static final String PARAM_INHERITANCE = "inheritance"; public static final String PARAM_ACTIVE_SEVERITIES = "active_severities"; public static final String PARAM_IS_TEMPLATE = "is_template"; + public static final String PARAM_IS_EXTERNAL = "is_external"; public static final String PARAM_TEMPLATE_KEY = "template_key"; public static final String PARAM_ORGANIZATION = "organization"; public static final String PARAM_COMPARE_TO_PROFILE = "compareToProfile"; @@ -46,6 +47,7 @@ public class RulesWsParameters { public static final String FIELD_SEVERITY = "severity"; public static final String FIELD_STATUS = "status"; public static final String FIELD_INTERNAL_KEY = "internalKey"; + public static final String FIELD_IS_EXTERNAL = "isExternal"; public static final String FIELD_IS_TEMPLATE = "isTemplate"; public static final String FIELD_TEMPLATE_KEY = "templateKey"; public static final String FIELD_TAGS = "tags"; @@ -90,11 +92,11 @@ public class RulesWsParameters { * @since 7.1 */ public static final String FIELD_SCOPE = "scope"; - + public static final String FIELD_PARAMS = "params"; public static final String FIELD_ACTIVES = "actives"; - public static final Set OPTIONAL_FIELDS = ImmutableSet.of(FIELD_REPO, FIELD_NAME, FIELD_CREATED_AT, FIELD_SEVERITY, FIELD_STATUS, FIELD_INTERNAL_KEY, FIELD_IS_TEMPLATE, + public static final Set OPTIONAL_FIELDS = ImmutableSet.of(FIELD_REPO, FIELD_NAME, FIELD_CREATED_AT, FIELD_SEVERITY, FIELD_STATUS, FIELD_INTERNAL_KEY, FIELD_IS_EXTERNAL, FIELD_IS_TEMPLATE, FIELD_TEMPLATE_KEY, FIELD_TAGS, FIELD_SYSTEM_TAGS, FIELD_LANGUAGE, FIELD_LANGUAGE_NAME, FIELD_HTML_DESCRIPTION, FIELD_MARKDOWN_DESCRIPTION, FIELD_NOTE_LOGIN, FIELD_MARKDOWN_NOTE, FIELD_HTML_NOTE, FIELD_DEFAULT_DEBT_REM_FUNCTION, FIELD_EFFORT_TO_FIX_DESCRIPTION, FIELD_DEBT_OVERLOADED, FIELD_DEBT_REM_FUNCTION, diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java index 856ecf74362..27349801230 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java @@ -85,6 +85,7 @@ import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVE_SEVERITIES import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_AVAILABLE_SINCE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_COMPARE_TO_PROFILE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_INHERITANCE; +import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_IS_EXTERNAL; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_IS_TEMPLATE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_LANGUAGES; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ORGANIZATION; @@ -132,7 +133,9 @@ public class SearchAction implements RulesWsAction { .addPagingParams(100, MAX_LIMIT) .setHandler(this) .setChangelog(new Change("7.1", "The field 'scope' has been added to the response")) - .setChangelog(new Change("7.1", "The field 'scope' has been added to the 'f' parameter")); + .setChangelog(new Change("7.1", "The field 'scope' has been added to the 'f' parameter")) + .setChangelog(new Change("7.2", "The field 'isExternal' has been added to the response")) + .setChangelog(new Change("7.2", "The field 'isExternal' has been added to the 'f' parameter"));; action.createParam(FACETS) .setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.") @@ -292,6 +295,12 @@ public class SearchAction implements RulesWsAction { .setDescription("Filter template rules") .setBooleanPossibleValues(); + action + .createParam(PARAM_IS_EXTERNAL) + .setDescription("Filter external engine rules") + .setBooleanPossibleValues() + .setSince("7.2"); + action .createParam(PARAM_TEMPLATE_KEY) .setDescription("Key of the template rule to filter on. Used to search for the custom rules based on this template.") diff --git a/server/sonar-server/src/main/resources/org/sonar/server/rule/ws/example-show.json b/server/sonar-server/src/main/resources/org/sonar/server/rule/ws/example-show.json index b52d4c8c4c2..8664d1509fa 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/rule/ws/example-show.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/rule/ws/example-show.json @@ -20,6 +20,7 @@ "lang": "java", "langName": "Java", "scope": "MAIN", + "isExternal": false, "type": "CODE_SMELL", "params": [ { diff --git a/server/sonar-server/src/main/resources/org/sonar/server/rule/ws/search-example.json b/server/sonar-server/src/main/resources/org/sonar/server/rule/ws/search-example.json index 84c840898a8..2e5091ac51f 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/rule/ws/search-example.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/rule/ws/search-example.json @@ -18,6 +18,7 @@ "lang": "java", "langName": "Java", "scope": "MAIN", + "isExternal": false, "type": "CODE_SMELL", "params": [ { @@ -42,6 +43,7 @@ "lang": "java", "langName": "Java", "scope": "MAIN", + "isExternal": false, "type": "BUG", "params": [ { @@ -66,6 +68,7 @@ "lang": "java", "langName": "Java", "scope": "MAIN", + "isExternal": false, "type": "VULNERABILITY", "params": [ { @@ -93,6 +96,7 @@ "lang": "java", "langName": "Java", "scope": "MAIN", + "isExternal": false, "type": "CODE_SMELL", "params": [ { @@ -123,6 +127,7 @@ "lang": "java", "langName": "Java", "scope": "MAIN", + "isExternal": false, "type": "CODE_SMELL", "params": [ { diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/RuleRepositoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/RuleRepositoryImplTest.java index 318f4e4ca7b..39d085ad39c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/RuleRepositoryImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/RuleRepositoryImplTest.java @@ -21,6 +21,7 @@ package org.sonar.server.computation.task.projectanalysis.issue; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import java.util.List; import java.util.Optional; import org.junit.Before; import org.junit.Test; @@ -41,6 +42,7 @@ import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetada import org.sonar.server.es.EsTester; import org.sonar.server.rule.ExternalRuleCreator; import org.sonar.server.rule.index.RuleIndexDefinition; +import org.sonar.server.rule.index.RuleIndexer; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -75,14 +77,13 @@ public class RuleRepositoryImplTest { @org.junit.Rule public DbTester db = DbTester.create(System2.INSTANCE); - @org.junit.Rule - public EsTester es = new EsTester(new RuleIndexDefinition(new MapSettings().asConfig())); private DbClient dbClient = mock(DbClient.class); private DbSession dbSession = mock(DbSession.class); private RuleDao ruleDao = mock(RuleDao.class); - private ExternalRuleCreator externalRuleCreator = new ExternalRuleCreator(dbClient, System2.INSTANCE); + private RuleIndexer ruleIndexer = mock(RuleIndexer.class); + private ExternalRuleCreator externalRuleCreator = new ExternalRuleCreator(db.getDbClient(), System2.INSTANCE, ruleIndexer); private RuleRepositoryImpl underTest = new RuleRepositoryImpl(externalRuleCreator, dbClient, analysisMetadataHolder); @Before @@ -272,10 +273,6 @@ public class RuleRepositoryImplTest { @Test public void accept_new_externally_defined_Rules() { - DbClient dbClient = db.getDbClient(); - externalRuleCreator = new ExternalRuleCreator(dbClient, System2.INSTANCE); - underTest = new RuleRepositoryImpl(externalRuleCreator, dbClient, analysisMetadataHolder); - RuleKey ruleKey = RuleKey.of("eslint", "no-cond-assign"); underTest.insertNewExternalRuleIfAbsent(ruleKey, () -> new NewExternalRule.Builder() @@ -299,10 +296,7 @@ public class RuleRepositoryImplTest { @Test public void persist_new_externally_defined_Rules() { - DbClient dbClient = db.getDbClient(); - DbSession dbSession = dbClient.openSession(false); - externalRuleCreator = new ExternalRuleCreator(dbClient, System2.INSTANCE); - underTest = new RuleRepositoryImpl(externalRuleCreator, dbClient, analysisMetadataHolder); + underTest = new RuleRepositoryImpl(externalRuleCreator, db.getDbClient(), analysisMetadataHolder); RuleKey ruleKey = RuleKey.of("eslint", "no-cond-assign"); underTest.insertNewExternalRuleIfAbsent(ruleKey, () -> new NewExternalRule.Builder() @@ -314,18 +308,17 @@ public class RuleRepositoryImplTest { .setType(BUG) .build()); - underTest.persistNewExternalRules(dbSession); - - dbSession.commit(); + underTest.persistNewExternalRules(db.getSession()); + db.commit(); - RuleDao ruleDao = dbClient.ruleDao(); - Optional ruleDefinitionDto = ruleDao.selectDefinitionByKey(dbClient.openSession(false), ruleKey); + Optional ruleDefinitionDto = db.getDbClient().ruleDao().selectDefinitionByKey(db.getSession(), ruleKey); assertThat(ruleDefinitionDto).isPresent(); Rule rule = underTest.getByKey(ruleKey); assertThat(rule).isNotNull(); assertThat(underTest.getById(ruleDefinitionDto.get().getId())).isNotNull(); + verify(ruleIndexer).commitAndIndex(db.getSession(), ruleDefinitionDto.get().getId()); } private void expectNullRuleKeyNPE() { diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistExternalRulesStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistExternalRulesStepTest.java index d3f2a9d33db..219470ccf18 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistExternalRulesStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistExternalRulesStepTest.java @@ -19,6 +19,7 @@ */ package org.sonar.server.computation.task.projectanalysis.step; +import java.util.Optional; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -36,8 +37,7 @@ import org.sonar.server.computation.task.step.ComputationStep; import org.sonar.server.es.EsTester; import org.sonar.server.rule.ExternalRuleCreator; import org.sonar.server.rule.index.RuleIndexDefinition; - -import java.util.Optional; +import org.sonar.server.rule.index.RuleIndexer; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.api.rule.Severity.BLOCKER; @@ -60,7 +60,8 @@ public class PersistExternalRulesStepTest extends BaseStepTest { @org.junit.Rule public EsTester es = new EsTester(new RuleIndexDefinition(new MapSettings().asConfig())); - private ExternalRuleCreator externalRuleCreator = new ExternalRuleCreator(dbClient, System2.INSTANCE); + private RuleIndexer indexer = new RuleIndexer(es.client(), dbClient); + private ExternalRuleCreator externalRuleCreator = new ExternalRuleCreator(dbClient, System2.INSTANCE, indexer); @Override protected ComputationStep step() { @@ -74,7 +75,7 @@ public class PersistExternalRulesStepTest extends BaseStepTest { } @Test - public void persist_new_external_rules() { + public void persist_and_index_new_external_rules() { RuleKey ruleKey = RuleKey.of("eslint", "no-cond-assign"); ruleRepository.insertNewExternalRuleIfAbsent(ruleKey, () -> new NewExternalRule.Builder() @@ -102,7 +103,28 @@ public class PersistExternalRulesStepTest extends BaseStepTest { assertThat(reloaded.getName()).isEqualTo("disallow assignment operators in conditional statements (no-cond-assign)"); assertThat(reloaded.getPluginKey()).isEqualTo("eslint"); + assertThat(es.countDocuments(RuleIndexDefinition.INDEX_TYPE_RULE)).isEqualTo(1l); + assertThat(es.getDocuments(RuleIndexDefinition.INDEX_TYPE_RULE).iterator().next().getId()).isEqualTo(Integer.toString(reloaded.getId())); + } + + @Test + public void do_not_persist_existing_external_rules() { + RuleKey ruleKey = RuleKey.of("eslint", "no-cond-assign"); + db.rules().insert(ruleKey, r -> r.setIsExternal(true)); + ruleRepository.insertNewExternalRuleIfAbsent(ruleKey, () -> new NewExternalRule.Builder() + .setKey(ruleKey) + .setPluginKey("eslint") + .setName("disallow assignment operators in conditional statements (no-cond-assign)") + .setDescriptionUrl("https://eslint.org/docs/rules/no-cond-assign") + .setSeverity(BLOCKER) + .setType(BUG) + .build()); + + underTest.execute(); + RuleDao ruleDao = dbClient.ruleDao(); + assertThat(ruleDao.selectAllDefinitions(dbClient.openSession(false))).hasSize(1); + assertThat(es.countDocuments(RuleIndexDefinition.INDEX_TYPE_RULE)).isZero(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStepTest.java index e57c3dd6729..0b4eaa16e1e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStepTest.java @@ -89,7 +89,7 @@ public class PersistIssuesStepTest extends BaseStepTest { @org.junit.Rule public EsTester es = new EsTester(new RuleIndexDefinition(new MapSettings().asConfig())); - private ExternalRuleCreator externalRuleCreator = new ExternalRuleCreator(dbClient, System2.INSTANCE); + private ExternalRuleCreator externalRuleCreator = mock(ExternalRuleCreator.class); @Override protected ComputationStep step() { diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/ExternalRuleCreatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/ExternalRuleCreatorTest.java index 2beeddfc76c..ca23d02eb4a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/ExternalRuleCreatorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/ExternalRuleCreatorTest.java @@ -20,12 +20,16 @@ package org.sonar.server.rule; import org.junit.Test; +import org.sonar.api.config.internal.MapSettings; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.System2; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.server.computation.task.projectanalysis.issue.NewExternalRule; import org.sonar.server.computation.task.projectanalysis.issue.Rule; +import org.sonar.server.es.EsTester; +import org.sonar.server.rule.index.RuleIndexDefinition; +import org.sonar.server.rule.index.RuleIndexer; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.api.rule.Severity.BLOCKER; @@ -35,13 +39,15 @@ public class ExternalRuleCreatorTest { @org.junit.Rule public DbTester dbTester = DbTester.create(System2.INSTANCE); - - private ExternalRuleCreator underTest = new ExternalRuleCreator(dbTester.getDbClient(), System2.INSTANCE); + @org.junit.Rule + public EsTester es = new EsTester(new RuleIndexDefinition(new MapSettings().asConfig())); + + private RuleIndexer indexer = new RuleIndexer(es.client(), dbTester.getDbClient()); + private ExternalRuleCreator underTest = new ExternalRuleCreator(dbTester.getDbClient(), System2.INSTANCE, indexer); private DbSession dbSession = dbTester.getSession(); @Test public void create_external_rule() { - RuleKey ruleKey = RuleKey.of("eslint", "no-cond-assign"); NewExternalRule externalRule = new NewExternalRule.Builder() .setKey(ruleKey) @@ -52,7 +58,7 @@ public class ExternalRuleCreatorTest { .setType(BUG) .build(); - Rule rule1 = underTest.create(dbSession, externalRule); + Rule rule1 = underTest.persistAndIndex(dbSession, externalRule); assertThat(rule1).isNotNull(); assertThat(rule1.isExternal()).isTrue(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java index 4c961c262dd..455a9a84193 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java @@ -63,6 +63,7 @@ import static org.sonar.api.rules.RuleType.CODE_SMELL; import static org.sonar.api.rules.RuleType.VULNERABILITY; import static org.sonar.db.rule.RuleTesting.setCreatedAt; import static org.sonar.db.rule.RuleTesting.setIsTemplate; +import static org.sonar.db.rule.RuleTesting.setIsExternal; import static org.sonar.db.rule.RuleTesting.setLanguage; import static org.sonar.db.rule.RuleTesting.setName; import static org.sonar.db.rule.RuleTesting.setOrganization; @@ -366,7 +367,7 @@ public class RuleIndexTest { // find all RuleQuery query = new RuleQuery(); - SearchIdResult results = underTest.search(query, new SearchOptions()); + SearchIdResult results = underTest.search(query, new SearchOptions()); assertThat(results.getIds()).hasSize(2); // Only template @@ -384,6 +385,33 @@ public class RuleIndexTest { results = underTest.search(query, new SearchOptions()); assertThat(results.getIds()).containsOnly(ruleIsTemplate.getId(), ruleNoTemplate.getId()); } + + @Test + public void search_by_is_external() { + RuleDefinitionDto ruleIsNotExternal = createRule(setIsExternal(false)); + RuleDefinitionDto ruleIsExternal = createRule(setIsExternal(true)); + index(); + + // find all + RuleQuery query = new RuleQuery(); + SearchIdResult results = underTest.search(query, new SearchOptions()); + assertThat(results.getIds()).hasSize(2); + + // Only template + query = new RuleQuery().setIsTemplate(true); + results = underTest.search(query, new SearchOptions()); + assertThat(results.getIds()).containsOnly(ruleIsExternal.getId()); + + // Only not template + query = new RuleQuery().setIsTemplate(false); + results = underTest.search(query, new SearchOptions()); + assertThat(results.getIds()).containsOnly(ruleIsNotExternal.getId()); + + // null => no filter + query = new RuleQuery().setIsTemplate(null); + results = underTest.search(query, new SearchOptions()); + assertThat(results.getIds()).containsOnly(ruleIsExternal.getId(), ruleIsNotExternal.getId()); + } @Test public void search_by_template_key() { diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleQueryFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleQueryFactoryTest.java index ea3c0761a6f..49edcf25723 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleQueryFactoryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleQueryFactoryTest.java @@ -51,13 +51,14 @@ import static org.sonar.api.server.ws.WebService.Param.SORT; import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY; import static org.sonar.db.qualityprofile.ActiveRuleDto.INHERITED; import static org.sonar.db.qualityprofile.ActiveRuleDto.OVERRIDES; -import static org.sonar.server.rule.ws.SearchAction.defineRuleSearchParameters; +import static org.sonar.server.rule.ws.SearchAction.defineGenericRuleSearchParameters; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVATION; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVE_SEVERITIES; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_AVAILABLE_SINCE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_COMPARE_TO_PROFILE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_INHERITANCE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_IS_TEMPLATE; +import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_IS_EXTERNAL; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_LANGUAGES; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ORGANIZATION; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_QPROFILE; @@ -99,6 +100,7 @@ public class RuleQueryFactoryTest { assertThat(result.isAscendingSort()).isTrue(); assertThat(result.getAvailableSinceLong()).isNull(); assertThat(result.getInheritance()).isNull(); + assertThat(result.isExternal()).isNull(); assertThat(result.isTemplate()).isNull(); assertThat(result.getLanguages()).isNull(); assertThat(result.getQueryText()).isNull(); @@ -126,7 +128,8 @@ public class RuleQueryFactoryTest { PARAM_ACTIVE_SEVERITIES, "MINOR,MAJOR", PARAM_AVAILABLE_SINCE, "2016-01-01", PARAM_INHERITANCE, "INHERITED,OVERRIDES", - PARAM_IS_TEMPLATE, "true", + PARAM_IS_TEMPLATE, "true", + PARAM_IS_EXTERNAL, "true", PARAM_LANGUAGES, "java,js", TEXT_QUERY, "S001", PARAM_ORGANIZATION, organization.getKey(), @@ -149,6 +152,7 @@ public class RuleQueryFactoryTest { assertThat(result.isAscendingSort()).isFalse(); assertThat(result.getAvailableSinceLong()).isNotNull(); assertThat(result.getInheritance()).containsOnly(INHERITED, OVERRIDES); + assertThat(result.isExternal()).isTrue(); assertThat(result.isTemplate()).isTrue(); assertThat(result.getLanguages()).containsOnly(qualityProfile.getLanguage()); assertThat(result.getQueryText()).isEqualTo("S001"); @@ -308,7 +312,7 @@ public class RuleQueryFactoryTest { public void define(WebService.NewController controller) { WebService.NewAction action = controller.createAction("fake") .setHandler(this); - defineRuleSearchParameters(action); + defineGenericRuleSearchParameters(action); } @Override diff --git a/sonar-ws/src/main/protobuf/ws-rules.proto b/sonar-ws/src/main/protobuf/ws-rules.proto index 7bca93aab8b..ee45d81f8b7 100644 --- a/sonar-ws/src/main/protobuf/ws-rules.proto +++ b/sonar-ws/src/main/protobuf/ws-rules.proto @@ -120,6 +120,7 @@ message Rule { optional bool remFnOverloaded = 45; optional string gapDescription = 44; optional sonarqube.ws.commons.RuleScope scope = 46; + optional bool isExternal = 47; optional sonarqube.ws.commons.RuleType type = 37; -- 2.39.5