import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.System2;
import org.sonar.core.persistence.DbSession;
-import org.sonar.core.qualityprofile.db.ActiveRuleDto;
-import org.sonar.core.qualityprofile.db.ActiveRuleKey;
-import org.sonar.core.qualityprofile.db.ActiveRuleMapper;
-import org.sonar.core.qualityprofile.db.ActiveRuleParamDto;
-import org.sonar.core.qualityprofile.db.QualityProfileDao;
-import org.sonar.core.qualityprofile.db.QualityProfileKey;
+import org.sonar.core.qualityprofile.db.*;
import org.sonar.core.rule.RuleDto;
import org.sonar.server.db.BaseDao;
import org.sonar.server.rule.db.RuleDao;
import org.sonar.server.search.action.KeyIndexAction;
import javax.annotation.CheckForNull;
+
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
public void deleteParam(DbSession session, ActiveRuleDto activeRule, ActiveRuleParamDto activeRuleParam) {
Preconditions.checkNotNull(activeRule.getId(), "ActiveRule is not persisted");
Preconditions.checkNotNull(activeRuleParam.getId(), "ActiveRuleParam is not persisted");
- mapper(session).updateParameter(activeRuleParam);
- this.enqueueUpdate(activeRuleParam, activeRule.getKey(), session);
+ mapper(session).deleteParameter(activeRuleParam.getId());
+ this.enqueueDelete(activeRuleParam, activeRule.getKey(), session);
}
public void deleteByProfileKey(DbSession session, QualityProfileKey profileKey) {
List<RuleParam> params();
+ @CheckForNull
+ RuleParam param(final String key);
+
@CheckForNull
String debtCharacteristicKey();
for (RuleParamDto templateRuleParamDto : dbClient.ruleDao().findRuleParamsByRuleKey(dbSession, templateRuleDto.getKey())) {
String newRuleParam = newRule.parameter(templateRuleParamDto.getName());
- if (newRuleParam == null) {
- throw new IllegalArgumentException(String.format("The parameter '%s' has not been set", templateRuleParamDto.getName()));
+ if (newRuleParam != null) {
+ createCustomRuleParams(newRuleParam, ruleDto, templateRuleParamDto, dbSession);
}
- createCustomRuleParams(newRuleParam, ruleDto, templateRuleParamDto, dbSession);
}
return ruleKey;
}
package org.sonar.server.rule;
import com.google.common.base.Strings;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.sonar.server.db.DbClient;
import org.sonar.server.user.UserSession;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Set;
-
-import static com.google.common.collect.Lists.newArrayList;
+import java.util.*;
public class RuleUpdater implements ServerComponent {
// validate only the changes, not all the rule fields
apply(update, context, userSession);
dbClient.ruleDao().update(dbSession, context.rule);
- for (RuleParamDto ruleParamDto : context.parameters) {
- dbClient.ruleDao().updateRuleParam(dbSession, context.rule, ruleParamDto);
- }
- // update related active rules (for custom rules only)
- updateActiveRule(dbSession, update, context.rule);
+ updateParameters(dbSession, update, context);
dbSession.commit();
return true;
}
}
- private void updateActiveRule(DbSession dbSession, RuleUpdate update, RuleDto rule) {
- if (update.isCustomRule() && update.isChangeParameters()) {
- for (ActiveRuleDto activeRuleDto : dbClient.activeRuleDao().findByRule(dbSession, rule)) {
- for (ActiveRuleParamDto activeRuleParamDto : dbClient.activeRuleDao().findParamsByActiveRuleKey(dbSession, activeRuleDto.getKey())) {
- String newValue = update.getParameters().get(activeRuleParamDto.getKey());
- if (!Strings.isNullOrEmpty(newValue)) {
- activeRuleParamDto.setValue(newValue);
- dbClient.activeRuleDao().updateParam(dbSession, activeRuleDto, activeRuleParamDto);
- }
- }
- }
- }
- }
-
/**
* Load all the DTOs required for validating changes and updating rule
*/
if (RuleStatus.REMOVED == context.rule.getStatus()) {
throw new IllegalArgumentException("Rule with REMOVED status cannot be updated: " + change.getRuleKey());
}
- context.parameters = dbClient.ruleDao().findRuleParamsByRuleKey(dbSession, change.getRuleKey());
-
String subCharacteristicKey = change.getDebtSubCharacteristicKey();
if (subCharacteristicKey != null &&
!subCharacteristicKey.equals(RuleUpdate.DEFAULT_DEBT_CHARACTERISTIC)) {
if (update.isChangeStatus()) {
updateStatus(update, context);
}
- if (update.isChangeParameters()) {
- updateParameters(update, context);
- }
if (update.isChangeMarkdownNote()) {
updateMarkdownNote(update, context, userSession);
}
}
}
- /**
- * Only update existing parameters, ignore the ones that are not existing in the list
- */
- private void updateParameters(RuleUpdate update, Context context) {
- for (RuleParamDto ruleParamDto : context.parameters) {
- String value = update.parameter(ruleParamDto.getName());
- if (!Strings.isNullOrEmpty(value)) {
- ruleParamDto.setDefaultValue(value);
- }
- // Ignore parameter not existing in update.getParameters()
- }
- }
-
private void updateTags(RuleUpdate update, Context context) {
Set<String> tags = update.getTags();
if (tags == null || tags.isEmpty()) {
context.rule.setRemediationCoefficient(null);
context.rule.setRemediationOffset(null);
- } else if (update.getDebtSubCharacteristicKey().equals(RuleUpdate.DEFAULT_DEBT_CHARACTERISTIC)) {
+ } else if (StringUtils.equals(update.getDebtSubCharacteristicKey(), RuleUpdate.DEFAULT_DEBT_CHARACTERISTIC)) {
// reset to default
context.rule.setSubCharacteristicId(null);
context.rule.setRemediationFunction(null);
.isEquals();
}
+
+ private void updateParameters(DbSession dbSession, RuleUpdate update, Context context) {
+ if (update.isChangeParameters() && update.isCustomRule()) {
+ RuleDto customRule = context.rule;
+ RuleDto templateRule = dbClient.ruleDao().getById(dbSession, customRule.getTemplateId());
+ List<RuleParamDto> templateRuleParams = dbClient.ruleDao().findRuleParamsByRuleKey(dbSession, templateRule.getKey());
+ List<String> paramKeys = new ArrayList<String>();
+
+ // Load active rules and its parameters in cache
+ Multimap<RuleDto, ActiveRuleDto> activeRules = ArrayListMultimap.create();
+ Multimap<ActiveRuleDto, ActiveRuleParamDto> activeRuleParams = ArrayListMultimap.create();
+ for (ActiveRuleDto activeRuleDto : dbClient.activeRuleDao().findByRule(dbSession, customRule)) {
+ activeRules.put(customRule, activeRuleDto);
+ for (ActiveRuleParamDto activeRuleParamDto : dbClient.activeRuleDao().findParamsByActiveRuleKey(dbSession, activeRuleDto.getKey())) {
+ activeRuleParams.put(activeRuleDto, activeRuleParamDto);
+ }
+ }
+
+ // Browse custom rule parameters to update or delete them
+ for (RuleParamDto ruleParamDto : dbClient.ruleDao().findRuleParamsByRuleKey(dbSession, update.getRuleKey())) {
+ String key = ruleParamDto.getName();
+ String value = update.parameter(key);
+ if (!Strings.isNullOrEmpty(value)) {
+ // Update rule param
+ ruleParamDto.setDefaultValue(value);
+ dbClient.ruleDao().updateRuleParam(dbSession, customRule, ruleParamDto);
+
+ // Update linked active rule params
+ for (ActiveRuleDto activeRuleDto : activeRules.get(customRule)) {
+ for (ActiveRuleParamDto activeRuleParamDto : activeRuleParams.get(activeRuleDto)) {
+ if (activeRuleParamDto.getKey().equals(key)) {
+ dbClient.activeRuleDao().updateParam(dbSession, activeRuleDto, activeRuleParamDto.setValue(value));
+ }
+ }
+ }
+ } else {
+ // Delete rule param
+ dbClient.ruleDao().removeRuleParam(dbSession, customRule, ruleParamDto);
+
+ // Delete linked active rule params
+ for (ActiveRuleDto activeRuleDto : activeRules.get(customRule)) {
+ for (ActiveRuleParamDto activeRuleParamDto : activeRuleParams.get(activeRuleDto)) {
+ if (activeRuleParamDto.getKey().equals(key)) {
+ dbClient.activeRuleDao().deleteParam(dbSession, activeRuleDto, activeRuleParamDto);
+ }
+ }
+ }
+ }
+ paramKeys.add(key);
+ }
+
+ // Browse template rule parameters to create new parameters
+ for (RuleParamDto templateRuleParam : templateRuleParams) {
+ String key = templateRuleParam.getName();
+ if (!paramKeys.contains(key)) {
+ String value = update.parameter(key);
+ if (!Strings.isNullOrEmpty(value)) {
+
+ // Create new param
+ RuleParamDto paramDto = RuleParamDto.createFor(customRule)
+ .setName(key)
+ .setDescription(templateRuleParam.getDescription())
+ .setDefaultValue(value)
+ .setType(templateRuleParam.getType());
+ dbClient.ruleDao().addRuleParam(dbSession, customRule, paramDto);
+
+ // Create new active rule param
+ for (ActiveRuleDto activeRuleDto : activeRules.get(customRule)) {
+ dbClient.activeRuleDao().addParam(dbSession, activeRuleDto, ActiveRuleParamDto.createFor(paramDto).setValue(value));
+ }
+ }
+ }
+ }
+ }
+ }
+
/**
* Data loaded before update
*/
private static class Context {
private RuleDto rule;
- private List<RuleParamDto> parameters = newArrayList();
private CharacteristicDto newCharacteristic;
}
*/
package org.sonar.server.rule.index;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.server.search.BaseDoc;
import org.sonar.server.search.IndexUtils;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
return params;
}
+ @CheckForNull
+ @Override
+ public RuleParam param(final String key) {
+ return Iterables.find(params(), new Predicate<RuleParam>() {
+ @Override
+ public boolean apply(@Nullable RuleParam input) {
+ return input != null && input.key().equals(key);
+ }
+ }, null);
+ }
+
@Override
public String debtCharacteristicKey() {
return (String) getNullableField(RuleNormalizer.RuleField.CHARACTERISTIC.field());
assertThat(param.getDefaultValue()).isEqualTo("a.*");
}
+ @Test
+ public void not_fail_to_create_custom_rule_when_a_param_is_missing() throws Exception {
+ // insert template rule
+ RuleDto templateRule = createTemplateRule();
+
+ NewRule newRule = new NewRule()
+ .setRuleKey("CUSTOM_RULE")
+ .setTemplateKey(templateRule.getKey())
+ .setName("My custom")
+ .setHtmlDescription("Some description")
+ .setSeverity(Severity.MAJOR)
+ .setStatus(RuleStatus.READY);
+
+ RuleKey customRuleKey = creator.create(newRule);
+ dbSession.clearCache();
+
+ List<RuleParamDto> params = db.ruleDao().findRuleParamsByRuleKey(dbSession, customRuleKey);
+ assertThat(params).hasSize(0);
+ }
+
@Test
public void fail_to_create_custom_rule_when_missing_key() throws Exception {
// insert template rule
}
}
- @Test
- public void fail_to_create_custom_rule_when_a_param_is_missing() throws Exception {
- // insert template rule
- RuleDto templateRule = createTemplateRule();
-
- NewRule newRule = new NewRule()
- .setRuleKey("CUSTOM_RULE")
- .setTemplateKey(templateRule.getKey())
- .setName("My custom")
- .setHtmlDescription("Some description")
- .setSeverity(Severity.MAJOR)
- .setStatus(RuleStatus.READY);
-
- try {
- creator.create(newRule);
- fail();
- } catch (Exception e) {
- assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("The parameter 'regex' has not been set");
- }
- }
-
- private RuleDto createTemplateRule(){
+ private RuleDto createTemplateRule() {
RuleDto templateRule = dao.insert(dbSession,
RuleTesting.newDto(RuleKey.of("java", "S001"))
.setIsTemplate(true)
.setEffortToFixDescription("desc")
.setTags(Sets.newHashSet("usertag1", "usertag2"))
.setSystemTags(Sets.newHashSet("tag1", "tag4"))
- );
+ );
RuleParamDto ruleParamDto = RuleParamDto.createFor(templateRule).setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(".*");
dao.addRuleParam(dbSession, templateRule, ruleParamDto);
dbSession.commit();
@Test
public void update_active_rule_parameters_when_updating_custom_rule() throws Exception {
- // Create template rule with a parameter
+ // Create template rule with 3 parameters
RuleDto templateRule = RuleTesting.newTemplateRule(RuleKey.of("java", "S001")).setLanguage("xoo");
ruleDao.insert(dbSession, templateRule);
- RuleParamDto templateRuleParam = RuleParamDto.createFor(templateRule).setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(".*");
- ruleDao.addRuleParam(dbSession, templateRule, templateRuleParam);
-
- // Create custom rule with a parameter
+ RuleParamDto templateRuleParam1 = RuleParamDto.createFor(templateRule).setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(".*");
+ ruleDao.addRuleParam(dbSession, templateRule, templateRuleParam1);
+ RuleParamDto templateRuleParam2 = RuleParamDto.createFor(templateRule).setName("format").setType("STRING").setDescription("format").setDefaultValue("csv");
+ ruleDao.addRuleParam(dbSession, templateRule, templateRuleParam2);
+ RuleParamDto templateRuleParam3 = RuleParamDto.createFor(templateRule).setName("message").setType("STRING").setDescription("message");
+ ruleDao.addRuleParam(dbSession, templateRule, templateRuleParam3);
+
+ // Create custom rule with 2 parameters
RuleDto customRule = RuleTesting.newCustomRule(templateRule).setSeverity(Severity.MAJOR).setLanguage("xoo");
ruleDao.insert(dbSession, customRule);
- ruleDao.addRuleParam(dbSession, customRule, templateRuleParam.setDefaultValue("a.*"));
+ ruleDao.addRuleParam(dbSession, customRule, templateRuleParam1.setDefaultValue("a.*"));
+ ruleDao.addRuleParam(dbSession, customRule, templateRuleParam2.setDefaultValue("txt"));
// Create a quality profile
QualityProfileKey qualityProfileKey = QualityProfileKey.of("P1", "xoo");
dbSession.clearCache();
- // Update custom rule parameter
+ // Update custom rule parameter 'regex', add 'message' and remove 'format'
RuleUpdate update = RuleUpdate.createForCustomRule(customRule.getKey())
- .setParameters(ImmutableMap.of("regex", "b.*"));
+ .setParameters(ImmutableMap.of("regex", "b.*", "message", "a message"));
updater.update(update, UserSession.get());
- // Verify active rule parameter has been updated
+ dbSession.clearCache();
+
+ // Verify custom rule parameters has been updated
+ Rule customRuleReloaded = ruleIndex.getByKey(customRule.getKey());
+ assertThat(customRuleReloaded.params()).hasSize(2);
+ assertThat(customRuleReloaded.param("regex")).isNotNull();
+ assertThat(customRuleReloaded.param("regex").defaultValue()).isEqualTo("b.*");
+ assertThat(customRuleReloaded.param("message")).isNotNull();
+ assertThat(customRuleReloaded.param("message").defaultValue()).isEqualTo("a message");
+ assertThat(customRuleReloaded.param("format")).isNull();
+
+ RuleParam param = customRuleReloaded.params().get(0);
+ assertThat(param.defaultValue()).isEqualTo("b.*");
+
+ // Verify active rule parameters has been updated
ActiveRule activeRule = tester.get(ActiveRuleIndex.class).getByKey(ActiveRuleKey.of(qualityProfileKey, customRule.getKey()));
+ assertThat(activeRule.params()).hasSize(2);
assertThat(activeRule.params().get("regex")).isEqualTo("b.*");
+ assertThat(activeRule.params().get("format")).isNull();
+ assertThat(activeRule.params().get("message")).isEqualTo("a message");
// Verify that severity has not changed
assertThat(activeRule.severity()).isEqualTo(Severity.BLOCKER);