From 45d303431198988fdde0c32e3c5c861bb792cba4 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Lievremont Date: Thu, 23 Jan 2014 14:54:34 +0100 Subject: SONAR-4326 Continue separating .rule from .qualityprofile --- .../java/org/sonar/server/platform/Platform.java | 5 +- .../server/qualityprofile/ActiveRuleDocument.java | 45 +++ .../sonar/server/qualityprofile/ESActiveRule.java | 1 - .../server/qualityprofile/ProfileRuleQuery.java | 282 +++++++++++++++ .../sonar/server/qualityprofile/QProfileRule.java | 1 - .../server/qualityprofile/QProfileRuleLookup.java | 2 - .../qualityprofile/QProfileRuleOperations.java | 321 ----------------- .../org/sonar/server/qualityprofile/QProfiles.java | 6 +- .../org/sonar/server/rule/ActiveRuleDocument.java | 45 --- .../org/sonar/server/rule/ProfileRuleQuery.java | 283 --------------- .../java/org/sonar/server/rule/RuleOperations.java | 321 +++++++++++++++++ .../qualityprofile/ProfileRuleQueryTest.java | 150 ++++++++ .../qualityprofile/QProfileRuleLookupTest.java | 1 - .../qualityprofile/QProfileRuleOperationsTest.java | 378 -------------------- .../sonar/server/qualityprofile/QProfilesTest.java | 4 +- .../sonar/server/rule/ProfileRuleQueryTest.java | 148 -------- .../org/sonar/server/rule/RuleOperationsTest.java | 381 +++++++++++++++++++++ 17 files changed, 1187 insertions(+), 1187 deletions(-) create mode 100644 sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleDocument.java create mode 100644 sonar-server/src/main/java/org/sonar/server/qualityprofile/ProfileRuleQuery.java delete mode 100644 sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRuleOperations.java delete mode 100644 sonar-server/src/main/java/org/sonar/server/rule/ActiveRuleDocument.java delete mode 100644 sonar-server/src/main/java/org/sonar/server/rule/ProfileRuleQuery.java create mode 100644 sonar-server/src/main/java/org/sonar/server/rule/RuleOperations.java create mode 100644 sonar-server/src/test/java/org/sonar/server/qualityprofile/ProfileRuleQueryTest.java delete mode 100644 sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRuleOperationsTest.java delete mode 100644 sonar-server/src/test/java/org/sonar/server/rule/ProfileRuleQueryTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/rule/RuleOperationsTest.java diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index 7555aeec380..1a1eb76af72 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -19,8 +19,9 @@ */ package org.sonar.server.platform; -import org.sonar.server.qualityprofile.QProfileRuleLookup; +import org.sonar.server.rule.RuleOperations; +import org.sonar.server.qualityprofile.QProfileRuleLookup; import org.apache.commons.configuration.BaseConfiguration; import org.slf4j.LoggerFactory; import org.sonar.api.config.EmailSettings; @@ -282,7 +283,7 @@ public final class Platform { servicesContainer.addSingleton(QProfileLookup.class); servicesContainer.addSingleton(QProfileOperations.class); servicesContainer.addSingleton(QProfileActiveRuleOperations.class); - servicesContainer.addSingleton(QProfileRuleOperations.class); + servicesContainer.addSingleton(RuleOperations.class); servicesContainer.addSingleton(QProfileProjectOperations.class); servicesContainer.addSingleton(QProfileProjectLookup.class); servicesContainer.addSingleton(QProfileBackup.class); diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleDocument.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleDocument.java new file mode 100644 index 00000000000..4450b0bc309 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleDocument.java @@ -0,0 +1,45 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.qualityprofile; + + +public class ActiveRuleDocument { + + public static final String FIELD_ID = "id"; + public static final String FIELD_SEVERITY = "severity"; + public static final String FIELD_PROFILE_ID = "profileId"; + public static final String FIELD_INHERITANCE = "inheritance"; + public static final String FIELD_ACTIVE_RULE_PARENT_ID = "activeRuleParentId"; + public static final String FIELD_PARAMS = "params"; + + public static final String FIELD_NOTE = "note"; + public static final String FIELD_NOTE_DATA = "data"; + public static final String FIELD_NOTE_USER_LOGIN = "userLogin"; + public static final String FIELD_NOTE_CREATED_AT = "createdAt"; + public static final String FIELD_NOTE_UPDATED_AT = "updatedAt"; + + public static final String FIELD_PARAM_KEY = "key"; + public static final String FIELD_PARAM_VALUE = "value"; + + private ActiveRuleDocument() { + // Only constants + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/ESActiveRule.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ESActiveRule.java index eafacdd4748..f57aafbd232 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/ESActiveRule.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ESActiveRule.java @@ -24,7 +24,6 @@ import org.elasticsearch.index.query.FilterBuilders; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; -import org.sonar.server.rule.ActiveRuleDocument; import org.sonar.server.rule.RuleDocument; import com.google.common.base.Function; import com.google.common.collect.Iterables; diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/ProfileRuleQuery.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ProfileRuleQuery.java new file mode 100644 index 00000000000..e8003d1b52c --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ProfileRuleQuery.java @@ -0,0 +1,282 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.qualityprofile; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.util.RubyUtils; + +import javax.annotation.CheckForNull; + +import java.util.*; + +import static com.google.common.collect.Lists.newArrayList; + +/** + * @since 4.2 + */ +public class ProfileRuleQuery { + + public static final String SORT_BY_CREATION_DATE = "SORT_BY_CREATION_DATE"; + public static final String SORT_BY_RULE_NAME = "SORT_BY_RULE_NAME"; + public static final Set SORTS = ImmutableSet.of(SORT_BY_CREATION_DATE, SORT_BY_RULE_NAME); + + private static final String INHERITANCE_ANY = "any"; + private static final String INHERITANCE_NOT = "NOT"; + private static final Set AUTHORIZED_INHERITANCE_PARAMS = ImmutableSet.of(QProfileRule.INHERITED, QProfileRule.OVERRIDES, INHERITANCE_ANY, INHERITANCE_NOT); + + private static final String PARAM_PROFILE_ID = "profileId"; + private static final String PARAM_LANGUAGE = "language"; + private static final String PARAM_NAME_OR_KEY = "nameOrKey"; + private static final String PARAM_REPOSITORY_KEYS = "repositoryKeys"; + private static final String PARAM_SEVERITIES = "severities"; + private static final String PARAM_STATUSES = "statuses"; + private static final String PARAM_TAGS = "tags"; + private static final String PARAM_INHERITANCE = "inheritance"; + private static final String PARAM_SORT = "sort_by"; + private static final String PARAM_ASC = "asc"; + + private int profileId; + private String language; + private String nameOrKey; + private List repositoryKeys; + private List severities; + private List statuses; + private List tags; + private String inheritance; + private boolean anyInheritance; + private boolean noInheritance; + + private String sort; + private boolean asc; + + private ProfileRuleQuery() { + repositoryKeys = Lists.newArrayList(); + severities = Lists.newArrayList(); + statuses = Lists.newArrayList(); + tags = Lists.newArrayList(); + sort = SORT_BY_RULE_NAME; + asc = true; + } + + public static ProfileRuleQuery parse(Map params) { + List errors = newArrayList(); + + validatePresenceOf(params, errors, PARAM_PROFILE_ID); + + ProfileRuleQuery result = new ProfileRuleQuery(); + + if (params.containsKey(PARAM_LANGUAGE)) { + result.setLanguage((String) params.get(PARAM_LANGUAGE)); + } + if (params.containsKey(PARAM_NAME_OR_KEY)) { + result.setNameOrKey((String) params.get(PARAM_NAME_OR_KEY)); + } + if (params.get(PARAM_REPOSITORY_KEYS) != null) { + result.addRepositoryKeys(optionalVarargs(params.get(PARAM_REPOSITORY_KEYS))); + } + if (params.get(PARAM_SEVERITIES) != null) { + result.addSeverities(optionalVarargs(params.get(PARAM_SEVERITIES))); + } + if (params.get(PARAM_STATUSES) != null) { + result.addStatuses(optionalVarargs(params.get(PARAM_STATUSES))); + } + if (params.get(PARAM_TAGS) != null) { + result.addTags(optionalVarargs(params.get(PARAM_TAGS))); + } + + if (params.containsKey(PARAM_INHERITANCE)) { + String inheritance = (String) params.get(PARAM_INHERITANCE); + validateInheritance(inheritance, errors); + if (inheritance.equals(INHERITANCE_ANY)) { + result.setAnyInheritance(true); + } else if (inheritance.equals(INHERITANCE_NOT)) { + result.setNoInheritance(true); + } else { + result.setInheritance(inheritance); + } + } else { + result.setAnyInheritance(true); + } + + if (params.get(PARAM_SORT) != null) { + String sort = (String) params.get(PARAM_SORT); + Boolean asc = RubyUtils.toBoolean(params.get(PARAM_ASC)); + validateSort(sort, errors); + result.setSort(sort); + if (asc != null) { + result.setAsc(asc); + } + } + + if (!errors.isEmpty()) { + throw BadRequestException.of("Incorrect rule search parameters", errors); + } else { + result.profileId = RubyUtils.toInteger(params.get(PARAM_PROFILE_ID)); + } + return result; + } + + private static void validatePresenceOf(Map params, List errors, String... paramNames) { + for (String param : paramNames) { + if (params.get(param) == null) { + errors.add(BadRequestException.Message.of("Missing parameter " + param)); + } + } + } + + private static void validateInheritance(String value, List errors) { + if (!AUTHORIZED_INHERITANCE_PARAMS.contains(value)) { + errors.add(BadRequestException.Message.of("Wrong inheritance param, should be one of " + AUTHORIZED_INHERITANCE_PARAMS)); + } + } + + private static void validateSort(String sort, List errors) { + if (!SORTS.contains(sort)) { + errors.add(BadRequestException.Message.of("Bad sort field, should be one of " + SORTS)); + } + } + + public static ProfileRuleQuery create(int profileId) { + ProfileRuleQuery newQuery = new ProfileRuleQuery(); + newQuery.profileId = profileId; + return newQuery; + } + + public ProfileRuleQuery setNameOrKey(String nameOrKey) { + this.nameOrKey = nameOrKey; + return this; + } + + public ProfileRuleQuery setLanguage(String language) { + this.language = language; + return this; + } + + public ProfileRuleQuery addRepositoryKeys(String... repositoryKeys) { + this.repositoryKeys.addAll(Arrays.asList(repositoryKeys)); + return this; + } + + public ProfileRuleQuery addSeverities(String... severities) { + this.severities.addAll(Arrays.asList(severities)); + return this; + } + + public ProfileRuleQuery addStatuses(String... statuses) { + this.statuses.addAll(Arrays.asList(statuses)); + return this; + } + + public ProfileRuleQuery addTags(String... tags) { + this.tags.addAll(Arrays.asList(tags)); + return this; + } + + public ProfileRuleQuery setInheritance(String inheritance) { + this.inheritance = inheritance; + return this; + } + + public ProfileRuleQuery setAnyInheritance(boolean anyInheritance) { + this.anyInheritance = anyInheritance; + return this; + } + + public ProfileRuleQuery setNoInheritance(boolean noInheritance) { + this.noInheritance = noInheritance; + return this; + } + + public ProfileRuleQuery setSort(String sort) { + this.sort = sort; + return this; + } + + public ProfileRuleQuery setAsc(boolean asc) { + this.asc = asc; + return this; + } + + public int profileId() { + return profileId; + } + + @CheckForNull + public String language() { + return language; + } + + @CheckForNull + public String nameOrKey() { + return nameOrKey; + } + + public Collection repositoryKeys() { + return ImmutableList.copyOf(repositoryKeys); + } + + public Collection severities() { + return ImmutableList.copyOf(severities); + } + + public Collection statuses() { + return ImmutableList.copyOf(statuses); + } + + public Collection tags() { + return ImmutableList.copyOf(tags); + } + + @CheckForNull + public String inheritance() { + return inheritance; + } + + public boolean anyInheritance() { + return anyInheritance; + } + + public boolean noInheritance() { + return noInheritance; + } + + public String sort() { + return sort; + } + + public boolean asc() { + return asc; + } + + private static String[] optionalVarargs(Object jRubyArray) { + List items = RubyUtils.toStrings(jRubyArray); + String[] empty = new String[0]; + if (items == null) { + return empty; + } else { + items.remove(""); + return items.toArray(empty); + } + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRule.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRule.java index 1b22e99c01f..4679f150795 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRule.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRule.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.collect.Maps; import org.elasticsearch.common.joda.time.format.ISODateTimeFormat; import org.sonar.api.server.rule.RuleParamType; import org.sonar.check.Cardinality; -import org.sonar.server.rule.ActiveRuleDocument; import org.sonar.server.rule.RuleDocument; import javax.annotation.CheckForNull; diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRuleLookup.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRuleLookup.java index 4f0b3c3bca5..8a7677d64e7 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRuleLookup.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRuleLookup.java @@ -35,8 +35,6 @@ import org.elasticsearch.search.sort.SortOrder; import org.sonar.api.ServerExtension; import org.sonar.api.rules.Rule; import org.sonar.server.es.ESIndex; -import org.sonar.server.rule.ActiveRuleDocument; -import org.sonar.server.rule.ProfileRuleQuery; import org.sonar.server.rule.RuleDocument; import javax.annotation.CheckForNull; diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRuleOperations.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRuleOperations.java deleted file mode 100644 index 75e2fa7c068..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRuleOperations.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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.qualityprofile; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Strings; -import com.google.common.collect.Iterables; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import org.apache.commons.lang.StringUtils; -import org.apache.ibatis.session.SqlSession; -import org.sonar.api.ServerComponent; -import org.sonar.api.rules.Rule; -import org.sonar.api.utils.System2; -import org.sonar.check.Cardinality; -import org.sonar.core.permission.GlobalPermissions; -import org.sonar.core.persistence.MyBatis; -import org.sonar.core.qualityprofile.db.ActiveRuleDao; -import org.sonar.core.qualityprofile.db.ActiveRuleDto; -import org.sonar.core.rule.RuleDao; -import org.sonar.core.rule.RuleDto; -import org.sonar.core.rule.RuleParamDto; -import org.sonar.core.rule.RuleRuleTagDto; -import org.sonar.core.rule.RuleTagDao; -import org.sonar.core.rule.RuleTagType; -import org.sonar.server.exceptions.BadRequestException; -import org.sonar.server.exceptions.NotFoundException; -import org.sonar.server.rule.RuleRegistry; -import org.sonar.server.rule.RuleTagOperations; -import org.sonar.server.user.UserSession; - -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static com.google.common.collect.Lists.newArrayList; - -public class QProfileRuleOperations implements ServerComponent { - - private final MyBatis myBatis; - private final ActiveRuleDao activeRuleDao; - private final RuleDao ruleDao; - private final RuleTagDao ruleTagDao; - private final RuleTagOperations ruleTagOperations; - private final ESActiveRule esActiveRule; - private final RuleRegistry ruleRegistry; - - private final System2 system; - - public QProfileRuleOperations(MyBatis myBatis, ActiveRuleDao activeRuleDao, RuleDao ruleDao, RuleTagDao ruleTagDao, RuleTagOperations ruleTagOperations, - ESActiveRule esActiveRule, RuleRegistry ruleRegistry) { - this(myBatis, activeRuleDao, ruleDao, ruleTagDao, ruleTagOperations, esActiveRule, ruleRegistry, System2.INSTANCE); - } - - @VisibleForTesting - QProfileRuleOperations(MyBatis myBatis, ActiveRuleDao activeRuleDao, RuleDao ruleDao, RuleTagDao ruleTagDao, RuleTagOperations ruleTagOperations, ESActiveRule esActiveRule, - RuleRegistry ruleRegistry, System2 system) { - this.myBatis = myBatis; - this.activeRuleDao = activeRuleDao; - this.ruleDao = ruleDao; - this.ruleTagDao = ruleTagDao; - this.ruleTagOperations = ruleTagOperations; - this.esActiveRule = esActiveRule; - this.ruleRegistry = ruleRegistry; - this.system = system; - } - - public void updateRuleNote(RuleDto rule, String note, UserSession userSession) { - checkPermission(userSession); - Date now = new Date(system.now()); - - SqlSession session = myBatis.openSession(); - try { - if (rule.getNoteData() == null) { - rule.setNoteCreatedAt(now); - rule.setNoteUserLogin(getLoggedLogin(userSession)); - } - rule.setNoteUpdatedAt(now); - rule.setNoteData(note); - ruleDao.update(rule); - session.commit(); - - reindexRule(rule, session); - } finally { - MyBatis.closeQuietly(session); - } - } - - public void deleteRuleNote(RuleDto rule, UserSession userSession) { - checkPermission(userSession); - - SqlSession session = myBatis.openSession(); - try { - rule.setNoteData(null); - rule.setNoteUserLogin(null); - rule.setNoteCreatedAt(null); - rule.setNoteUpdatedAt(null); - ruleDao.update(rule); - session.commit(); - - reindexRule(rule, session); - } finally { - MyBatis.closeQuietly(session); - } - } - - public RuleDto createRule(RuleDto templateRule, String name, String severity, String description, Map paramsByKey, - UserSession userSession) { - checkPermission(userSession); - SqlSession session = myBatis.openSession(); - try { - RuleDto rule = new RuleDto() - .setParentId(templateRule.getId()) - .setName(name) - .setDescription(description) - .setSeverity(severity) - .setRepositoryKey(templateRule.getRepositoryKey()) - .setConfigKey(templateRule.getConfigKey()) - .setRuleKey(templateRule.getRuleKey() + "_" + system.now()) - .setCardinality(Cardinality.SINGLE) - .setStatus(Rule.STATUS_READY) - .setLanguage(templateRule.getLanguage()) - .setCreatedAt(new Date(system.now())) - .setUpdatedAt(new Date(system.now())); - ruleDao.insert(rule, session); - - List templateRuleParams = ruleDao.selectParameters(templateRule.getId(), session); - List ruleParams = newArrayList(); - for (RuleParamDto templateRuleParam : templateRuleParams) { - String key = templateRuleParam.getName(); - String value = paramsByKey.get(key); - - RuleParamDto param = new RuleParamDto() - .setRuleId(rule.getId()) - .setName(key) - .setDescription(templateRuleParam.getDescription()) - .setType(templateRuleParam.getType()) - .setDefaultValue(Strings.emptyToNull(value)); - ruleDao.insert(param, session); - ruleParams.add(param); - } - - List templateRuleTags = ruleDao.selectTags(templateRule.getId(), session); - List ruleTags = newArrayList(); - for (RuleRuleTagDto tag: templateRuleTags) { - RuleRuleTagDto newTag = new RuleRuleTagDto() - .setRuleId(rule.getId()) - .setTagId(tag.getTagId()) - .setTag(tag.getTag()) - .setType(tag.getType()); - ruleDao.insert(newTag, session); - ruleTags.add(newTag); - } - - session.commit(); - reindexRule(rule, ruleParams, ruleTags); - return rule; - } finally { - MyBatis.closeQuietly(session); - } - } - - public void updateRule(RuleDto rule, String name, String severity, String description, Map paramsByKey, - UserSession userSession) { - checkPermission(userSession); - SqlSession session = myBatis.openSession(); - try { - rule.setName(name) - .setDescription(description) - .setSeverity(severity) - .setUpdatedAt(new Date(system.now())); - ruleDao.update(rule, session); - - List ruleParams = ruleDao.selectParameters(rule.getId(), session); - for (RuleParamDto ruleParam : ruleParams) { - String value = paramsByKey.get(ruleParam.getName()); - ruleParam.setDefaultValue(Strings.emptyToNull(value)); - ruleDao.update(ruleParam, session); - } - List ruleTags = ruleDao.selectTags(rule.getId(), session); - session.commit(); - reindexRule(rule, ruleParams, ruleTags); - } finally { - MyBatis.closeQuietly(session); - } - } - - public void deleteRule(RuleDto rule, UserSession userSession) { - checkPermission(userSession); - SqlSession session = myBatis.openSession(); - try { - // Set status REMOVED on rule - rule.setStatus(Rule.STATUS_REMOVED) - .setUpdatedAt(new Date(system.now())); - ruleDao.update(rule, session); - session.commit(); - reindexRule(rule, session); - - // Delete all active rules and active rule params linked to the rule - List activeRules = activeRuleDao.selectByRuleId(rule.getId()); - for (ActiveRuleDto activeRule : activeRules) { - activeRuleDao.deleteParameters(activeRule.getId(), session); - } - activeRuleDao.deleteFromRule(rule.getId(), session); - session.commit(); - esActiveRule.deleteActiveRules(newArrayList(Iterables.transform(activeRules, new Function() { - @Override - public Integer apply(ActiveRuleDto input) { - return input.getId(); - } - }))); - } finally { - MyBatis.closeQuietly(session); - } - } - - public void updateTags(RuleDto rule, List newTags, UserSession userSession) { - checkPermission(userSession); - SqlSession session = myBatis.openSession(); - try { - Map neededTagIds = validateAndGetTagIds(newTags, session); - - final Integer ruleId = rule.getId(); - - boolean ruleChanged = synchronizeTags(ruleId, newTags, neededTagIds, session); - if (ruleChanged) { - rule.setUpdatedAt(new Date(system.now())); - ruleDao.update(rule, session); - session.commit(); - reindexRule(rule, session); - } - } finally { - MyBatis.closeQuietly(session); - } - } - - private Map validateAndGetTagIds(List newTags, SqlSession session) { - Map neededTagIds = Maps.newHashMap(); - Set unknownTags = Sets.newHashSet(); - for (String tag: newTags) { - Long tagId = ruleTagDao.selectId(tag, session); - if (tagId == null) { - unknownTags.add(tag); - } else { - neededTagIds.put(tag, tagId); - } - } - if (!unknownTags.isEmpty()) { - throw new NotFoundException("The following tags are unknown and must be created before association: " - + StringUtils.join(unknownTags, ", ")); - } - return neededTagIds; - } - - private boolean synchronizeTags(final Integer ruleId, List newTags, Map neededTagIds, SqlSession session) { - boolean ruleChanged = false; - - Set tagsToKeep = Sets.newHashSet(); - List currentTags = ruleDao.selectTags(ruleId, session); - for (RuleRuleTagDto existingTag: currentTags) { - if(existingTag.getType() == RuleTagType.ADMIN && !newTags.contains(existingTag.getTag())) { - ruleDao.deleteTag(existingTag, session); - ruleChanged = true; - } else { - tagsToKeep.add(existingTag.getTag()); - } - } - - for (String tag: newTags) { - if (! tagsToKeep.contains(tag)) { - ruleDao.insert(new RuleRuleTagDto().setRuleId(ruleId).setTagId(neededTagIds.get(tag)).setType(RuleTagType.ADMIN), session); - ruleChanged = true; - } - } - - ruleTagOperations.deleteUnusedTags(session); - - return ruleChanged; - } - - private void reindexRule(RuleDto rule, SqlSession session) { - reindexRule(rule, ruleDao.selectParameters(rule.getId(), session), ruleDao.selectTags(rule.getId(), session)); - } - - private void reindexRule(RuleDto rule, List ruleParams, List ruleTags) { - ruleRegistry.save(rule, ruleParams, ruleTags); - } - - private void checkPermission(UserSession userSession) { - userSession.checkLoggedIn(); - userSession.checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN); - } - - private String getLoggedLogin(UserSession userSession) { - String login = userSession.login(); - if (Strings.isNullOrEmpty(login)) { - throw new BadRequestException("User login can't be null"); - } - return login; - } -} diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java index a1b95bdd985..15399d5ab61 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java @@ -20,6 +20,7 @@ package org.sonar.server.qualityprofile; +import org.sonar.server.rule.RuleOperations; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import org.sonar.api.ServerComponent; @@ -36,7 +37,6 @@ import org.sonar.core.rule.RuleDao; import org.sonar.core.rule.RuleDto; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.NotFoundException; -import org.sonar.server.rule.ProfileRuleQuery; import org.sonar.server.user.UserSession; import org.sonar.server.util.RubyUtils; import org.sonar.server.util.Validation; @@ -68,12 +68,12 @@ public class QProfiles implements ServerComponent { private final QProfileLookup profileLookup; private final QProfileOperations operations; private final QProfileActiveRuleOperations activeRuleOperations; - private final QProfileRuleOperations ruleOperations; + private final RuleOperations ruleOperations; private final QProfileRuleLookup rules; public QProfiles(QualityProfileDao qualityProfileDao, ActiveRuleDao activeRuleDao, RuleDao ruleDao, ResourceDao resourceDao, QProfileProjectOperations projectOperations, QProfileProjectLookup projectLookup, QProfileBackup backup, QProfilePluginExporter exporter, - QProfileLookup profileLookup, QProfileOperations operations, QProfileActiveRuleOperations activeRuleOperations, QProfileRuleOperations ruleOperations, + QProfileLookup profileLookup, QProfileOperations operations, QProfileActiveRuleOperations activeRuleOperations, RuleOperations ruleOperations, QProfileRuleLookup rules) { this.qualityProfileDao = qualityProfileDao; this.activeRuleDao = activeRuleDao; diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ActiveRuleDocument.java b/sonar-server/src/main/java/org/sonar/server/rule/ActiveRuleDocument.java deleted file mode 100644 index 523a4249ae6..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/rule/ActiveRuleDocument.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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; - - -public class ActiveRuleDocument { - - public static final String FIELD_ID = "id"; - public static final String FIELD_SEVERITY = "severity"; - public static final String FIELD_PROFILE_ID = "profileId"; - public static final String FIELD_INHERITANCE = "inheritance"; - public static final String FIELD_ACTIVE_RULE_PARENT_ID = "activeRuleParentId"; - public static final String FIELD_PARAMS = "params"; - - public static final String FIELD_NOTE = "note"; - public static final String FIELD_NOTE_DATA = "data"; - public static final String FIELD_NOTE_USER_LOGIN = "userLogin"; - public static final String FIELD_NOTE_CREATED_AT = "createdAt"; - public static final String FIELD_NOTE_UPDATED_AT = "updatedAt"; - - public static final String FIELD_PARAM_KEY = "key"; - public static final String FIELD_PARAM_VALUE = "value"; - - private ActiveRuleDocument() { - // Only constants - } - -} diff --git a/sonar-server/src/main/java/org/sonar/server/rule/ProfileRuleQuery.java b/sonar-server/src/main/java/org/sonar/server/rule/ProfileRuleQuery.java deleted file mode 100644 index 463959bc1a0..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/rule/ProfileRuleQuery.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; -import org.sonar.server.exceptions.BadRequestException; -import org.sonar.server.qualityprofile.QProfileRule; -import org.sonar.server.util.RubyUtils; - -import javax.annotation.CheckForNull; - -import java.util.*; - -import static com.google.common.collect.Lists.newArrayList; - -/** - * @since 4.2 - */ -public class ProfileRuleQuery { - - public static final String SORT_BY_CREATION_DATE = "SORT_BY_CREATION_DATE"; - public static final String SORT_BY_RULE_NAME = "SORT_BY_RULE_NAME"; - public static final Set SORTS = ImmutableSet.of(SORT_BY_CREATION_DATE, SORT_BY_RULE_NAME); - - private static final String INHERITANCE_ANY = "any"; - private static final String INHERITANCE_NOT = "NOT"; - private static final Set AUTHORIZED_INHERITANCE_PARAMS = ImmutableSet.of(QProfileRule.INHERITED, QProfileRule.OVERRIDES, INHERITANCE_ANY, INHERITANCE_NOT); - - private static final String PARAM_PROFILE_ID = "profileId"; - private static final String PARAM_LANGUAGE = "language"; - private static final String PARAM_NAME_OR_KEY = "nameOrKey"; - private static final String PARAM_REPOSITORY_KEYS = "repositoryKeys"; - private static final String PARAM_SEVERITIES = "severities"; - private static final String PARAM_STATUSES = "statuses"; - private static final String PARAM_TAGS = "tags"; - private static final String PARAM_INHERITANCE = "inheritance"; - private static final String PARAM_SORT = "sort_by"; - private static final String PARAM_ASC = "asc"; - - private int profileId; - private String language; - private String nameOrKey; - private List repositoryKeys; - private List severities; - private List statuses; - private List tags; - private String inheritance; - private boolean anyInheritance; - private boolean noInheritance; - - private String sort; - private boolean asc; - - private ProfileRuleQuery() { - repositoryKeys = Lists.newArrayList(); - severities = Lists.newArrayList(); - statuses = Lists.newArrayList(); - tags = Lists.newArrayList(); - sort = SORT_BY_RULE_NAME; - asc = true; - } - - public static ProfileRuleQuery parse(Map params) { - List errors = newArrayList(); - - validatePresenceOf(params, errors, PARAM_PROFILE_ID); - - ProfileRuleQuery result = new ProfileRuleQuery(); - - if (params.containsKey(PARAM_LANGUAGE)) { - result.setLanguage((String) params.get(PARAM_LANGUAGE)); - } - if (params.containsKey(PARAM_NAME_OR_KEY)) { - result.setNameOrKey((String) params.get(PARAM_NAME_OR_KEY)); - } - if (params.get(PARAM_REPOSITORY_KEYS) != null) { - result.addRepositoryKeys(optionalVarargs(params.get(PARAM_REPOSITORY_KEYS))); - } - if (params.get(PARAM_SEVERITIES) != null) { - result.addSeverities(optionalVarargs(params.get(PARAM_SEVERITIES))); - } - if (params.get(PARAM_STATUSES) != null) { - result.addStatuses(optionalVarargs(params.get(PARAM_STATUSES))); - } - if (params.get(PARAM_TAGS) != null) { - result.addTags(optionalVarargs(params.get(PARAM_TAGS))); - } - - if (params.containsKey(PARAM_INHERITANCE)) { - String inheritance = (String) params.get(PARAM_INHERITANCE); - validateInheritance(inheritance, errors); - if (inheritance.equals(INHERITANCE_ANY)) { - result.setAnyInheritance(true); - } else if (inheritance.equals(INHERITANCE_NOT)) { - result.setNoInheritance(true); - } else { - result.setInheritance(inheritance); - } - } else { - result.setAnyInheritance(true); - } - - if (params.get(PARAM_SORT) != null) { - String sort = (String) params.get(PARAM_SORT); - Boolean asc = RubyUtils.toBoolean(params.get(PARAM_ASC)); - validateSort(sort, errors); - result.setSort(sort); - if (asc != null) { - result.setAsc(asc); - } - } - - if (!errors.isEmpty()) { - throw BadRequestException.of("Incorrect rule search parameters", errors); - } else { - result.profileId = RubyUtils.toInteger(params.get(PARAM_PROFILE_ID)); - } - return result; - } - - private static void validatePresenceOf(Map params, List errors, String... paramNames) { - for (String param : paramNames) { - if (params.get(param) == null) { - errors.add(BadRequestException.Message.of("Missing parameter " + param)); - } - } - } - - private static void validateInheritance(String value, List errors) { - if (!AUTHORIZED_INHERITANCE_PARAMS.contains(value)) { - errors.add(BadRequestException.Message.of("Wrong inheritance param, should be one of " + AUTHORIZED_INHERITANCE_PARAMS)); - } - } - - private static void validateSort(String sort, List errors) { - if (!SORTS.contains(sort)) { - errors.add(BadRequestException.Message.of("Bad sort field, should be one of " + SORTS)); - } - } - - public static ProfileRuleQuery create(int profileId) { - ProfileRuleQuery newQuery = new ProfileRuleQuery(); - newQuery.profileId = profileId; - return newQuery; - } - - public ProfileRuleQuery setNameOrKey(String nameOrKey) { - this.nameOrKey = nameOrKey; - return this; - } - - public ProfileRuleQuery setLanguage(String language) { - this.language = language; - return this; - } - - public ProfileRuleQuery addRepositoryKeys(String... repositoryKeys) { - this.repositoryKeys.addAll(Arrays.asList(repositoryKeys)); - return this; - } - - public ProfileRuleQuery addSeverities(String... severities) { - this.severities.addAll(Arrays.asList(severities)); - return this; - } - - public ProfileRuleQuery addStatuses(String... statuses) { - this.statuses.addAll(Arrays.asList(statuses)); - return this; - } - - public ProfileRuleQuery addTags(String... tags) { - this.tags.addAll(Arrays.asList(tags)); - return this; - } - - public ProfileRuleQuery setInheritance(String inheritance) { - this.inheritance = inheritance; - return this; - } - - public ProfileRuleQuery setAnyInheritance(boolean anyInheritance) { - this.anyInheritance = anyInheritance; - return this; - } - - public ProfileRuleQuery setNoInheritance(boolean noInheritance) { - this.noInheritance = noInheritance; - return this; - } - - public ProfileRuleQuery setSort(String sort) { - this.sort = sort; - return this; - } - - public ProfileRuleQuery setAsc(boolean asc) { - this.asc = asc; - return this; - } - - public int profileId() { - return profileId; - } - - @CheckForNull - public String language() { - return language; - } - - @CheckForNull - public String nameOrKey() { - return nameOrKey; - } - - public Collection repositoryKeys() { - return ImmutableList.copyOf(repositoryKeys); - } - - public Collection severities() { - return ImmutableList.copyOf(severities); - } - - public Collection statuses() { - return ImmutableList.copyOf(statuses); - } - - public Collection tags() { - return ImmutableList.copyOf(tags); - } - - @CheckForNull - public String inheritance() { - return inheritance; - } - - public boolean anyInheritance() { - return anyInheritance; - } - - public boolean noInheritance() { - return noInheritance; - } - - public String sort() { - return sort; - } - - public boolean asc() { - return asc; - } - - private static String[] optionalVarargs(Object jRubyArray) { - List items = RubyUtils.toStrings(jRubyArray); - String[] empty = new String[0]; - if (items == null) { - return empty; - } else { - items.remove(""); - return items.toArray(empty); - } - } -} diff --git a/sonar-server/src/main/java/org/sonar/server/rule/RuleOperations.java b/sonar-server/src/main/java/org/sonar/server/rule/RuleOperations.java new file mode 100644 index 00000000000..68299d5db0c --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule/RuleOperations.java @@ -0,0 +1,321 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.server.qualityprofile.ESActiveRule; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Strings; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.apache.commons.lang.StringUtils; +import org.apache.ibatis.session.SqlSession; +import org.sonar.api.ServerComponent; +import org.sonar.api.rules.Rule; +import org.sonar.api.utils.System2; +import org.sonar.check.Cardinality; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.qualityprofile.db.ActiveRuleDao; +import org.sonar.core.qualityprofile.db.ActiveRuleDto; +import org.sonar.core.rule.RuleDao; +import org.sonar.core.rule.RuleDto; +import org.sonar.core.rule.RuleParamDto; +import org.sonar.core.rule.RuleRuleTagDto; +import org.sonar.core.rule.RuleTagDao; +import org.sonar.core.rule.RuleTagType; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.user.UserSession; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.google.common.collect.Lists.newArrayList; + +public class RuleOperations implements ServerComponent { + + private final MyBatis myBatis; + private final ActiveRuleDao activeRuleDao; + private final RuleDao ruleDao; + private final RuleTagDao ruleTagDao; + private final RuleTagOperations ruleTagOperations; + private final ESActiveRule esActiveRule; + private final RuleRegistry ruleRegistry; + + private final System2 system; + + public RuleOperations(MyBatis myBatis, ActiveRuleDao activeRuleDao, RuleDao ruleDao, RuleTagDao ruleTagDao, RuleTagOperations ruleTagOperations, + ESActiveRule esActiveRule, RuleRegistry ruleRegistry) { + this(myBatis, activeRuleDao, ruleDao, ruleTagDao, ruleTagOperations, esActiveRule, ruleRegistry, System2.INSTANCE); + } + + @VisibleForTesting + RuleOperations(MyBatis myBatis, ActiveRuleDao activeRuleDao, RuleDao ruleDao, RuleTagDao ruleTagDao, RuleTagOperations ruleTagOperations, ESActiveRule esActiveRule, + RuleRegistry ruleRegistry, System2 system) { + this.myBatis = myBatis; + this.activeRuleDao = activeRuleDao; + this.ruleDao = ruleDao; + this.ruleTagDao = ruleTagDao; + this.ruleTagOperations = ruleTagOperations; + this.esActiveRule = esActiveRule; + this.ruleRegistry = ruleRegistry; + this.system = system; + } + + public void updateRuleNote(RuleDto rule, String note, UserSession userSession) { + checkPermission(userSession); + Date now = new Date(system.now()); + + SqlSession session = myBatis.openSession(); + try { + if (rule.getNoteData() == null) { + rule.setNoteCreatedAt(now); + rule.setNoteUserLogin(getLoggedLogin(userSession)); + } + rule.setNoteUpdatedAt(now); + rule.setNoteData(note); + ruleDao.update(rule); + session.commit(); + + reindexRule(rule, session); + } finally { + MyBatis.closeQuietly(session); + } + } + + public void deleteRuleNote(RuleDto rule, UserSession userSession) { + checkPermission(userSession); + + SqlSession session = myBatis.openSession(); + try { + rule.setNoteData(null); + rule.setNoteUserLogin(null); + rule.setNoteCreatedAt(null); + rule.setNoteUpdatedAt(null); + ruleDao.update(rule); + session.commit(); + + reindexRule(rule, session); + } finally { + MyBatis.closeQuietly(session); + } + } + + public RuleDto createRule(RuleDto templateRule, String name, String severity, String description, Map paramsByKey, + UserSession userSession) { + checkPermission(userSession); + SqlSession session = myBatis.openSession(); + try { + RuleDto rule = new RuleDto() + .setParentId(templateRule.getId()) + .setName(name) + .setDescription(description) + .setSeverity(severity) + .setRepositoryKey(templateRule.getRepositoryKey()) + .setConfigKey(templateRule.getConfigKey()) + .setRuleKey(templateRule.getRuleKey() + "_" + system.now()) + .setCardinality(Cardinality.SINGLE) + .setStatus(Rule.STATUS_READY) + .setLanguage(templateRule.getLanguage()) + .setCreatedAt(new Date(system.now())) + .setUpdatedAt(new Date(system.now())); + ruleDao.insert(rule, session); + + List templateRuleParams = ruleDao.selectParameters(templateRule.getId(), session); + List ruleParams = newArrayList(); + for (RuleParamDto templateRuleParam : templateRuleParams) { + String key = templateRuleParam.getName(); + String value = paramsByKey.get(key); + + RuleParamDto param = new RuleParamDto() + .setRuleId(rule.getId()) + .setName(key) + .setDescription(templateRuleParam.getDescription()) + .setType(templateRuleParam.getType()) + .setDefaultValue(Strings.emptyToNull(value)); + ruleDao.insert(param, session); + ruleParams.add(param); + } + + List templateRuleTags = ruleDao.selectTags(templateRule.getId(), session); + List ruleTags = newArrayList(); + for (RuleRuleTagDto tag: templateRuleTags) { + RuleRuleTagDto newTag = new RuleRuleTagDto() + .setRuleId(rule.getId()) + .setTagId(tag.getTagId()) + .setTag(tag.getTag()) + .setType(tag.getType()); + ruleDao.insert(newTag, session); + ruleTags.add(newTag); + } + + session.commit(); + reindexRule(rule, ruleParams, ruleTags); + return rule; + } finally { + MyBatis.closeQuietly(session); + } + } + + public void updateRule(RuleDto rule, String name, String severity, String description, Map paramsByKey, + UserSession userSession) { + checkPermission(userSession); + SqlSession session = myBatis.openSession(); + try { + rule.setName(name) + .setDescription(description) + .setSeverity(severity) + .setUpdatedAt(new Date(system.now())); + ruleDao.update(rule, session); + + List ruleParams = ruleDao.selectParameters(rule.getId(), session); + for (RuleParamDto ruleParam : ruleParams) { + String value = paramsByKey.get(ruleParam.getName()); + ruleParam.setDefaultValue(Strings.emptyToNull(value)); + ruleDao.update(ruleParam, session); + } + List ruleTags = ruleDao.selectTags(rule.getId(), session); + session.commit(); + reindexRule(rule, ruleParams, ruleTags); + } finally { + MyBatis.closeQuietly(session); + } + } + + public void deleteRule(RuleDto rule, UserSession userSession) { + checkPermission(userSession); + SqlSession session = myBatis.openSession(); + try { + // Set status REMOVED on rule + rule.setStatus(Rule.STATUS_REMOVED) + .setUpdatedAt(new Date(system.now())); + ruleDao.update(rule, session); + session.commit(); + reindexRule(rule, session); + + // Delete all active rules and active rule params linked to the rule + List activeRules = activeRuleDao.selectByRuleId(rule.getId()); + for (ActiveRuleDto activeRule : activeRules) { + activeRuleDao.deleteParameters(activeRule.getId(), session); + } + activeRuleDao.deleteFromRule(rule.getId(), session); + session.commit(); + esActiveRule.deleteActiveRules(newArrayList(Iterables.transform(activeRules, new Function() { + @Override + public Integer apply(ActiveRuleDto input) { + return input.getId(); + } + }))); + } finally { + MyBatis.closeQuietly(session); + } + } + + public void updateTags(RuleDto rule, List newTags, UserSession userSession) { + checkPermission(userSession); + SqlSession session = myBatis.openSession(); + try { + Map neededTagIds = validateAndGetTagIds(newTags, session); + + final Integer ruleId = rule.getId(); + + boolean ruleChanged = synchronizeTags(ruleId, newTags, neededTagIds, session); + if (ruleChanged) { + rule.setUpdatedAt(new Date(system.now())); + ruleDao.update(rule, session); + session.commit(); + reindexRule(rule, session); + } + } finally { + MyBatis.closeQuietly(session); + } + } + + private Map validateAndGetTagIds(List newTags, SqlSession session) { + Map neededTagIds = Maps.newHashMap(); + Set unknownTags = Sets.newHashSet(); + for (String tag: newTags) { + Long tagId = ruleTagDao.selectId(tag, session); + if (tagId == null) { + unknownTags.add(tag); + } else { + neededTagIds.put(tag, tagId); + } + } + if (!unknownTags.isEmpty()) { + throw new NotFoundException("The following tags are unknown and must be created before association: " + + StringUtils.join(unknownTags, ", ")); + } + return neededTagIds; + } + + private boolean synchronizeTags(final Integer ruleId, List newTags, Map neededTagIds, SqlSession session) { + boolean ruleChanged = false; + + Set tagsToKeep = Sets.newHashSet(); + List currentTags = ruleDao.selectTags(ruleId, session); + for (RuleRuleTagDto existingTag: currentTags) { + if(existingTag.getType() == RuleTagType.ADMIN && !newTags.contains(existingTag.getTag())) { + ruleDao.deleteTag(existingTag, session); + ruleChanged = true; + } else { + tagsToKeep.add(existingTag.getTag()); + } + } + + for (String tag: newTags) { + if (! tagsToKeep.contains(tag)) { + ruleDao.insert(new RuleRuleTagDto().setRuleId(ruleId).setTagId(neededTagIds.get(tag)).setType(RuleTagType.ADMIN), session); + ruleChanged = true; + } + } + + ruleTagOperations.deleteUnusedTags(session); + + return ruleChanged; + } + + private void reindexRule(RuleDto rule, SqlSession session) { + reindexRule(rule, ruleDao.selectParameters(rule.getId(), session), ruleDao.selectTags(rule.getId(), session)); + } + + private void reindexRule(RuleDto rule, List ruleParams, List ruleTags) { + ruleRegistry.save(rule, ruleParams, ruleTags); + } + + private void checkPermission(UserSession userSession) { + userSession.checkLoggedIn(); + userSession.checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN); + } + + private String getLoggedLogin(UserSession userSession) { + String login = userSession.login(); + if (Strings.isNullOrEmpty(login)) { + throw new BadRequestException("User login can't be null"); + } + return login; + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/ProfileRuleQueryTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/ProfileRuleQueryTest.java new file mode 100644 index 00000000000..62650676bb7 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/ProfileRuleQueryTest.java @@ -0,0 +1,150 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.qualityprofile; + +import org.sonar.server.qualityprofile.ProfileRuleQuery; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import org.junit.Test; +import org.sonar.server.exceptions.BadRequestException; + +import java.util.Map; + +import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; + +public class ProfileRuleQueryTest { + + @Test + public void create_basic_query() { + final int profileId = 42; + ProfileRuleQuery query = ProfileRuleQuery.create(profileId); + assertThat(query.profileId()).isEqualTo(profileId); + } + + @Test + public void parse_nominal_request() { + final int profileId = 42; + Map params = ImmutableMap.of("profileId", (Object) Integer.toString(profileId)); + ProfileRuleQuery query = ProfileRuleQuery.parse(params); + assertThat(query.profileId()).isEqualTo(profileId); + assertThat(query.anyInheritance()).isTrue(); + } + + @Test + public void fail_on_missing_profileId() { + Map params = ImmutableMap.of(); + try { + ProfileRuleQuery.parse(params); + fail("Expected BadRequestException"); + } catch(BadRequestException bre) { + assertThat(bre.errors().get(0).text()).isEqualTo("Missing parameter profileId"); + } + } + + @Test + public void fail_on_incorrect_profileId() { + Map params = ImmutableMap.of("profileId", (Object) "not an integer"); + try { + ProfileRuleQuery.parse(params); + fail("Expected BadRequestException"); + } catch(Exception e) { + assertThat(e).isInstanceOf(NumberFormatException.class); + } + } + + @Test + public void parse_with_inheritance() { + final int profileId = 42; + Map params = ImmutableMap.of( + "profileId", (Object) Integer.toString(profileId), + "inheritance", "OVERRIDES" + ); + ProfileRuleQuery query = ProfileRuleQuery.parse(params); + assertThat(query.profileId()).isEqualTo(profileId); + assertThat(query.inheritance()).isEqualTo("OVERRIDES"); + } + + @Test + public void fail_on_incorrect_inheritance() { + Map params = ImmutableMap.of("profileId", (Object) Integer.toString(42), "inheritance", "bad value"); + try { + ProfileRuleQuery.parse(params); + fail(); + } catch(Exception e) { + assertThat(e).isInstanceOf(BadRequestException.class); + } + } + + @Test + public void parse_with_sort() { + ProfileRuleQuery query = ProfileRuleQuery.parse(ImmutableMap.of("profileId", (Object) Integer.toString(42), + "sort_by", ProfileRuleQuery.SORT_BY_RULE_NAME, "asc", "true")); + assertThat(query.sort()).isEqualTo(ProfileRuleQuery.SORT_BY_RULE_NAME); + assertThat(query.asc()).isTrue(); + + query = ProfileRuleQuery.parse(ImmutableMap.of("profileId", (Object) Integer.toString(42), + "sort_by", ProfileRuleQuery.SORT_BY_RULE_NAME, "asc", "false")); + assertThat(query.sort()).isEqualTo(ProfileRuleQuery.SORT_BY_RULE_NAME); + assertThat(query.asc()).isFalse(); + + // Default sort + query = ProfileRuleQuery.parse(ImmutableMap.of("profileId", (Object) Integer.toString(42))); + assertThat(query.sort()).isEqualTo(ProfileRuleQuery.SORT_BY_RULE_NAME); + assertThat(query.asc()).isTrue(); + } + + @Test + public void fail_on_incorrect_sort() { + Map params = ImmutableMap.of("profileId", (Object) Integer.toString(42), "sort_by", "bad sort"); + try { + ProfileRuleQuery.parse(params); + fail(); + } catch(Exception e) { + assertThat(e).isInstanceOf(BadRequestException.class); + } + } + + @Test + public void should_parse_all_parameters() { + final int profileId = 42; + final String language = "xoo"; + final String nameOrKey = "unused parameter"; + Map params = ImmutableMap. builder() + .put("profileId", (Object) Integer.toString(profileId)) + .put("language", (Object) language) + .put("nameOrKey", (Object) nameOrKey) + .put("repositoryKeys", (Object) Lists.newArrayList("", "repo1", "repo2")) + .put("severities", new Object()) + .put("statuses", (Object) Lists.newArrayList("", "BETA", "DEPRECATED")) + .put("tags", (Object) Lists.newArrayList("", "tag1", "tag2")) + .build(); + ProfileRuleQuery query = ProfileRuleQuery.parse(params); + assertThat(query.profileId()).isEqualTo(profileId); + assertThat(query.language()).isEqualTo(language); + assertThat(query.nameOrKey()).isEqualTo(nameOrKey); + assertThat(query.repositoryKeys()).containsOnly("repo1", "repo2"); + assertThat(query.severities()).isEmpty(); + assertThat(query.statuses()).containsOnly("BETA", "DEPRECATED"); + assertThat(query.tags()).containsOnly("tag1", "tag2"); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRuleLookupTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRuleLookupTest.java index 8194cd8a0fa..1a7c7a1c873 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRuleLookupTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRuleLookupTest.java @@ -31,7 +31,6 @@ import org.sonar.api.rule.Severity; import org.sonar.core.profiling.Profiling; import org.sonar.server.es.ESIndex; import org.sonar.server.es.ESNode; -import org.sonar.server.rule.ProfileRuleQuery; import org.sonar.server.rule.RuleRegistry; import org.sonar.test.TestUtils; diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRuleOperationsTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRuleOperationsTest.java deleted file mode 100644 index 9bc796d2f4e..00000000000 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileRuleOperationsTest.java +++ /dev/null @@ -1,378 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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.qualityprofile; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import org.apache.ibatis.session.SqlSession; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.runners.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; -import org.sonar.api.rule.Severity; -import org.sonar.api.rules.Rule; -import org.sonar.api.utils.DateUtils; -import org.sonar.api.utils.System2; -import org.sonar.check.Cardinality; -import org.sonar.core.permission.GlobalPermissions; -import org.sonar.core.persistence.MyBatis; -import org.sonar.core.qualityprofile.db.ActiveRuleDao; -import org.sonar.core.qualityprofile.db.ActiveRuleDto; -import org.sonar.core.rule.RuleDao; -import org.sonar.core.rule.RuleDto; -import org.sonar.core.rule.RuleParamDto; -import org.sonar.core.rule.RuleRuleTagDto; -import org.sonar.core.rule.RuleTagDao; -import org.sonar.core.rule.RuleTagType; -import org.sonar.server.exceptions.ForbiddenException; -import org.sonar.server.exceptions.NotFoundException; -import org.sonar.server.rule.RuleRegistry; -import org.sonar.server.rule.RuleTagOperations; -import org.sonar.server.user.MockUserSession; -import org.sonar.server.user.UserSession; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import static com.google.common.collect.Lists.newArrayList; -import static org.fest.assertions.Assertions.assertThat; -import static org.fest.assertions.Fail.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class QProfileRuleOperationsTest { - - @Mock - MyBatis myBatis; - - @Mock - SqlSession session; - - @Mock - ActiveRuleDao activeRuleDao; - - @Mock - RuleDao ruleDao; - - @Mock - RuleTagDao ruleTagDao; - - @Mock - RuleTagOperations ruleTagOperations; - - @Mock - ESActiveRule esActiveRule; - - @Mock - RuleRegistry ruleRegistry; - - @Mock - System2 system; - - Integer currentId = 1; - - UserSession authorizedUserSession = MockUserSession.create().setLogin("nicolas").setName("Nicolas").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); - UserSession unauthorizedUserSession = MockUserSession.create().setLogin("nicolas").setName("Nicolas"); - - QProfileRuleOperations operations; - - @Before - public void setUp() throws Exception { - when(myBatis.openSession()).thenReturn(session); - - // Associate an id when inserting an object to simulate the db id generator - doAnswer(new Answer() { - public Object answer(InvocationOnMock invocation) { - Object[] args = invocation.getArguments(); - ActiveRuleDto dto = (ActiveRuleDto) args[0]; - dto.setId(currentId++); - return null; - } - }).when(activeRuleDao).insert(any(ActiveRuleDto.class), any(SqlSession.class)); - - operations = new QProfileRuleOperations(myBatis, activeRuleDao, ruleDao, ruleTagDao, ruleTagOperations, esActiveRule, ruleRegistry, system); - } - - @Test - public void update_rule_note_when_no_existing_note() throws Exception { - RuleDto rule = new RuleDto().setId(10).setNoteCreatedAt(null).setNoteData(null); - - List ruleParams = newArrayList(new RuleParamDto().setId(20).setName("max").setDefaultValue("10")); - when(ruleDao.selectParameters(eq(10), eq(session))).thenReturn(ruleParams); - List ruleTags = newArrayList(new RuleRuleTagDto().setId(30L).setTag("style").setType(RuleTagType.SYSTEM)); - when(ruleDao.selectTags(eq(10), eq(session))).thenReturn(ruleTags); - - long now = System.currentTimeMillis(); - doReturn(now).when(system).now(); - - operations.updateRuleNote(rule, "My note", authorizedUserSession); - - ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(RuleDto.class); - verify(ruleDao).update(argumentCaptor.capture()); - assertThat(argumentCaptor.getValue().getNoteData()).isEqualTo("My note"); - assertThat(argumentCaptor.getValue().getNoteUserLogin()).isEqualTo("nicolas"); - assertThat(argumentCaptor.getValue().getNoteCreatedAt().getTime()).isEqualTo(now); - assertThat(argumentCaptor.getValue().getNoteUpdatedAt().getTime()).isEqualTo(now); - - verify(session).commit(); - verify(ruleRegistry).save(eq(rule), eq(ruleParams), eq(ruleTags)); - } - - @Test - public void fail_to_update_rule_note_without_profile_admin_permission() throws Exception { - RuleDto rule = new RuleDto().setId(10).setRepositoryKey("squid").setRuleKey("AvoidCycle"); - - try { - operations.updateRuleNote(rule, "My note", unauthorizedUserSession); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(ForbiddenException.class); - } - verifyNoMoreInteractions(ruleDao); - verify(session, never()).commit(); - } - - @Test - public void update_rule_note_when_already_note() throws Exception { - Date createdAt = DateUtils.parseDate("2013-12-20"); - RuleDto rule = new RuleDto().setId(10).setNoteCreatedAt(createdAt).setNoteData("My previous note").setNoteUserLogin("nicolas"); - - List ruleParams = newArrayList(new RuleParamDto().setId(20).setName("max").setDefaultValue("10")); - when(ruleDao.selectParameters(eq(10), eq(session))).thenReturn(ruleParams); - List ruleTags = newArrayList(new RuleRuleTagDto().setId(30L).setTag("style").setType(RuleTagType.SYSTEM)); - when(ruleDao.selectTags(eq(10), eq(session))).thenReturn(ruleTags); - - long now = System.currentTimeMillis(); - doReturn(now).when(system).now(); - - operations.updateRuleNote(rule, "My new note", MockUserSession.create().setLogin("guy").setName("Guy").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)); - - ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(RuleDto.class); - verify(ruleDao).update(argumentCaptor.capture()); - assertThat(argumentCaptor.getValue().getNoteData()).isEqualTo("My new note"); - assertThat(argumentCaptor.getValue().getNoteUserLogin()).isEqualTo("nicolas"); - assertThat(argumentCaptor.getValue().getNoteCreatedAt()).isEqualTo(createdAt); - assertThat(argumentCaptor.getValue().getNoteUpdatedAt().getTime()).isEqualTo(now); - - verify(session).commit(); - verify(ruleRegistry).save(eq(rule), eq(ruleParams), eq(ruleTags)); - } - - @Test - public void delete_rule_note() throws Exception { - Date createdAt = DateUtils.parseDate("2013-12-20"); - RuleDto rule = new RuleDto().setId(10).setNoteData("My note").setNoteUserLogin("nicolas").setNoteCreatedAt(createdAt).setNoteUpdatedAt(createdAt); - - List ruleParams = newArrayList(new RuleParamDto().setId(20).setName("max").setDefaultValue("10")); - when(ruleDao.selectParameters(eq(10), eq(session))).thenReturn(ruleParams); - List ruleTags = newArrayList(new RuleRuleTagDto().setId(30L).setTag("style").setType(RuleTagType.SYSTEM)); - when(ruleDao.selectTags(eq(10), eq(session))).thenReturn(ruleTags); - - long now = System.currentTimeMillis(); - doReturn(now).when(system).now(); - - operations.deleteRuleNote(rule, authorizedUserSession); - - ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(RuleDto.class); - verify(ruleDao).update(argumentCaptor.capture()); - assertThat(argumentCaptor.getValue().getNoteData()).isNull(); - assertThat(argumentCaptor.getValue().getNoteUserLogin()).isNull(); - assertThat(argumentCaptor.getValue().getNoteCreatedAt()).isNull(); - assertThat(argumentCaptor.getValue().getNoteUpdatedAt()).isNull(); - - verify(session).commit(); - verify(ruleRegistry).save(eq(rule), eq(ruleParams), eq(ruleTags)); - } - - @Test - public void create_rule() throws Exception { - RuleDto templateRule = new RuleDto().setId(10).setRepositoryKey("squid").setRuleKey("AvoidCycle").setConfigKey("Xpath"); - when(ruleDao.selectParameters(eq(10), eq(session))).thenReturn(newArrayList(new RuleParamDto().setId(20).setName("max").setDefaultValue("10"))); - when(ruleDao.selectTags(eq(10), eq(session))).thenReturn(newArrayList(new RuleRuleTagDto().setId(30L).setTag("style").setType(RuleTagType.SYSTEM))); - - Map paramsByKey = ImmutableMap.of("max", "20"); - RuleDto result = operations.createRule(templateRule, "My New Rule", Severity.BLOCKER, "Rule Description", paramsByKey, authorizedUserSession); - assertThat(result).isNotNull(); - - ArgumentCaptor ruleArgument = ArgumentCaptor.forClass(RuleDto.class); - verify(ruleDao).insert(ruleArgument.capture(), eq(session)); - assertThat(ruleArgument.getValue().getParentId()).isEqualTo(10); - assertThat(ruleArgument.getValue().getName()).isEqualTo("My New Rule"); - assertThat(ruleArgument.getValue().getDescription()).isEqualTo("Rule Description"); - assertThat(ruleArgument.getValue().getSeverityString()).isEqualTo(Severity.BLOCKER); - assertThat(ruleArgument.getValue().getConfigKey()).isEqualTo("Xpath"); - assertThat(ruleArgument.getValue().getRepositoryKey()).isEqualTo("squid"); - assertThat(ruleArgument.getValue().getRuleKey()).startsWith("AvoidCycle"); - assertThat(ruleArgument.getValue().getStatus()).isEqualTo("READY"); - assertThat(ruleArgument.getValue().getCardinality()).isEqualTo(Cardinality.SINGLE); - - ArgumentCaptor ruleParamArgument = ArgumentCaptor.forClass(RuleParamDto.class); - verify(ruleDao).insert(ruleParamArgument.capture(), eq(session)); - assertThat(ruleParamArgument.getValue().getName()).isEqualTo("max"); - assertThat(ruleParamArgument.getValue().getDefaultValue()).isEqualTo("20"); - - ArgumentCaptor ruleTagArgument = ArgumentCaptor.forClass(RuleRuleTagDto.class); - verify(ruleDao).insert(ruleTagArgument.capture(), eq(session)); - assertThat(ruleTagArgument.getValue().getTag()).isEqualTo("style"); - assertThat(ruleTagArgument.getValue().getType()).isEqualTo(RuleTagType.SYSTEM); - - verify(session).commit(); - verify(ruleRegistry).save(eq(ruleArgument.getValue()), eq(newArrayList(ruleParamArgument.getValue())), eq(newArrayList(ruleTagArgument.getValue()))); - } - - @Test - public void update_rule() throws Exception { - RuleDto rule = new RuleDto().setId(11).setRepositoryKey("squid").setRuleKey("XPath_1387869254").setConfigKey("Xpath"); - when(ruleDao.selectParameters(eq(11), eq(session))).thenReturn(newArrayList(new RuleParamDto().setId(21).setName("max").setDefaultValue("20"))); - ArrayList ruleTags = newArrayList(new RuleRuleTagDto().setId(30L).setTag("style").setType(RuleTagType.SYSTEM)); - when(ruleDao.selectTags(eq(11), eq(session))).thenReturn(ruleTags); - - Map paramsByKey = ImmutableMap.of("max", "21"); - operations.updateRule(rule, "Updated Rule", Severity.MAJOR, "Updated Description", paramsByKey, authorizedUserSession); - - ArgumentCaptor ruleArgument = ArgumentCaptor.forClass(RuleDto.class); - verify(ruleDao).update(ruleArgument.capture(), eq(session)); - assertThat(ruleArgument.getValue().getName()).isEqualTo("Updated Rule"); - assertThat(ruleArgument.getValue().getDescription()).isEqualTo("Updated Description"); - assertThat(ruleArgument.getValue().getSeverityString()).isEqualTo(Severity.MAJOR); - - ArgumentCaptor ruleParamArgument = ArgumentCaptor.forClass(RuleParamDto.class); - verify(ruleDao).update(ruleParamArgument.capture(), eq(session)); - assertThat(ruleParamArgument.getValue().getDefaultValue()).isEqualTo("21"); - - verify(session).commit(); - verify(ruleRegistry).save(eq(ruleArgument.getValue()), eq(newArrayList(ruleParamArgument.getValue())), eq(ruleTags)); - } - - @Test - public void delete_rule() throws Exception { - final int ruleId = 11; - RuleDto rule = new RuleDto().setId(ruleId).setRepositoryKey("squid").setRuleKey("XPath_1387869254").setConfigKey("Xpath").setUpdatedAt(DateUtils.parseDate("2013-12-23")); - RuleParamDto param = new RuleParamDto().setId(21).setName("max").setDefaultValue("20"); - when(ruleDao.selectParameters(eq(ruleId), eq(session))).thenReturn(newArrayList(param)); - ArrayList ruleTags = newArrayList(new RuleRuleTagDto().setId(30L).setTag("style").setType(RuleTagType.SYSTEM)); - when(ruleDao.selectTags(eq(ruleId), eq(session))).thenReturn(ruleTags); - - final int activeRuleId = 5; - ActiveRuleDto activeRule = new ActiveRuleDto().setId(activeRuleId).setProfileId(1).setRuleId(ruleId).setSeverity(Severity.MINOR); - when(activeRuleDao.selectByRuleId(ruleId)).thenReturn(newArrayList(activeRule)); - - long now = System.currentTimeMillis(); - doReturn(now).when(system).now(); - - operations.deleteRule(rule, authorizedUserSession); - - ArgumentCaptor ruleArgument = ArgumentCaptor.forClass(RuleDto.class); - verify(ruleDao).update(ruleArgument.capture(), eq(session)); - assertThat(ruleArgument.getValue().getStatus()).isEqualTo(Rule.STATUS_REMOVED); - assertThat(ruleArgument.getValue().getUpdatedAt()).isEqualTo(new Date(now)); - - verify(ruleRegistry).save(eq(ruleArgument.getValue()), eq(newArrayList(param)), eq(ruleTags)); - verify(activeRuleDao).deleteParameters(eq(activeRuleId), eq(session)); - verify(activeRuleDao).deleteFromRule(eq(ruleId), eq(session)); - verify(session, times(2)).commit(); - verify(esActiveRule).deleteActiveRules(newArrayList(activeRuleId)); - } - - @Test(expected = ForbiddenException.class) - public void should_fail_update_tags_on_unauthorized_user() { - operations.updateTags(new RuleDto(), ImmutableList.of("polop"), unauthorizedUserSession); - } - - @Test(expected = NotFoundException.class) - public void should_fail_update_tags_on_unknown_tag() { - final String tag = "polop"; - when(ruleTagDao.selectId(tag, session)).thenReturn(null); - operations.updateTags(new RuleDto(), ImmutableList.of(tag), authorizedUserSession); - } - - @Test - public void should_add_new_tags() { - final int ruleId = 24; - final RuleDto rule = new RuleDto().setId(ruleId); - final String tag = "polop"; - final long tagId = 42L; - when(ruleTagDao.selectId(tag, session)).thenReturn(tagId); - - operations.updateTags(rule, ImmutableList.of(tag), authorizedUserSession); - - verify(ruleTagDao).selectId(tag, session); - ArgumentCaptor capture = ArgumentCaptor.forClass(RuleRuleTagDto.class); - verify(ruleDao).insert(capture.capture(), eq(session)); - final RuleRuleTagDto newTag = capture.getValue(); - assertThat(newTag.getRuleId()).isEqualTo(ruleId); - assertThat(newTag.getTagId()).isEqualTo(tagId); - assertThat(newTag.getType()).isEqualTo(RuleTagType.ADMIN); - verify(ruleDao).update(rule, session); - verify(session).commit(); - } - - @Test - public void should_delete_removed_tags() { - final int ruleId = 24; - final RuleDto rule = new RuleDto().setId(ruleId); - final String tag = "polop"; - RuleRuleTagDto existingTag = new RuleRuleTagDto().setTag(tag).setType(RuleTagType.ADMIN); - when(ruleDao.selectTags(ruleId, session)).thenReturn(ImmutableList.of(existingTag)); - - - operations.updateTags(rule, ImmutableList.of(), authorizedUserSession); - - verify(ruleDao, atLeast(1)).selectTags(ruleId, session); - verify(ruleDao).deleteTag(existingTag, session); - verify(ruleDao).update(rule, session); - verify(ruleTagOperations).deleteUnusedTags(session); - verify(session).commit(); - } - - @Test - public void should_not_update_rule_if_tags_unchanged() { - final int ruleId = 24; - final RuleDto rule = new RuleDto().setId(ruleId); - final String tag = "polop"; - final long tagId = 42L; - when(ruleTagDao.selectId(tag, session)).thenReturn(tagId); - RuleRuleTagDto existingTag = new RuleRuleTagDto().setTag(tag).setType(RuleTagType.ADMIN); - when(ruleDao.selectTags(ruleId, session)).thenReturn(ImmutableList.of(existingTag)); - - operations.updateTags(rule, ImmutableList.of(tag), authorizedUserSession); - - verify(ruleTagDao).selectId(tag, session); - verify(ruleDao).selectTags(ruleId, session); - verify(ruleTagOperations).deleteUnusedTags(session); - verify(ruleDao, never()).update(rule); - } -} diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesTest.java index b45436fffad..e41d7c961c7 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesTest.java @@ -20,6 +20,7 @@ package org.sonar.server.qualityprofile; +import org.sonar.server.rule.RuleOperations; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; @@ -39,7 +40,6 @@ import org.sonar.core.rule.RuleDao; import org.sonar.core.rule.RuleDto; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.NotFoundException; -import org.sonar.server.rule.ProfileRuleQuery; import org.sonar.server.user.UserSession; import java.util.List; @@ -83,7 +83,7 @@ public class QProfilesTest { QProfileActiveRuleOperations activeRuleOperations; @Mock - QProfileRuleOperations ruleOperations; + RuleOperations ruleOperations; @Mock QProfileBackup backup; diff --git a/sonar-server/src/test/java/org/sonar/server/rule/ProfileRuleQueryTest.java b/sonar-server/src/test/java/org/sonar/server/rule/ProfileRuleQueryTest.java deleted file mode 100644 index 439026df1c8..00000000000 --- a/sonar-server/src/test/java/org/sonar/server/rule/ProfileRuleQueryTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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.ImmutableMap; -import com.google.common.collect.Lists; -import org.junit.Test; -import org.sonar.server.exceptions.BadRequestException; - -import java.util.Map; - -import static org.fest.assertions.Assertions.assertThat; -import static org.fest.assertions.Fail.fail; - -public class ProfileRuleQueryTest { - - @Test - public void create_basic_query() { - final int profileId = 42; - ProfileRuleQuery query = ProfileRuleQuery.create(profileId); - assertThat(query.profileId()).isEqualTo(profileId); - } - - @Test - public void parse_nominal_request() { - final int profileId = 42; - Map params = ImmutableMap.of("profileId", (Object) Integer.toString(profileId)); - ProfileRuleQuery query = ProfileRuleQuery.parse(params); - assertThat(query.profileId()).isEqualTo(profileId); - assertThat(query.anyInheritance()).isTrue(); - } - - @Test - public void fail_on_missing_profileId() { - Map params = ImmutableMap.of(); - try { - ProfileRuleQuery.parse(params); - fail("Expected BadRequestException"); - } catch(BadRequestException bre) { - assertThat(bre.errors().get(0).text()).isEqualTo("Missing parameter profileId"); - } - } - - @Test - public void fail_on_incorrect_profileId() { - Map params = ImmutableMap.of("profileId", (Object) "not an integer"); - try { - ProfileRuleQuery.parse(params); - fail("Expected BadRequestException"); - } catch(Exception e) { - assertThat(e).isInstanceOf(NumberFormatException.class); - } - } - - @Test - public void parse_with_inheritance() { - final int profileId = 42; - Map params = ImmutableMap.of( - "profileId", (Object) Integer.toString(profileId), - "inheritance", "OVERRIDES" - ); - ProfileRuleQuery query = ProfileRuleQuery.parse(params); - assertThat(query.profileId()).isEqualTo(profileId); - assertThat(query.inheritance()).isEqualTo("OVERRIDES"); - } - - @Test - public void fail_on_incorrect_inheritance() { - Map params = ImmutableMap.of("profileId", (Object) Integer.toString(42), "inheritance", "bad value"); - try { - ProfileRuleQuery.parse(params); - fail(); - } catch(Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class); - } - } - - @Test - public void parse_with_sort() { - ProfileRuleQuery query = ProfileRuleQuery.parse(ImmutableMap.of("profileId", (Object) Integer.toString(42), - "sort_by", ProfileRuleQuery.SORT_BY_RULE_NAME, "asc", "true")); - assertThat(query.sort()).isEqualTo(ProfileRuleQuery.SORT_BY_RULE_NAME); - assertThat(query.asc()).isTrue(); - - query = ProfileRuleQuery.parse(ImmutableMap.of("profileId", (Object) Integer.toString(42), - "sort_by", ProfileRuleQuery.SORT_BY_RULE_NAME, "asc", "false")); - assertThat(query.sort()).isEqualTo(ProfileRuleQuery.SORT_BY_RULE_NAME); - assertThat(query.asc()).isFalse(); - - // Default sort - query = ProfileRuleQuery.parse(ImmutableMap.of("profileId", (Object) Integer.toString(42))); - assertThat(query.sort()).isEqualTo(ProfileRuleQuery.SORT_BY_RULE_NAME); - assertThat(query.asc()).isTrue(); - } - - @Test - public void fail_on_incorrect_sort() { - Map params = ImmutableMap.of("profileId", (Object) Integer.toString(42), "sort_by", "bad sort"); - try { - ProfileRuleQuery.parse(params); - fail(); - } catch(Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class); - } - } - - @Test - public void should_parse_all_parameters() { - final int profileId = 42; - final String language = "xoo"; - final String nameOrKey = "unused parameter"; - Map params = ImmutableMap. builder() - .put("profileId", (Object) Integer.toString(profileId)) - .put("language", (Object) language) - .put("nameOrKey", (Object) nameOrKey) - .put("repositoryKeys", (Object) Lists.newArrayList("", "repo1", "repo2")) - .put("severities", new Object()) - .put("statuses", (Object) Lists.newArrayList("", "BETA", "DEPRECATED")) - .put("tags", (Object) Lists.newArrayList("", "tag1", "tag2")) - .build(); - ProfileRuleQuery query = ProfileRuleQuery.parse(params); - assertThat(query.profileId()).isEqualTo(profileId); - assertThat(query.language()).isEqualTo(language); - assertThat(query.nameOrKey()).isEqualTo(nameOrKey); - assertThat(query.repositoryKeys()).containsOnly("repo1", "repo2"); - assertThat(query.severities()).isEmpty(); - assertThat(query.statuses()).containsOnly("BETA", "DEPRECATED"); - assertThat(query.tags()).containsOnly("tag1", "tag2"); - } -} diff --git a/sonar-server/src/test/java/org/sonar/server/rule/RuleOperationsTest.java b/sonar-server/src/test/java/org/sonar/server/rule/RuleOperationsTest.java new file mode 100644 index 00000000000..088c8a39d0a --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/rule/RuleOperationsTest.java @@ -0,0 +1,381 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.server.qualityprofile.ESActiveRule; + +import org.sonar.server.rule.RuleOperations; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.ibatis.session.SqlSession; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.Rule; +import org.sonar.api.utils.DateUtils; +import org.sonar.api.utils.System2; +import org.sonar.check.Cardinality; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.qualityprofile.db.ActiveRuleDao; +import org.sonar.core.qualityprofile.db.ActiveRuleDto; +import org.sonar.core.rule.RuleDao; +import org.sonar.core.rule.RuleDto; +import org.sonar.core.rule.RuleParamDto; +import org.sonar.core.rule.RuleRuleTagDto; +import org.sonar.core.rule.RuleTagDao; +import org.sonar.core.rule.RuleTagType; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.rule.RuleRegistry; +import org.sonar.server.rule.RuleTagOperations; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.user.UserSession; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static com.google.common.collect.Lists.newArrayList; +import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class RuleOperationsTest { + + @Mock + MyBatis myBatis; + + @Mock + SqlSession session; + + @Mock + ActiveRuleDao activeRuleDao; + + @Mock + RuleDao ruleDao; + + @Mock + RuleTagDao ruleTagDao; + + @Mock + RuleTagOperations ruleTagOperations; + + @Mock + ESActiveRule esActiveRule; + + @Mock + RuleRegistry ruleRegistry; + + @Mock + System2 system; + + Integer currentId = 1; + + UserSession authorizedUserSession = MockUserSession.create().setLogin("nicolas").setName("Nicolas").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + UserSession unauthorizedUserSession = MockUserSession.create().setLogin("nicolas").setName("Nicolas"); + + RuleOperations operations; + + @Before + public void setUp() throws Exception { + when(myBatis.openSession()).thenReturn(session); + + // Associate an id when inserting an object to simulate the db id generator + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + ActiveRuleDto dto = (ActiveRuleDto) args[0]; + dto.setId(currentId++); + return null; + } + }).when(activeRuleDao).insert(any(ActiveRuleDto.class), any(SqlSession.class)); + + operations = new RuleOperations(myBatis, activeRuleDao, ruleDao, ruleTagDao, ruleTagOperations, esActiveRule, ruleRegistry, system); + } + + @Test + public void update_rule_note_when_no_existing_note() throws Exception { + RuleDto rule = new RuleDto().setId(10).setNoteCreatedAt(null).setNoteData(null); + + List ruleParams = newArrayList(new RuleParamDto().setId(20).setName("max").setDefaultValue("10")); + when(ruleDao.selectParameters(eq(10), eq(session))).thenReturn(ruleParams); + List ruleTags = newArrayList(new RuleRuleTagDto().setId(30L).setTag("style").setType(RuleTagType.SYSTEM)); + when(ruleDao.selectTags(eq(10), eq(session))).thenReturn(ruleTags); + + long now = System.currentTimeMillis(); + doReturn(now).when(system).now(); + + operations.updateRuleNote(rule, "My note", authorizedUserSession); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(RuleDto.class); + verify(ruleDao).update(argumentCaptor.capture()); + assertThat(argumentCaptor.getValue().getNoteData()).isEqualTo("My note"); + assertThat(argumentCaptor.getValue().getNoteUserLogin()).isEqualTo("nicolas"); + assertThat(argumentCaptor.getValue().getNoteCreatedAt().getTime()).isEqualTo(now); + assertThat(argumentCaptor.getValue().getNoteUpdatedAt().getTime()).isEqualTo(now); + + verify(session).commit(); + verify(ruleRegistry).save(eq(rule), eq(ruleParams), eq(ruleTags)); + } + + @Test + public void fail_to_update_rule_note_without_profile_admin_permission() throws Exception { + RuleDto rule = new RuleDto().setId(10).setRepositoryKey("squid").setRuleKey("AvoidCycle"); + + try { + operations.updateRuleNote(rule, "My note", unauthorizedUserSession); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(ForbiddenException.class); + } + verifyNoMoreInteractions(ruleDao); + verify(session, never()).commit(); + } + + @Test + public void update_rule_note_when_already_note() throws Exception { + Date createdAt = DateUtils.parseDate("2013-12-20"); + RuleDto rule = new RuleDto().setId(10).setNoteCreatedAt(createdAt).setNoteData("My previous note").setNoteUserLogin("nicolas"); + + List ruleParams = newArrayList(new RuleParamDto().setId(20).setName("max").setDefaultValue("10")); + when(ruleDao.selectParameters(eq(10), eq(session))).thenReturn(ruleParams); + List ruleTags = newArrayList(new RuleRuleTagDto().setId(30L).setTag("style").setType(RuleTagType.SYSTEM)); + when(ruleDao.selectTags(eq(10), eq(session))).thenReturn(ruleTags); + + long now = System.currentTimeMillis(); + doReturn(now).when(system).now(); + + operations.updateRuleNote(rule, "My new note", MockUserSession.create().setLogin("guy").setName("Guy").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(RuleDto.class); + verify(ruleDao).update(argumentCaptor.capture()); + assertThat(argumentCaptor.getValue().getNoteData()).isEqualTo("My new note"); + assertThat(argumentCaptor.getValue().getNoteUserLogin()).isEqualTo("nicolas"); + assertThat(argumentCaptor.getValue().getNoteCreatedAt()).isEqualTo(createdAt); + assertThat(argumentCaptor.getValue().getNoteUpdatedAt().getTime()).isEqualTo(now); + + verify(session).commit(); + verify(ruleRegistry).save(eq(rule), eq(ruleParams), eq(ruleTags)); + } + + @Test + public void delete_rule_note() throws Exception { + Date createdAt = DateUtils.parseDate("2013-12-20"); + RuleDto rule = new RuleDto().setId(10).setNoteData("My note").setNoteUserLogin("nicolas").setNoteCreatedAt(createdAt).setNoteUpdatedAt(createdAt); + + List ruleParams = newArrayList(new RuleParamDto().setId(20).setName("max").setDefaultValue("10")); + when(ruleDao.selectParameters(eq(10), eq(session))).thenReturn(ruleParams); + List ruleTags = newArrayList(new RuleRuleTagDto().setId(30L).setTag("style").setType(RuleTagType.SYSTEM)); + when(ruleDao.selectTags(eq(10), eq(session))).thenReturn(ruleTags); + + long now = System.currentTimeMillis(); + doReturn(now).when(system).now(); + + operations.deleteRuleNote(rule, authorizedUserSession); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(RuleDto.class); + verify(ruleDao).update(argumentCaptor.capture()); + assertThat(argumentCaptor.getValue().getNoteData()).isNull(); + assertThat(argumentCaptor.getValue().getNoteUserLogin()).isNull(); + assertThat(argumentCaptor.getValue().getNoteCreatedAt()).isNull(); + assertThat(argumentCaptor.getValue().getNoteUpdatedAt()).isNull(); + + verify(session).commit(); + verify(ruleRegistry).save(eq(rule), eq(ruleParams), eq(ruleTags)); + } + + @Test + public void create_rule() throws Exception { + RuleDto templateRule = new RuleDto().setId(10).setRepositoryKey("squid").setRuleKey("AvoidCycle").setConfigKey("Xpath"); + when(ruleDao.selectParameters(eq(10), eq(session))).thenReturn(newArrayList(new RuleParamDto().setId(20).setName("max").setDefaultValue("10"))); + when(ruleDao.selectTags(eq(10), eq(session))).thenReturn(newArrayList(new RuleRuleTagDto().setId(30L).setTag("style").setType(RuleTagType.SYSTEM))); + + Map paramsByKey = ImmutableMap.of("max", "20"); + RuleDto result = operations.createRule(templateRule, "My New Rule", Severity.BLOCKER, "Rule Description", paramsByKey, authorizedUserSession); + assertThat(result).isNotNull(); + + ArgumentCaptor ruleArgument = ArgumentCaptor.forClass(RuleDto.class); + verify(ruleDao).insert(ruleArgument.capture(), eq(session)); + assertThat(ruleArgument.getValue().getParentId()).isEqualTo(10); + assertThat(ruleArgument.getValue().getName()).isEqualTo("My New Rule"); + assertThat(ruleArgument.getValue().getDescription()).isEqualTo("Rule Description"); + assertThat(ruleArgument.getValue().getSeverityString()).isEqualTo(Severity.BLOCKER); + assertThat(ruleArgument.getValue().getConfigKey()).isEqualTo("Xpath"); + assertThat(ruleArgument.getValue().getRepositoryKey()).isEqualTo("squid"); + assertThat(ruleArgument.getValue().getRuleKey()).startsWith("AvoidCycle"); + assertThat(ruleArgument.getValue().getStatus()).isEqualTo("READY"); + assertThat(ruleArgument.getValue().getCardinality()).isEqualTo(Cardinality.SINGLE); + + ArgumentCaptor ruleParamArgument = ArgumentCaptor.forClass(RuleParamDto.class); + verify(ruleDao).insert(ruleParamArgument.capture(), eq(session)); + assertThat(ruleParamArgument.getValue().getName()).isEqualTo("max"); + assertThat(ruleParamArgument.getValue().getDefaultValue()).isEqualTo("20"); + + ArgumentCaptor ruleTagArgument = ArgumentCaptor.forClass(RuleRuleTagDto.class); + verify(ruleDao).insert(ruleTagArgument.capture(), eq(session)); + assertThat(ruleTagArgument.getValue().getTag()).isEqualTo("style"); + assertThat(ruleTagArgument.getValue().getType()).isEqualTo(RuleTagType.SYSTEM); + + verify(session).commit(); + verify(ruleRegistry).save(eq(ruleArgument.getValue()), eq(newArrayList(ruleParamArgument.getValue())), eq(newArrayList(ruleTagArgument.getValue()))); + } + + @Test + public void update_rule() throws Exception { + RuleDto rule = new RuleDto().setId(11).setRepositoryKey("squid").setRuleKey("XPath_1387869254").setConfigKey("Xpath"); + when(ruleDao.selectParameters(eq(11), eq(session))).thenReturn(newArrayList(new RuleParamDto().setId(21).setName("max").setDefaultValue("20"))); + ArrayList ruleTags = newArrayList(new RuleRuleTagDto().setId(30L).setTag("style").setType(RuleTagType.SYSTEM)); + when(ruleDao.selectTags(eq(11), eq(session))).thenReturn(ruleTags); + + Map paramsByKey = ImmutableMap.of("max", "21"); + operations.updateRule(rule, "Updated Rule", Severity.MAJOR, "Updated Description", paramsByKey, authorizedUserSession); + + ArgumentCaptor ruleArgument = ArgumentCaptor.forClass(RuleDto.class); + verify(ruleDao).update(ruleArgument.capture(), eq(session)); + assertThat(ruleArgument.getValue().getName()).isEqualTo("Updated Rule"); + assertThat(ruleArgument.getValue().getDescription()).isEqualTo("Updated Description"); + assertThat(ruleArgument.getValue().getSeverityString()).isEqualTo(Severity.MAJOR); + + ArgumentCaptor ruleParamArgument = ArgumentCaptor.forClass(RuleParamDto.class); + verify(ruleDao).update(ruleParamArgument.capture(), eq(session)); + assertThat(ruleParamArgument.getValue().getDefaultValue()).isEqualTo("21"); + + verify(session).commit(); + verify(ruleRegistry).save(eq(ruleArgument.getValue()), eq(newArrayList(ruleParamArgument.getValue())), eq(ruleTags)); + } + + @Test + public void delete_rule() throws Exception { + final int ruleId = 11; + RuleDto rule = new RuleDto().setId(ruleId).setRepositoryKey("squid").setRuleKey("XPath_1387869254").setConfigKey("Xpath").setUpdatedAt(DateUtils.parseDate("2013-12-23")); + RuleParamDto param = new RuleParamDto().setId(21).setName("max").setDefaultValue("20"); + when(ruleDao.selectParameters(eq(ruleId), eq(session))).thenReturn(newArrayList(param)); + ArrayList ruleTags = newArrayList(new RuleRuleTagDto().setId(30L).setTag("style").setType(RuleTagType.SYSTEM)); + when(ruleDao.selectTags(eq(ruleId), eq(session))).thenReturn(ruleTags); + + final int activeRuleId = 5; + ActiveRuleDto activeRule = new ActiveRuleDto().setId(activeRuleId).setProfileId(1).setRuleId(ruleId).setSeverity(Severity.MINOR); + when(activeRuleDao.selectByRuleId(ruleId)).thenReturn(newArrayList(activeRule)); + + long now = System.currentTimeMillis(); + doReturn(now).when(system).now(); + + operations.deleteRule(rule, authorizedUserSession); + + ArgumentCaptor ruleArgument = ArgumentCaptor.forClass(RuleDto.class); + verify(ruleDao).update(ruleArgument.capture(), eq(session)); + assertThat(ruleArgument.getValue().getStatus()).isEqualTo(Rule.STATUS_REMOVED); + assertThat(ruleArgument.getValue().getUpdatedAt()).isEqualTo(new Date(now)); + + verify(ruleRegistry).save(eq(ruleArgument.getValue()), eq(newArrayList(param)), eq(ruleTags)); + verify(activeRuleDao).deleteParameters(eq(activeRuleId), eq(session)); + verify(activeRuleDao).deleteFromRule(eq(ruleId), eq(session)); + verify(session, times(2)).commit(); + verify(esActiveRule).deleteActiveRules(newArrayList(activeRuleId)); + } + + @Test(expected = ForbiddenException.class) + public void should_fail_update_tags_on_unauthorized_user() { + operations.updateTags(new RuleDto(), ImmutableList.of("polop"), unauthorizedUserSession); + } + + @Test(expected = NotFoundException.class) + public void should_fail_update_tags_on_unknown_tag() { + final String tag = "polop"; + when(ruleTagDao.selectId(tag, session)).thenReturn(null); + operations.updateTags(new RuleDto(), ImmutableList.of(tag), authorizedUserSession); + } + + @Test + public void should_add_new_tags() { + final int ruleId = 24; + final RuleDto rule = new RuleDto().setId(ruleId); + final String tag = "polop"; + final long tagId = 42L; + when(ruleTagDao.selectId(tag, session)).thenReturn(tagId); + + operations.updateTags(rule, ImmutableList.of(tag), authorizedUserSession); + + verify(ruleTagDao).selectId(tag, session); + ArgumentCaptor capture = ArgumentCaptor.forClass(RuleRuleTagDto.class); + verify(ruleDao).insert(capture.capture(), eq(session)); + final RuleRuleTagDto newTag = capture.getValue(); + assertThat(newTag.getRuleId()).isEqualTo(ruleId); + assertThat(newTag.getTagId()).isEqualTo(tagId); + assertThat(newTag.getType()).isEqualTo(RuleTagType.ADMIN); + verify(ruleDao).update(rule, session); + verify(session).commit(); + } + + @Test + public void should_delete_removed_tags() { + final int ruleId = 24; + final RuleDto rule = new RuleDto().setId(ruleId); + final String tag = "polop"; + RuleRuleTagDto existingTag = new RuleRuleTagDto().setTag(tag).setType(RuleTagType.ADMIN); + when(ruleDao.selectTags(ruleId, session)).thenReturn(ImmutableList.of(existingTag)); + + + operations.updateTags(rule, ImmutableList.of(), authorizedUserSession); + + verify(ruleDao, atLeast(1)).selectTags(ruleId, session); + verify(ruleDao).deleteTag(existingTag, session); + verify(ruleDao).update(rule, session); + verify(ruleTagOperations).deleteUnusedTags(session); + verify(session).commit(); + } + + @Test + public void should_not_update_rule_if_tags_unchanged() { + final int ruleId = 24; + final RuleDto rule = new RuleDto().setId(ruleId); + final String tag = "polop"; + final long tagId = 42L; + when(ruleTagDao.selectId(tag, session)).thenReturn(tagId); + RuleRuleTagDto existingTag = new RuleRuleTagDto().setTag(tag).setType(RuleTagType.ADMIN); + when(ruleDao.selectTags(ruleId, session)).thenReturn(ImmutableList.of(existingTag)); + + operations.updateTags(rule, ImmutableList.of(tag), authorizedUserSession); + + verify(ruleTagDao).selectId(tag, session); + verify(ruleDao).selectTags(ruleId, session); + verify(ruleTagOperations).deleteUnusedTags(session); + verify(ruleDao, never()).update(rule); + } +} -- cgit v1.2.3