From d61dfe2937a485041a72571c544e2da93399dad1 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Wed, 28 May 2014 09:21:31 +0200 Subject: [PATCH] SONAR-5007 merge api/rules/set_tags and api/rules/set_note into api/rules/update --- .../java/org/sonar/core/rule/RuleDto.java | 6 +- .../org/sonar/core/rule/RuleMapper.xml | 10 +- .../server/platform/ServerComponents.java | 2 + .../java/org/sonar/server/rule/NewRule.java | 182 +++++++++ .../sonar/server/rule/RubyRuleService.java | 27 +- .../org/sonar/server/rule/RuleService.java | 8 +- .../org/sonar/server/rule/RuleUpdate.java | 122 ++++++ .../org/sonar/server/rule/RuleUpdater.java | 208 ++++++++++ .../sonar/server/rule/ws/UpdateAction.java | 64 ++++ .../org/sonar/server/platform/logback.xml | 6 +- .../index/ActiveRuleIndexMediumTest.java | 77 ++-- .../server/rule/RubyRuleServiceTest.java | 80 ---- .../java/org/sonar/server/rule/RuleTests.java | 54 +++ .../server/rule/RuleUpdaterMediumTest.java | 356 ++++++++++++++++++ 14 files changed, 1060 insertions(+), 142 deletions(-) create mode 100644 sonar-server/src/main/java/org/sonar/server/rule/NewRule.java create mode 100644 sonar-server/src/main/java/org/sonar/server/rule/RuleUpdate.java create mode 100644 sonar-server/src/main/java/org/sonar/server/rule/RuleUpdater.java create mode 100644 sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java delete mode 100644 sonar-server/src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/rule/RuleTests.java create mode 100644 sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java diff --git a/sonar-core/src/main/java/org/sonar/core/rule/RuleDto.java b/sonar-core/src/main/java/org/sonar/core/rule/RuleDto.java index 87fb8803b2f..c009af2cca9 100644 --- a/sonar-core/src/main/java/org/sonar/core/rule/RuleDto.java +++ b/sonar-core/src/main/java/org/sonar/core/rule/RuleDto.java @@ -340,12 +340,12 @@ public final class RuleDto extends Dto { } public RuleDto setTags(Set tags) { - this.tags = tags.isEmpty()?null:StringUtils.join(tags, ','); + this.tags = tags.isEmpty() ? null : StringUtils.join(tags, ','); return this; } public RuleDto setSystemTags(Set tags) { - this.systemTags = tags.isEmpty()?null:StringUtils.join(tags, ','); + this.systemTags = tags.isEmpty() ? null : StringUtils.join(tags, ','); return this; } @@ -377,7 +377,7 @@ public final class RuleDto extends Dto { return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString(); } - public static RuleDto createFor(RuleKey key){ + public static RuleDto createFor(RuleKey key) { return new RuleDto() .setRepositoryKey(key.repository()) .setRuleKey(key.rule()); diff --git a/sonar-core/src/main/resources/org/sonar/core/rule/RuleMapper.xml b/sonar-core/src/main/resources/org/sonar/core/rule/RuleMapper.xml index 73450f041d3..9775d15e944 100644 --- a/sonar-core/src/main/resources/org/sonar/core/rule/RuleMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/rule/RuleMapper.xml @@ -105,7 +105,7 @@ (plugin_rule_key, plugin_name, description, status, name, plugin_config_key, priority, cardinality, language, parent_id, characteristic_id, default_characteristic_id, remediation_function, default_remediation_function, remediation_coeff, default_remediation_coeff, remediation_offset, default_remediation_offset, - effort_to_fix_description, tags, system_tags, + effort_to_fix_description, tags, system_tags, note_data, note_user_login, note_created_at, note_updated_at, created_at, updated_at) @@ -115,8 +115,8 @@ #{severity}, #{cardinality}, #{language}, #{parentId}, #{subCharacteristicId}, #{defaultSubCharacteristicId}, #{remediationFunction}, #{defaultRemediationFunction}, #{remediationCoefficient}, #{defaultRemediationCoefficient}, #{remediationOffset}, #{defaultRemediationOffset}, - #{effortToFixDescription}, #{tagsField}, #{systemTagsField}, - #{createdAt}, #{updatedAt}) + #{effortToFixDescription}, #{tagsField}, #{systemTagsField}, #{noteData}, #{noteUserLogin}, #{noteCreatedAt}, + #{noteUpdatedAt}, #{createdAt}, #{updatedAt}) @@ -125,8 +125,8 @@ #{severity}, #{cardinality}, #{language}, #{parentId}, #{subCharacteristicId}, #{defaultSubCharacteristicId}, #{remediationFunction}, #{defaultRemediationFunction}, #{remediationCoefficient}, #{defaultRemediationCoefficient}, #{remediationOffset}, #{defaultRemediationOffset}, - #{effortToFixDescription}, #{tagsField}, #{systemTagsField}, - #{createdAt}, #{updatedAt}) + #{effortToFixDescription}, #{tagsField}, #{systemTagsField}, #{noteData}, #{noteUserLogin}, #{noteCreatedAt}, + #{noteUpdatedAt}, #{createdAt}, #{updatedAt}) diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 16d760ebe37..180cbcfdf31 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -321,6 +321,8 @@ class ServerComponents { pico.addSingleton(RuleDefinitionsLoader.class); pico.addSingleton(RulesDefinitionXmlLoader.class); pico.addSingleton(RuleService.class); + pico.addSingleton(RuleUpdater.class); + pico.addSingleton(UpdateAction.class); pico.addSingleton(RulesWebService.class); pico.addSingleton(SearchAction.class); pico.addSingleton(org.sonar.server.rule.ws.ShowAction.class); diff --git a/sonar-server/src/main/java/org/sonar/server/rule/NewRule.java b/sonar-server/src/main/java/org/sonar/server/rule/NewRule.java new file mode 100644 index 00000000000..840fe50d419 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule/NewRule.java @@ -0,0 +1,182 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.rule; + +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleStatus; +import org.sonar.api.server.debt.DebtRemediationFunction; + +import java.util.Set; + +class NewRule { + private final RuleKey key; + private boolean template; + private Set tags, systemTags; + private String language, name, htmlDescription, severity; + private RuleStatus status; + private String internalKey; + private String defaultDebtCharacteristic, defaultDebtSubCharacteristic, debtCharacteristic, debtSubCharacteristic; + private DebtRemediationFunction defaultDebtRemediationFunction, debtRemediationFunction; + private String markdownNote, noteLogin; + + NewRule(RuleKey key) { + this.key = key; + } + + public RuleKey getKey() { + return key; + } + + public boolean isTemplate() { + return template; + } + + public void setTemplate(boolean template) { + this.template = template; + } + + public Set getTags() { + return tags; + } + + public void setTags(Set tags) { + this.tags = tags; + } + + public Set getSystemTags() { + return systemTags; + } + + public void setSystemTags(Set systemTags) { + this.systemTags = systemTags; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getHtmlDescription() { + return htmlDescription; + } + + public void setHtmlDescription(String htmlDescription) { + this.htmlDescription = htmlDescription; + } + + public String getSeverity() { + return severity; + } + + public void setSeverity(String severity) { + this.severity = severity; + } + + public RuleStatus getStatus() { + return status; + } + + public void setStatus(RuleStatus status) { + this.status = status; + } + + public String getInternalKey() { + return internalKey; + } + + public void setInternalKey(String internalKey) { + this.internalKey = internalKey; + } + + public String getDefaultDebtCharacteristic() { + return defaultDebtCharacteristic; + } + + public void setDefaultDebtCharacteristic(String defaultDebtCharacteristic) { + this.defaultDebtCharacteristic = defaultDebtCharacteristic; + } + + public String getDefaultDebtSubCharacteristic() { + return defaultDebtSubCharacteristic; + } + + public void setDefaultDebtSubCharacteristic(String defaultDebtSubCharacteristic) { + this.defaultDebtSubCharacteristic = defaultDebtSubCharacteristic; + } + + public String getDebtCharacteristic() { + return debtCharacteristic; + } + + public void setDebtCharacteristic(String debtCharacteristic) { + this.debtCharacteristic = debtCharacteristic; + } + + public String getDebtSubCharacteristic() { + return debtSubCharacteristic; + } + + public void setDebtSubCharacteristic(String debtSubCharacteristic) { + this.debtSubCharacteristic = debtSubCharacteristic; + } + + public DebtRemediationFunction getDefaultDebtRemediationFunction() { + return defaultDebtRemediationFunction; + } + + public void setDefaultDebtRemediationFunction(DebtRemediationFunction defaultDebtRemediationFunction) { + this.defaultDebtRemediationFunction = defaultDebtRemediationFunction; + } + + public DebtRemediationFunction getDebtRemediationFunction() { + return debtRemediationFunction; + } + + public void setDebtRemediationFunction(DebtRemediationFunction debtRemediationFunction) { + this.debtRemediationFunction = debtRemediationFunction; + } + + public String getMarkdownNote() { + return markdownNote; + } + + public void setMarkdownNote(String markdownNote) { + this.markdownNote = markdownNote; + } + + public String getNoteLogin() { + return noteLogin; + } + + public void setNoteLogin(String noteLogin) { + this.noteLogin = noteLogin; + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule/RubyRuleService.java b/sonar-server/src/main/java/org/sonar/server/rule/RubyRuleService.java index dbc26d8ff8b..6704b68cab1 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/RubyRuleService.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/RubyRuleService.java @@ -24,11 +24,14 @@ import org.picocontainer.Startable; import org.sonar.api.ServerComponent; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; import org.sonar.server.paging.PagedResult; import org.sonar.server.paging.PagingResult; import org.sonar.server.rule.index.RuleQuery; import org.sonar.server.rule.index.RuleResult; import org.sonar.server.search.QueryOptions; +import org.sonar.server.user.UserSession; import org.sonar.server.util.RubyUtils; import javax.annotation.CheckForNull; @@ -43,9 +46,11 @@ import java.util.Map; public class RubyRuleService implements ServerComponent, Startable { private final RuleService service; + private final RuleUpdater updater; - public RubyRuleService(RuleService service) { + public RubyRuleService(RuleService service, RuleUpdater updater) { this.service = service; + this.updater = updater; } /** @@ -78,13 +83,19 @@ public class RubyRuleService implements ServerComponent, Startable { // sqale public void updateRule(Map params) { - //TODO -// rules.updateRule(new RuleOperations.RuleChange() -// .setRuleKey(RuleKey.parse((String) params.get("ruleKey"))) -// .setDebtCharacteristicKey(Strings.emptyToNull((String) params.get("debtCharacteristicKey"))) -// .setDebtRemediationFunction((String) params.get("debtRemediationFunction")) -// .setDebtRemediationCoefficient(Strings.emptyToNull((String) params.get("debtRemediationCoefficient"))) -// .setDebtRemediationOffset(Strings.emptyToNull((String) params.get("debtRemediationOffset")))); + RuleUpdate update = new RuleUpdate(RuleKey.parse((String) params.get("ruleKey"))); + update.setDebtSubCharacteristic(Strings.emptyToNull((String) params.get("debtCharacteristicKey"))); + String fn = (String) params.get("debtRemediationFunction"); + if (fn == null) { + update.setDebtRemediationFunction(null); + } else { + update.setDebtRemediationFunction(new DefaultDebtRemediationFunction( + DebtRemediationFunction.Type.valueOf(fn), + Strings.emptyToNull((String) params.get("debtRemediationCoefficient")), + Strings.emptyToNull((String) params.get("debtRemediationOffset"))) + ); + } + updater.update(update, UserSession.get()); } @Override diff --git a/sonar-server/src/main/java/org/sonar/server/rule/RuleService.java b/sonar-server/src/main/java/org/sonar/server/rule/RuleService.java index 3724b0cdba1..d6b2e694ddd 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/RuleService.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/RuleService.java @@ -45,10 +45,12 @@ public class RuleService implements ServerComponent { private final RuleIndex index; private final DbClient db; + private final RuleUpdater ruleUpdater; - public RuleService(RuleIndex index, DbClient db) { + public RuleService(RuleIndex index, DbClient db, RuleUpdater ruleUpdater) { this.index = index; this.db = db; + this.ruleUpdater = ruleUpdater; } @CheckForNull @@ -124,6 +126,10 @@ public class RuleService implements ServerComponent { } } + public void update(RuleUpdate update) { + ruleUpdater.update(update, UserSession.get()); + } + private void checkAdminPermission(UserSession userSession) { userSession.checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN); } diff --git a/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdate.java b/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdate.java new file mode 100644 index 00000000000..601d775f0f9 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdate.java @@ -0,0 +1,122 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.rule; + +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.debt.DebtRemediationFunction; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import java.util.Set; + +public class RuleUpdate { + + public static final String DEFAULT_DEBT_CHARACTERISTIC = "_default"; + + private final RuleKey ruleKey; + + private boolean changeTags = false, changeMarkdownNote = false, changeDebtSubCharacteristic = false, changeDebtRemediationFunction = false; + private Set tags; + private String markdownNote; + private String debtSubCharacteristicKey; + private DebtRemediationFunction debtRemediationFunction; + + public RuleUpdate(RuleKey ruleKey) { + this.ruleKey = ruleKey; + } + + public RuleKey getRuleKey() { + return ruleKey; + } + + @CheckForNull + public Set getTags() { + return tags; + } + + /** + * Set to null or empty set to remove existing tags. + */ + public RuleUpdate setTags(@Nullable Set tags) { + this.tags = tags; + this.changeTags = true; + return this; + } + + @CheckForNull + public String getMarkdownNote() { + return markdownNote; + } + + /** + * Set to null or empty to remove existing note. + */ + public RuleUpdate setMarkdownNote(@Nullable String s) { + this.markdownNote = s; + this.changeMarkdownNote = true; + return this; + } + + @CheckForNull + public String getDebtSubCharacteristicKey() { + return debtSubCharacteristicKey; + } + + /** + * Set to null to force the characteristic "NONE". Set to value of {@link #DEFAULT_DEBT_CHARACTERISTIC} + * to reset to default characteristic. + */ + public RuleUpdate setDebtSubCharacteristic(@Nullable String c) { + this.debtSubCharacteristicKey = c; + this.changeDebtSubCharacteristic = true; + return this; + } + + @CheckForNull + public DebtRemediationFunction getDebtRemediationFunction() { + return debtRemediationFunction; + } + + public RuleUpdate setDebtRemediationFunction(@Nullable DebtRemediationFunction fn) { + this.debtRemediationFunction = fn; + this.changeDebtRemediationFunction = true; + return this; + } + + public boolean isChangeTags() { + return changeTags; + } + + public boolean isChangeMarkdownNote() { + return changeMarkdownNote; + } + + public boolean isChangeDebtSubCharacteristic() { + return changeDebtSubCharacteristic; + } + + public boolean isChangeDebtRemediationFunction() { + return changeDebtRemediationFunction; + } + + public boolean isEmpty() { + return !changeMarkdownNote && !changeTags && !changeDebtSubCharacteristic && !changeDebtRemediationFunction; + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdater.java b/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdater.java new file mode 100644 index 00000000000..f825a2d1eec --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdater.java @@ -0,0 +1,208 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.rule; + +import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.sonar.api.ServerComponent; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.utils.System2; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.rule.RuleDto; +import org.sonar.core.technicaldebt.db.CharacteristicDto; +import org.sonar.server.db.DbClient; +import org.sonar.server.user.UserSession; + +import java.util.Collections; +import java.util.Date; + +public class RuleUpdater implements ServerComponent { + + private final DbClient dbClient; + private final System2 system; + + public RuleUpdater(DbClient dbClient, System2 system) { + this.dbClient = dbClient; + this.system = system; + } + + public boolean update(RuleUpdate update, UserSession userSession) { + userSession.checkLoggedIn(); + userSession.checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN); + + if (update.isEmpty()) { + return false; + } + + DbSession dbSession = dbClient.openSession(false); + try { + Context context = newContext(update); + // validate only the changes, not all the rule fields + apply(update, context, userSession); + dbClient.ruleDao().update(dbSession, context.rule); + dbSession.commit(); + return true; + + } finally { + dbSession.close(); + } + } + + /** + * Load all the DTOs required for validating changes and updating rule + */ + private Context newContext(RuleUpdate change) { + DbSession dbSession = dbClient.openSession(false); + try { + Context context = new Context(); + context.rule = dbClient.ruleDao().getNonNullByKey(dbSession, change.getRuleKey()); + + if (change.getDebtSubCharacteristicKey() != null && + !change.getDebtSubCharacteristicKey().equals(RuleUpdate.DEFAULT_DEBT_CHARACTERISTIC)) { + CharacteristicDto characteristicDto = dbClient.debtCharacteristicDao().selectByKey(change.getDebtSubCharacteristicKey(), dbSession); + if (characteristicDto == null) { + throw new IllegalArgumentException("Unknown debt sub-characteristic: " + change.getDebtSubCharacteristicKey()); + } + if (!characteristicDto.isEnabled()) { + throw new IllegalArgumentException("Debt sub-characteristic is disabled: " + change.getDebtSubCharacteristicKey()); + } + if (characteristicDto.getParentId() == null) { + throw new IllegalArgumentException("Not a sub-characteristic: " + change.getDebtSubCharacteristicKey()); + } + context.newCharacteristic = characteristicDto; + } + return context; + + } finally { + dbSession.close(); + } + } + + private void apply(RuleUpdate update, Context context, UserSession userSession) { + if (update.isChangeMarkdownNote()) { + updateMarkdownNote(update, context, userSession); + } + if (update.isChangeTags()) { + updateTags(update, context); + } + if (update.isChangeDebtSubCharacteristic()) { + updateDebtSubCharacteristic(update, context); + } + // order is important -> sub-characteristic must be set + if (update.isChangeDebtRemediationFunction()) { + updateDebtRemediationFunction(update, context); + } + } + + private void updateTags(RuleUpdate update, Context context) { + if (update.getTags() == null || update.getTags().isEmpty()) { + context.rule.setTags(Collections.emptySet()); + } else { + RuleTagHelper.applyTags(context.rule, update.getTags()); + } + } + + private void updateDebtSubCharacteristic(RuleUpdate update, Context context) { + if (update.getDebtSubCharacteristicKey() == null) { + // set to "none" + Integer id = context.rule.getDefaultSubCharacteristicId() != null ? RuleDto.DISABLED_CHARACTERISTIC_ID : null; + context.rule.setSubCharacteristicId(id); + context.rule.setRemediationFunction(null); + context.rule.setRemediationCoefficient(null); + context.rule.setRemediationOffset(null); + + } else if (update.getDebtSubCharacteristicKey().equals(RuleUpdate.DEFAULT_DEBT_CHARACTERISTIC)) { + // reset to default + context.rule.setSubCharacteristicId(null); + context.rule.setRemediationFunction(null); + context.rule.setRemediationCoefficient(null); + context.rule.setRemediationOffset(null); + + } else { + if (ObjectUtils.equals(context.newCharacteristic.getId(), context.rule.getDefaultSubCharacteristicId())) { + // reset to default -> compatibility with SQALE + context.rule.setSubCharacteristicId(null); + context.rule.setRemediationFunction(null); + context.rule.setRemediationCoefficient(null); + context.rule.setRemediationOffset(null); + } else { + // override default + context.rule.setSubCharacteristicId(context.newCharacteristic.getId()); + } + } + } + + private void updateDebtRemediationFunction(RuleUpdate update, Context context) { + boolean noChar = + (context.rule.getDefaultSubCharacteristicId() == null && context.rule.getSubCharacteristicId() == null) || + (context.rule.getSubCharacteristicId() != null && context.rule.getSubCharacteristicId().intValue() == RuleDto.DISABLED_CHARACTERISTIC_ID); + + if (noChar || update.getDebtRemediationFunction()==null) { + context.rule.setRemediationFunction(null); + context.rule.setRemediationCoefficient(null); + context.rule.setRemediationOffset(null); + } else { + if (isSameAsDefaultFunction(update.getDebtRemediationFunction(), context.rule)) { + // reset to default + context.rule.setRemediationFunction(null); + context.rule.setRemediationCoefficient(null); + context.rule.setRemediationOffset(null); + } else { + context.rule.setRemediationFunction(update.getDebtRemediationFunction().type().name()); + context.rule.setRemediationCoefficient(update.getDebtRemediationFunction().coefficient()); + context.rule.setRemediationOffset(update.getDebtRemediationFunction().offset()); + } + } + } + + private void updateMarkdownNote(RuleUpdate update, Context context, UserSession userSession) { + if (StringUtils.isBlank(update.getMarkdownNote())) { + context.rule.setNoteData(null); + context.rule.setNoteCreatedAt(null); + context.rule.setNoteUpdatedAt(null); + context.rule.setNoteUserLogin(null); + } else { + Date now = new Date(system.now()); + context.rule.setNoteData(update.getMarkdownNote()); + context.rule.setNoteCreatedAt(context.rule.getNoteCreatedAt() != null ? context.rule.getNoteCreatedAt() : now); + context.rule.setNoteUpdatedAt(now); + context.rule.setNoteUserLogin(userSession.login()); + } + } + + private static boolean isSameAsDefaultFunction(DebtRemediationFunction fn, RuleDto rule) { + return new EqualsBuilder() + .append(fn.type().name(), rule.getDefaultRemediationFunction()) + .append(fn.coefficient(), rule.getDefaultRemediationCoefficient()) + .append(fn.offset(), rule.getDefaultRemediationOffset()) + .isEquals(); + } + + /** + * Data loaded before update + */ + private static class Context { + private RuleDto rule; + private CharacteristicDto newCharacteristic; + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java b/sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java new file mode 100644 index 00000000000..e577c3b670f --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule/ws/UpdateAction.java @@ -0,0 +1,64 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.rule.ws; + +import com.google.common.collect.Sets; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.RequestHandler; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.server.rule.RuleUpdate; + +import java.util.List; +import java.util.Set; + +public class UpdateAction implements RequestHandler { + + void define(WebService.NewController controller) { + WebService.NewAction action = controller + .createAction("update") + .setDescription("Update an existing rule") + .setSince("4.4") + .setHandler(this); + + action.createParam("rule_key") + .setRequired(true) + .setDescription("Key of the rule to update") + .setExampleValue("javascript:NullCheck"); + + action.createParam("tags") + .setDescription("Optional comma-separated list of tags to set. Use empty value to remove all current tags.") + .setExampleValue("java8,security"); + + action.createParam("markdown_note") + .setDescription("Optional note in markdown format. Use empty value to remove current note.") + .setExampleValue("my *note*"); + + action.createParam("debt_sub_characteristic") + .setDescription("Optional key of the new sub-characteristic to set. Use empty value to unset (-> none) or '" + + RuleUpdate.DEFAULT_DEBT_CHARACTERISTIC + "' to revert to default sub-characteristic .") + .setExampleValue("FAULT_TOLERANCE"); + } + + @Override + public void handle(Request request, Response response) throws Exception { + } +} diff --git a/sonar-server/src/main/resources/org/sonar/server/platform/logback.xml b/sonar-server/src/main/resources/org/sonar/server/platform/logback.xml index a4a640e59b1..684564cdce6 100644 --- a/sonar-server/src/main/resources/org/sonar/server/platform/logback.xml +++ b/sonar-server/src/main/resources/org/sonar/server/platform/logback.xml @@ -74,13 +74,13 @@ - + - + - + diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexMediumTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexMediumTest.java index 57eaad9753c..012fa0a984e 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexMediumTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexMediumTest.java @@ -29,16 +29,13 @@ import org.sonar.api.rule.Severity; import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.check.Cardinality; import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.MyBatis; import org.sonar.core.qualityprofile.db.ActiveRuleDto; import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; -import org.sonar.core.qualityprofile.db.QualityProfileDao; import org.sonar.core.qualityprofile.db.QualityProfileDto; import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleParamDto; +import org.sonar.server.db.DbClient; import org.sonar.server.qualityprofile.ActiveRule; -import org.sonar.server.qualityprofile.persistence.ActiveRuleDao; -import org.sonar.server.rule.db.RuleDao; import org.sonar.server.tester.ServerTester; import java.util.Collection; @@ -51,17 +48,14 @@ public class ActiveRuleIndexMediumTest { @ClassRule public static ServerTester tester = new ServerTester(); - MyBatis myBatis = tester.get(MyBatis.class); - QualityProfileDao qualityProfileDao = tester.get(QualityProfileDao.class); - ActiveRuleDao activeRuleDao = tester.get(ActiveRuleDao.class); - RuleDao dao = tester.get(RuleDao.class); + DbClient db = tester.get(DbClient.class); ActiveRuleIndex index = tester.get(ActiveRuleIndex.class); DbSession dbSession; @Before public void before() { tester.clearDbAndEs(); - dbSession = myBatis.openSession(false); + dbSession = db.openSession(false); } @After @@ -74,28 +68,27 @@ public class ActiveRuleIndexMediumTest { QualityProfileDto profileDto = new QualityProfileDto() .setName("myprofile") .setLanguage("java"); - qualityProfileDao.insert(dbSession, profileDto); + db.qualityProfileDao().insert(dbSession, profileDto); // insert db RuleKey ruleKey = RuleKey.of("javascript", "S001"); RuleDto ruleDto = newRuleDto(ruleKey); - dao.insert(dbSession, ruleDto); + db.ruleDao().insert(dbSession, ruleDto); ActiveRuleDto activeRule = ActiveRuleDto.createFor(profileDto, ruleDto) .setInheritance(ActiveRule.Inheritance.INHERIT.name()) .setSeverity(Severity.BLOCKER); - activeRuleDao.insert(dbSession, activeRule); + db.activeRuleDao().insert(dbSession, activeRule); dbSession.commit(); // verify that activeRules are persisted in db - List persistedDtos = activeRuleDao.findByRule(ruleDto, dbSession); + List persistedDtos = db.activeRuleDao().findByRule(ruleDto, dbSession); assertThat(persistedDtos).hasSize(1); // verify that activeRules are indexed in es - ActiveRule hit = index.getByKey(activeRule.getKey()); assertThat(hit).isNotNull(); @@ -110,41 +103,41 @@ public class ActiveRuleIndexMediumTest { QualityProfileDto profileDto = new QualityProfileDto() .setName("myprofile") .setLanguage("java"); - qualityProfileDao.insert(dbSession, profileDto); + db.qualityProfileDao().insert(dbSession, profileDto); // insert db RuleKey ruleKey = RuleKey.of("javascript", "S001"); RuleDto ruleDto = newRuleDto(ruleKey); - dao.insert(dbSession, ruleDto); + db.ruleDao().insert(dbSession, ruleDto); RuleParamDto minParam = new RuleParamDto() .setName("min") .setType("STRING"); - dao.addRuleParam(ruleDto, minParam, dbSession); + db.ruleDao().addRuleParam(ruleDto, minParam, dbSession); RuleParamDto maxParam = new RuleParamDto() .setName("max") .setType("STRING"); - dao.addRuleParam(ruleDto, maxParam, dbSession); + db.ruleDao().addRuleParam(ruleDto, maxParam, dbSession); ActiveRuleDto activeRule = ActiveRuleDto.createFor(profileDto, ruleDto) .setInheritance(ActiveRule.Inheritance.INHERIT.name()) .setSeverity(Severity.BLOCKER); - activeRuleDao.insert(dbSession, activeRule); + db.activeRuleDao().insert(dbSession, activeRule); ActiveRuleParamDto activeRuleMinParam = ActiveRuleParamDto.createFor(minParam) .setValue("minimum"); - activeRuleDao.addParam(activeRule, activeRuleMinParam, dbSession); + db.activeRuleDao().addParam(activeRule, activeRuleMinParam, dbSession); ActiveRuleParamDto activeRuleMaxParam = ActiveRuleParamDto.createFor(maxParam) .setValue("maximum"); - activeRuleDao.addParam(activeRule, activeRuleMaxParam, dbSession); + db.activeRuleDao().addParam(activeRule, activeRuleMaxParam, dbSession); dbSession.commit(); // verify that activeRulesParams are persisted in db - List persistedDtos = activeRuleDao.findParamsByActiveRule(activeRule, dbSession); + List persistedDtos = db.activeRuleDao().findParamsByActiveRule(activeRule, dbSession); assertThat(persistedDtos).hasSize(2); // verify that activeRulesParams are indexed in es @@ -152,8 +145,8 @@ public class ActiveRuleIndexMediumTest { ActiveRule rule = index.getByKey(activeRule.getKey()); assertThat(rule.params()).hasSize(2); - assertThat(rule.params().keySet()).containsOnly("min","max"); - assertThat(rule.params().values()).containsOnly("minimum","maximum"); + assertThat(rule.params().keySet()).containsOnly("min", "max"); + assertThat(rule.params().values()).containsOnly("minimum", "maximum"); assertThat(rule.params().get("min")).isEqualTo("minimum"); } @@ -161,16 +154,16 @@ public class ActiveRuleIndexMediumTest { public void find_activeRules() throws InterruptedException { QualityProfileDto profile1 = QualityProfileDto.createFor("p1", "java"); QualityProfileDto profile2 = QualityProfileDto.createFor("p2", "java"); - qualityProfileDao.insert(dbSession, profile1); - qualityProfileDao.insert(dbSession, profile2); + db.qualityProfileDao().insert(dbSession, profile1); + db.qualityProfileDao().insert(dbSession, profile2); // insert db RuleDto ruleDto = newRuleDto(RuleKey.of("javascript", "S001")); - dao.insert(dbSession, ruleDto); + db.ruleDao().insert(dbSession, ruleDto); // insert db RuleDto ruleDto2 = newRuleDto(RuleKey.of("javascript", "S002")); - dao.insert(dbSession, ruleDto2); + db.ruleDao().insert(dbSession, ruleDto2); ActiveRuleDto find1 = ActiveRuleDto.createFor(profile1, ruleDto) .setInheritance(ActiveRule.Inheritance.INHERIT.name()) @@ -184,15 +177,15 @@ public class ActiveRuleIndexMediumTest { .setInheritance(ActiveRule.Inheritance.INHERIT.name()) .setSeverity(Severity.BLOCKER); - activeRuleDao.insert(dbSession, find1); - activeRuleDao.insert(dbSession, find2); - activeRuleDao.insert(dbSession, notFound); + db.activeRuleDao().insert(dbSession, find1); + db.activeRuleDao().insert(dbSession, find2); + db.activeRuleDao().insert(dbSession, notFound); dbSession.commit(); // verify that activeRules are persisted in db - List persistedDtos = activeRuleDao.findByRule(ruleDto, dbSession); + List persistedDtos = db.activeRuleDao().findByRule(ruleDto, dbSession); assertThat(persistedDtos).hasSize(2); - persistedDtos = activeRuleDao.findByRule(ruleDto2, dbSession); + persistedDtos = db.activeRuleDao().findByRule(ruleDto2, dbSession); assertThat(persistedDtos).hasSize(1); // verify that activeRules are indexed in es @@ -213,14 +206,14 @@ public class ActiveRuleIndexMediumTest { QualityProfileDto profileDto2 = new QualityProfileDto() .setName("P2") .setLanguage("java"); - qualityProfileDao.insert(dbSession, profileDto); - qualityProfileDao.insert(dbSession, profileDto2); + db.qualityProfileDao().insert(dbSession, profileDto); + db.qualityProfileDao().insert(dbSession, profileDto2); // insert db RuleDto rule1 = newRuleDto(RuleKey.of("javascript", "S001")); - dao.insert(dbSession, rule1); RuleDto rule2 = newRuleDto(RuleKey.of("javascript", "S002")); - dao.insert(dbSession, rule2); + db.ruleDao().insert(dbSession, rule1); + db.ruleDao().insert(dbSession, rule2); ActiveRuleDto onP1 = ActiveRuleDto.createFor(profileDto, rule1) .setInheritance(ActiveRule.Inheritance.INHERIT.name()) @@ -234,15 +227,15 @@ public class ActiveRuleIndexMediumTest { .setInheritance(ActiveRule.Inheritance.INHERIT.name()) .setSeverity(Severity.BLOCKER); - activeRuleDao.insert(dbSession, onP1); - activeRuleDao.insert(dbSession, firstOnP1); - activeRuleDao.insert(dbSession, firstOnP2); + db.activeRuleDao().insert(dbSession, onP1); + db.activeRuleDao().insert(dbSession, firstOnP1); + db.activeRuleDao().insert(dbSession, firstOnP2); dbSession.commit(); // verify that activeRules are persisted in db - List persistedDtos = activeRuleDao.findByRule(rule1, dbSession); + List persistedDtos = db.activeRuleDao().findByRule(rule1, dbSession); assertThat(persistedDtos).hasSize(2); - persistedDtos = activeRuleDao.findByRule(rule2, dbSession); + persistedDtos = db.activeRuleDao().findByRule(rule2, dbSession); assertThat(persistedDtos).hasSize(1); // verify that activeRules are indexed in es diff --git a/sonar-server/src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java b/sonar-server/src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java deleted file mode 100644 index b64b784c193..00000000000 --- a/sonar-server/src/test/java/org/sonar/server/rule/RubyRuleServiceTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.rule; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import org.sonar.api.rule.RuleKey; - -import java.util.Map; - -import static com.google.common.collect.Maps.newHashMap; -import static org.mockito.Mockito.verify; - -@RunWith(MockitoJUnitRunner.class) -public class RubyRuleServiceTest { - - @Mock - RuleService service; - - RubyRuleService rubyService; - - @Before - public void setUp() throws Exception { - rubyService = new RubyRuleService(service); - } - - @Test - public void update_rule() { - Map params = newHashMap(); - params.put("ruleKey", "squid:UselessImportCheck"); - params.put("debtCharacteristicKey", "MODULARITY"); - params.put("debtRemediationFunction", "LINEAR_OFFSET"); - params.put("debtRemediationCoefficient", "1h"); - params.put("debtRemediationOffset", "10min"); - -// rubyService.updateRule(params); -// ArgumentCaptor ruleChangeCaptor = ArgumentCaptor.forClass(RuleOperations.RuleChange.class); -// verify(rules).updateRule(ruleChangeCaptor.capture()); -// -// RuleOperations.RuleChange ruleChange = ruleChangeCaptor.getValue(); -// assertThat(ruleChange.ruleKey()).isEqualTo(RuleKey.of("squid", "UselessImportCheck")); -// assertThat(ruleChange.debtCharacteristicKey()).isEqualTo("MODULARITY"); -// assertThat(ruleChange.debtRemediationFunction()).isEqualTo("LINEAR_OFFSET"); -// assertThat(ruleChange.debtRemediationCoefficient()).isEqualTo("1h"); -// assertThat(ruleChange.debtRemediationOffset()).isEqualTo("10min"); - } - - @Test - public void find_by_key() { - rubyService.findByKey("repo:key"); - verify(service).getByKey(RuleKey.of("repo", "key")); - } - - @Test - public void just_for_fun_and_coverage() throws Exception { - rubyService.start(); - rubyService.stop(); - // do not fail - } -} diff --git a/sonar-server/src/test/java/org/sonar/server/rule/RuleTests.java b/sonar-server/src/test/java/org/sonar/server/rule/RuleTests.java new file mode 100644 index 00000000000..fde6afa56fb --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/rule/RuleTests.java @@ -0,0 +1,54 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.rule; + +import com.google.common.collect.ImmutableSet; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleStatus; +import org.sonar.api.rule.Severity; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.check.Cardinality; +import org.sonar.core.rule.RuleDto; + +public class RuleTests { + + public static RuleDto newDto(RuleKey ruleKey) { + return new RuleDto() + .setRuleKey(ruleKey.rule()) + .setRepositoryKey(ruleKey.repository()) + .setName("Rule " + ruleKey.rule()) + .setDescription("Description " + ruleKey.rule()) + .setStatus(RuleStatus.READY.toString()) + .setConfigKey("InternalKey" + ruleKey.rule()) + .setSeverity(Severity.INFO) + .setCardinality(Cardinality.SINGLE) + .setTags(ImmutableSet.of("tag1", "tag2")) + .setSystemTags(ImmutableSet.of("systag1", "systag2")) + .setLanguage("js") + .setRemediationFunction(DebtRemediationFunction.Type.LINEAR.toString()) + .setDefaultRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.toString()) + .setRemediationCoefficient("1h") + .setDefaultRemediationCoefficient("5d") + .setRemediationOffset("5min") + .setDefaultRemediationOffset("10h") + .setEffortToFixDescription(ruleKey.repository() + "." + ruleKey.rule() + ".effortToFix"); + } + +} diff --git a/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java b/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java new file mode 100644 index 00000000000..e826a25c949 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java @@ -0,0 +1,356 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.rule; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; +import org.sonar.api.utils.System2; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.rule.RuleDto; +import org.sonar.core.technicaldebt.db.CharacteristicDto; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.UnauthorizedException; +import org.sonar.server.rule.index.RuleIndex; +import org.sonar.server.tester.ServerTester; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.user.UserSession; + +import java.util.Set; + +import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; +import static org.mockito.Mockito.mock; + +public class RuleUpdaterMediumTest { + + private static final RuleKey RULE_KEY = RuleKey.of("squid", "S001"); + + @ClassRule + public static ServerTester tester = new ServerTester(); + + DbClient db = tester.get(DbClient.class); + DbSession dbSession; + System2 system = mock(System2.class); + RuleUpdater updater = new RuleUpdater(db, system); + int reliabilityId, softReliabilityId, hardReliabilityId; + + @Before + public void before() { + tester.clearDbAndEs(); + dbSession = db.openSession(false); + } + + @After + public void after() { + dbSession.close(); + } + + @Test + public void no_changes() throws Exception { + grantPermission(); + db.ruleDao().insert(dbSession, RuleTests.newDto(RULE_KEY) + // the following fields are not supposed to be updated + .setNoteData("my *note*") + .setNoteUserLogin("me") + .setTags(ImmutableSet.of("tag1")) + .setSubCharacteristicId(33) + .setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name()) + .setRemediationCoefficient("1d") + .setRemediationOffset("5min")); + dbSession.commit(); + + RuleUpdate update = new RuleUpdate(RULE_KEY); + assertThat(update.isEmpty()).isTrue(); + updater.update(update, UserSession.get()); + + dbSession.clearCache(); + RuleDto rule = db.ruleDao().getByKey(dbSession, RULE_KEY); + assertThat(rule.getNoteData()).isEqualTo("my *note*"); + assertThat(rule.getNoteUserLogin()).isEqualTo("me"); + assertThat(rule.getTags()).containsOnly("tag1"); + assertThat(rule.getSubCharacteristicId()).isEqualTo(33); + assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE.name()); + assertThat(rule.getRemediationCoefficient()).isEqualTo("1d"); + assertThat(rule.getRemediationOffset()).isEqualTo("5min"); + } + + @Test + public void set_markdown_note() throws Exception { + grantPermission(); + db.ruleDao().insert(dbSession, RuleTests.newDto(RULE_KEY) + .setNoteData(null) + .setNoteUserLogin(null) + + // the following fields are not supposed to be updated + .setTags(ImmutableSet.of("tag1")) + .setSubCharacteristicId(33) + .setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name()) + .setRemediationCoefficient("1d") + .setRemediationOffset("5min")); + dbSession.commit(); + + RuleUpdate update = new RuleUpdate(RULE_KEY); + update.setMarkdownNote("my *note*"); + updater.update(update, UserSession.get()); + + dbSession.clearCache(); + RuleDto rule = db.ruleDao().getByKey(dbSession, RULE_KEY); + assertThat(rule.getNoteData()).isEqualTo("my *note*"); + assertThat(rule.getNoteUserLogin()).isEqualTo("me"); + assertThat(rule.getNoteCreatedAt()).isNotNull(); + assertThat(rule.getNoteUpdatedAt()).isNotNull(); + // no other changes + assertThat(rule.getTags()).containsOnly("tag1"); + assertThat(rule.getSubCharacteristicId()).isEqualTo(33); + assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE.name()); + assertThat(rule.getRemediationCoefficient()).isEqualTo("1d"); + assertThat(rule.getRemediationOffset()).isEqualTo("5min"); + } + + @Test + public void delete_markdown_note() throws Exception { + grantPermission(); + db.ruleDao().insert(dbSession, RuleTests.newDto(RULE_KEY) + .setNoteData("my *note*") + .setNoteUserLogin("me")); + dbSession.commit(); + + RuleUpdate update = new RuleUpdate(RULE_KEY).setMarkdownNote(null); + updater.update(update, UserSession.get()); + + dbSession.clearCache(); + RuleDto rule = db.ruleDao().getByKey(dbSession, RULE_KEY); + assertThat(rule.getNoteData()).isNull(); + assertThat(rule.getNoteUserLogin()).isNull(); + assertThat(rule.getNoteCreatedAt()).isNull(); + assertThat(rule.getNoteUpdatedAt()).isNull(); + } + + @Test + public void set_tags() throws Exception { + grantPermission(); + // insert db + db.ruleDao().insert(dbSession, RuleTests.newDto(RULE_KEY) + .setTags(Sets.newHashSet("security")) + .setSystemTags(Sets.newHashSet("java8", "javadoc")) + ); + dbSession.commit(); + + // java8 is a system tag -> ignore + RuleUpdate update = new RuleUpdate(RULE_KEY).setTags(Sets.newHashSet("bug", "java8")); + updater.update(update, UserSession.get()); + + dbSession.clearCache(); + RuleDto rule = db.ruleDao().getByKey(dbSession, RULE_KEY); + assertThat(rule.getTags()).containsOnly("bug"); + assertThat(rule.getSystemTags()).containsOnly("java8", "javadoc"); + + // verify that tags are indexed in index + Set tags = tester.get(RuleService.class).listTags(); + assertThat(tags).containsOnly("bug", "java8", "javadoc"); + } + + @Test + public void remove_tags() throws Exception { + grantPermission(); + db.ruleDao().insert(dbSession, RuleTests.newDto(RULE_KEY) + .setTags(Sets.newHashSet("security")) + .setSystemTags(Sets.newHashSet("java8", "javadoc"))); + dbSession.commit(); + + RuleUpdate update = new RuleUpdate(RULE_KEY).setTags(null); + updater.update(update, UserSession.get()); + + dbSession.clearCache(); + RuleDto rule = db.ruleDao().getByKey(dbSession, RULE_KEY); + assertThat(rule.getTags()).isEmpty(); + assertThat(rule.getSystemTags()).containsOnly("java8", "javadoc"); + + // verify that tags are indexed in index + Set tags = tester.get(RuleService.class).listTags(); + assertThat(tags).containsOnly("java8", "javadoc"); + } + + @Test + public void fail_to_update_if_not_granted() throws Exception { + MockUserSession.set().setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); + + db.ruleDao().insert(dbSession, RuleTests.newDto(RULE_KEY) + .setTags(Sets.newHashSet("security")) + .setSystemTags(Sets.newHashSet("java8", "javadoc"))); + dbSession.commit(); + + RuleUpdater updater = new RuleUpdater(tester.get(DbClient.class), system); + RuleUpdate update = new RuleUpdate(RULE_KEY).setMarkdownNote("my *note*"); + + try { + updater.update(update, UserSession.get()); + fail(); + } catch (UnauthorizedException e) { + // ok + } + } + + @Test + public void override_debt() throws Exception { + grantPermission(); + insertDebtCharacteristics(dbSession); + db.ruleDao().insert(dbSession, RuleTests.newDto(RULE_KEY) + .setDefaultSubCharacteristicId(hardReliabilityId) + .setDefaultRemediationFunction(DebtRemediationFunction.Type.LINEAR.name()) + .setDefaultRemediationCoefficient("1d") + .setDefaultRemediationOffset("5min") + .setRemediationFunction(null) + .setRemediationCoefficient(null) + .setRemediationOffset(null)); + dbSession.commit(); + + DefaultDebtRemediationFunction fn = new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE, null, "1min"); + RuleUpdate update = new RuleUpdate(RULE_KEY) + .setDebtSubCharacteristic("SOFT_RELIABILITY") + .setDebtRemediationFunction(fn); + updater.update(update, UserSession.get()); + + // verify db + dbSession.clearCache(); + RuleDto rule = db.ruleDao().getByKey(dbSession, RULE_KEY); + assertThat(rule.getSubCharacteristicId()).isEqualTo(softReliabilityId); + assertThat(rule.getRemediationFunction()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE.name()); + assertThat(rule.getRemediationCoefficient()).isNull(); + assertThat(rule.getRemediationOffset()).isEqualTo("1min"); + + // verify index + Rule indexedRule = tester.get(RuleIndex.class).getByKey(RULE_KEY); + assertThat(indexedRule.debtCharacteristicKey()).isEqualTo("RELIABILITY"); + assertThat(indexedRule.debtSubCharacteristicKey()).isEqualTo("SOFT_RELIABILITY"); + assertThat(indexedRule.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE); + assertThat(indexedRule.debtRemediationFunction().coefficient()).isNull(); + assertThat(indexedRule.debtRemediationFunction().offset()).isEqualTo("1min"); + } + + @Test + public void reset_debt() throws Exception { + grantPermission(); + insertDebtCharacteristics(dbSession); + db.ruleDao().insert(dbSession, RuleTests.newDto(RULE_KEY) + .setDefaultSubCharacteristicId(hardReliabilityId) + .setDefaultRemediationFunction(DebtRemediationFunction.Type.LINEAR.name()) + .setDefaultRemediationCoefficient("1d") + .setDefaultRemediationOffset("5min") + .setSubCharacteristicId(softReliabilityId) + .setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name()) + .setRemediationCoefficient(null) + .setRemediationOffset("1min")); + dbSession.commit(); + + RuleUpdate update = new RuleUpdate(RULE_KEY) + .setDebtSubCharacteristic(RuleUpdate.DEFAULT_DEBT_CHARACTERISTIC); + updater.update(update, UserSession.get()); + + // verify db + dbSession.clearCache(); + RuleDto rule = db.ruleDao().getByKey(dbSession, RULE_KEY); + assertThat(rule.getSubCharacteristicId()).isNull(); + assertThat(rule.getRemediationFunction()).isNull(); + assertThat(rule.getRemediationCoefficient()).isNull(); + assertThat(rule.getRemediationOffset()).isNull(); + + // verify index + Rule indexedRule = tester.get(RuleIndex.class).getByKey(RULE_KEY); + assertThat(indexedRule.debtCharacteristicKey()).isEqualTo("RELIABILITY"); + assertThat(indexedRule.debtSubCharacteristicKey()).isEqualTo("HARD_RELIABILITY"); + assertThat(indexedRule.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR); + assertThat(indexedRule.debtRemediationFunction().coefficient()).isEqualTo("1d"); + assertThat(indexedRule.debtRemediationFunction().offset()).isEqualTo("5min"); + } + + @Test + public void unset_debt() throws Exception { + grantPermission(); + insertDebtCharacteristics(dbSession); + db.ruleDao().insert(dbSession, RuleTests.newDto(RULE_KEY) + .setDefaultSubCharacteristicId(hardReliabilityId) + .setDefaultRemediationFunction(DebtRemediationFunction.Type.LINEAR.name()) + .setDefaultRemediationCoefficient("1d") + .setDefaultRemediationOffset("5min") + .setSubCharacteristicId(softReliabilityId) + .setRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE.name()) + .setRemediationCoefficient(null) + .setRemediationOffset("1min")); + dbSession.commit(); + + RuleUpdate update = new RuleUpdate(RULE_KEY) + .setDebtSubCharacteristic(null); + updater.update(update, UserSession.get()); + + // verify db + dbSession.clearCache(); + RuleDto rule = db.ruleDao().getByKey(dbSession, RULE_KEY); + assertThat(rule.getSubCharacteristicId()).isEqualTo(-1); + assertThat(rule.getRemediationFunction()).isNull(); + assertThat(rule.getRemediationCoefficient()).isNull(); + assertThat(rule.getRemediationOffset()).isNull(); + + // verify index + Rule indexedRule = tester.get(RuleIndex.class).getByKey(RULE_KEY); + assertThat(indexedRule.debtCharacteristicKey()).isNull(); + assertThat(indexedRule.debtSubCharacteristicKey()).isNull(); + //TODO pb with db code -1 ? assertThat(indexedRule.debtRemediationFunction()).isNull(); + } + + private void grantPermission() { + MockUserSession.set() + .setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN) + .setLogin("me"); + } + + private void insertDebtCharacteristics(DbSession dbSession) { + CharacteristicDto reliability = new CharacteristicDto() + .setKey("RELIABILITY") + .setName("Reliability") + .setEnabled(true); + db.debtCharacteristicDao().insert(reliability, dbSession); + reliabilityId = reliability.getId(); + + CharacteristicDto softReliability = new CharacteristicDto() + .setKey("SOFT_RELIABILITY") + .setName("Software reliability") + .setParentId(reliability.getId()) + .setEnabled(true); + db.debtCharacteristicDao().insert(softReliability, dbSession); + softReliabilityId = softReliability.getId(); + + CharacteristicDto hardReliability = new CharacteristicDto() + .setKey("HARD_RELIABILITY") + .setName("Hardware reliability") + .setParentId(reliability.getId()) + .setEnabled(true); + db.debtCharacteristicDao().insert(hardReliability, dbSession); + hardReliabilityId = hardReliability.getId(); + } +} -- 2.39.5