From: Simon Brandhof Date: Mon, 12 May 2014 21:36:54 +0000 (+0200) Subject: SONAR-5007 Complete new web services api/qualityprofiles and api/rules X-Git-Tag: 4.4-RC1~1117 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=2da1b8efdc956d95edb8e92fae9d19d0cf798683;p=sonarqube.git SONAR-5007 Complete new web services api/qualityprofiles and api/rules --- diff --git a/sonar-core/src/main/java/org/sonar/core/db/Dao.java b/sonar-core/src/main/java/org/sonar/core/db/Dao.java index 139fc06d0c2..f19951d065f 100644 --- a/sonar-core/src/main/java/org/sonar/core/db/Dao.java +++ b/sonar-core/src/main/java/org/sonar/core/db/Dao.java @@ -19,13 +19,15 @@ */ package org.sonar.core.db; +import org.sonar.api.ServerComponent; import org.sonar.core.persistence.DbSession; import javax.annotation.CheckForNull; import java.io.Serializable; +import java.util.Collection; import java.util.List; -public interface Dao, K extends Serializable> { +public interface Dao, K extends Serializable> extends ServerComponent { @CheckForNull E getByKey(K key, DbSession session); @@ -36,11 +38,11 @@ public interface Dao, K extends Serializable> { E insert(E item, DbSession session); - List insert(List items, DbSession session); + List insert(List items, DbSession session); void delete(E item, DbSession session); - void delete(List items, DbSession session); + void delete(Collection items, DbSession session); void deleteByKey(K key, DbSession session); diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseUtils.java b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseUtils.java index c58d2eb5751..90bb4c29746 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseUtils.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseUtils.java @@ -32,6 +32,7 @@ import java.sql.Statement; */ public final class DatabaseUtils { private DatabaseUtils() { + // only static methods } public static void closeQuietly(@Nullable Connection connection) { diff --git a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileKey.java b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileKey.java index cef4501b74b..f6677a43d51 100644 --- a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileKey.java +++ b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileKey.java @@ -32,7 +32,7 @@ import java.io.Serializable; public class QualityProfileKey implements Serializable{ private final String name, lang; - protected QualityProfileKey(String name, String lang) { + private QualityProfileKey(String name, String lang) { this.lang = lang; this.name = name; } diff --git a/sonar-core/src/test/java/org/sonar/core/persistence/AbstractDaoTestCase.java b/sonar-core/src/test/java/org/sonar/core/persistence/AbstractDaoTestCase.java index f4db47a783e..bd5113bccf5 100644 --- a/sonar-core/src/test/java/org/sonar/core/persistence/AbstractDaoTestCase.java +++ b/sonar-core/src/test/java/org/sonar/core/persistence/AbstractDaoTestCase.java @@ -19,9 +19,6 @@ */ package org.sonar.core.persistence; -import org.sonar.core.cluster.NullQueue; - -import org.sonar.core.cluster.WorkQueue; import com.google.common.collect.Maps; import com.google.common.io.Closeables; import org.apache.commons.io.FileUtils; @@ -33,7 +30,11 @@ import org.dbunit.DatabaseUnitException; import org.dbunit.IDatabaseTester; import org.dbunit.database.DatabaseConfig; import org.dbunit.database.IDatabaseConnection; -import org.dbunit.dataset.*; +import org.dbunit.dataset.CompositeDataSet; +import org.dbunit.dataset.DataSetException; +import org.dbunit.dataset.IDataSet; +import org.dbunit.dataset.ITable; +import org.dbunit.dataset.ReplacementDataSet; import org.dbunit.dataset.filter.DefaultColumnFilter; import org.dbunit.dataset.xml.FlatXmlDataSet; import org.dbunit.ext.mssql.InsertIdentityOperation; @@ -43,6 +44,8 @@ import org.junit.Before; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.config.Settings; +import org.sonar.core.cluster.NullQueue; +import org.sonar.core.cluster.WorkQueue; import org.sonar.core.config.Logback; import java.io.File; @@ -56,10 +59,10 @@ import java.sql.SQLException; import java.util.Map; import java.util.Properties; -import static org.mockito.Mockito.mock; import static org.junit.Assert.fail; public abstract class AbstractDaoTestCase { + private static final Logger LOG = LoggerFactory.getLogger(AbstractDaoTestCase.class); private static Database database; private static DatabaseCommands databaseCommands; @@ -90,7 +93,7 @@ public abstract class AbstractDaoTestCase { databaseCommands = DatabaseCommands.forDialect(database.getDialect()); databaseTester = new DataSourceDatabaseTester(database.getDataSource()); - myBatis = new MyBatis(database, new Logback(),queue); + myBatis = new MyBatis(database, new Logback(), queue); myBatis.start(); } diff --git a/sonar-core/src/test/java/org/sonar/core/persistence/TestDatabase.java b/sonar-core/src/test/java/org/sonar/core/persistence/TestDatabase.java index 6f9941d0966..5d0ad07a3b0 100644 --- a/sonar-core/src/test/java/org/sonar/core/persistence/TestDatabase.java +++ b/sonar-core/src/test/java/org/sonar/core/persistence/TestDatabase.java @@ -19,9 +19,6 @@ */ package org.sonar.core.persistence; -import org.sonar.core.cluster.NullQueue; - -import org.sonar.core.cluster.WorkQueue; import com.google.common.collect.Maps; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.dbutils.DbUtils; @@ -48,6 +45,8 @@ import org.junit.rules.ExternalResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.config.Settings; +import org.sonar.core.cluster.NullQueue; +import org.sonar.core.cluster.WorkQueue; import org.sonar.core.config.Logback; import org.sonar.core.persistence.dialect.Dialect; @@ -65,7 +64,6 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import static org.mockito.Mockito.mock; import static org.junit.Assert.fail; /** diff --git a/sonar-server/src/main/java/org/sonar/server/db/BaseDao.java b/sonar-server/src/main/java/org/sonar/server/db/BaseDao.java index 5a94af56455..23fc54e0ea3 100644 --- a/sonar-server/src/main/java/org/sonar/server/db/BaseDao.java +++ b/sonar-server/src/main/java/org/sonar/server/db/BaseDao.java @@ -30,6 +30,7 @@ import org.sonar.server.search.KeyIndexAction; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.List; /** @@ -170,10 +171,9 @@ public abstract class BaseDao, K extends Serializable> imple } @Override - public void delete(List items, DbSession session) { - //TODO check for bulk inserts + public void delete(Collection items, DbSession session) { for(E item:items) { - this.delete(item, session); + delete(item, session); } } diff --git a/sonar-server/src/main/java/org/sonar/server/db/DbClient.java b/sonar-server/src/main/java/org/sonar/server/db/DbClient.java new file mode 100644 index 00000000000..b690d6c6504 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/db/DbClient.java @@ -0,0 +1,62 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.db; + +import org.sonar.api.ServerComponent; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.qualityprofile.db.QualityProfileDao; +import org.sonar.server.rule2.ActiveRuleDao; +import org.sonar.server.rule2.RuleDao; + +/** + * Facade for all db components + */ +public class DbClient implements ServerComponent { + + private final MyBatis myBatis; + private final RuleDao ruleDao; + private final ActiveRuleDao activeRuleDao; + private final QualityProfileDao qProfileDao; + + public DbClient(MyBatis myBatis, RuleDao ruleDao, ActiveRuleDao activeRuleDao, + QualityProfileDao qProfileDao) { + this.myBatis = myBatis; + this.ruleDao = ruleDao; + this.activeRuleDao = activeRuleDao; + this.qProfileDao = qProfileDao; + } + + public DbSession openSession(boolean batch) { + return myBatis.openSession(batch); + } + + public RuleDao ruleDao() { + return ruleDao; + } + + public ActiveRuleDao activeRuleDao() { + return activeRuleDao; + } + + public QualityProfileDao qualityProfileDao() { + return qProfileDao; + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 55426551d76..68371960e3f 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -55,7 +55,13 @@ import org.sonar.core.measure.MeasureFilterFactory; import org.sonar.core.metric.DefaultMetricFinder; import org.sonar.core.notification.DefaultNotificationManager; import org.sonar.core.permission.PermissionFacade; -import org.sonar.core.persistence.*; +import org.sonar.core.persistence.DaoUtils; +import org.sonar.core.persistence.DatabaseVersion; +import org.sonar.core.persistence.DefaultDatabase; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.persistence.PreviewDatabaseFactory; +import org.sonar.core.persistence.SemaphoreUpdater; +import org.sonar.core.persistence.SemaphoresImpl; import org.sonar.core.preview.PreviewCache; import org.sonar.core.profiling.Profiling; import org.sonar.core.purge.PurgeProfiler; @@ -84,13 +90,36 @@ import org.sonar.server.component.DefaultRubyComponentService; import org.sonar.server.component.ws.ComponentsWs; import org.sonar.server.component.ws.ProjectsWs; import org.sonar.server.component.ws.ResourcesWs; +import org.sonar.server.db.DbClient; import org.sonar.server.db.EmbeddedDatabaseFactory; import org.sonar.server.db.migrations.DatabaseMigrations; import org.sonar.server.db.migrations.DatabaseMigrator; -import org.sonar.server.debt.*; +import org.sonar.server.debt.DebtCharacteristicsXMLImporter; +import org.sonar.server.debt.DebtModelBackup; +import org.sonar.server.debt.DebtModelLookup; +import org.sonar.server.debt.DebtModelOperations; +import org.sonar.server.debt.DebtModelPluginRepository; +import org.sonar.server.debt.DebtModelService; +import org.sonar.server.debt.DebtModelXMLExporter; +import org.sonar.server.debt.DebtRulesXMLImporter; import org.sonar.server.es.ESIndex; import org.sonar.server.es.ESNode; -import org.sonar.server.issue.*; +import org.sonar.server.issue.ActionService; +import org.sonar.server.issue.AssignAction; +import org.sonar.server.issue.CommentAction; +import org.sonar.server.issue.DefaultIssueFinder; +import org.sonar.server.issue.InternalRubyIssueService; +import org.sonar.server.issue.IssueBulkChangeService; +import org.sonar.server.issue.IssueChangelogFormatter; +import org.sonar.server.issue.IssueChangelogService; +import org.sonar.server.issue.IssueCommentService; +import org.sonar.server.issue.IssueService; +import org.sonar.server.issue.IssueStatsFinder; +import org.sonar.server.issue.PlanAction; +import org.sonar.server.issue.PublicRubyIssueService; +import org.sonar.server.issue.ServerIssueStorage; +import org.sonar.server.issue.SetSeverityAction; +import org.sonar.server.issue.TransitionAction; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.issue.actionplan.ActionPlanWs; import org.sonar.server.issue.filter.IssueFilterService; @@ -108,21 +137,74 @@ import org.sonar.server.permission.ws.PermissionsWs; import org.sonar.server.platform.ws.RestartHandler; import org.sonar.server.platform.ws.ServerWs; import org.sonar.server.platform.ws.SystemWs; -import org.sonar.server.plugins.*; +import org.sonar.server.plugins.BatchWs; +import org.sonar.server.plugins.InstalledPluginReferentialFactory; +import org.sonar.server.plugins.PluginDownloader; +import org.sonar.server.plugins.ServerExtensionInstaller; +import org.sonar.server.plugins.ServerPluginJarInstaller; +import org.sonar.server.plugins.ServerPluginJarsInstaller; +import org.sonar.server.plugins.ServerPluginRepository; +import org.sonar.server.plugins.UpdateCenterClient; +import org.sonar.server.plugins.UpdateCenterMatrixFactory; import org.sonar.server.qualitygate.QgateProjectFinder; import org.sonar.server.qualitygate.QualityGates; import org.sonar.server.qualitygate.RegisterQualityGates; -import org.sonar.server.qualitygate.ws.*; -import org.sonar.server.qualityprofile.*; -import org.sonar.server.qualityprofile.ws.ActivateRuleAction; +import org.sonar.server.qualitygate.ws.QGatesAppAction; +import org.sonar.server.qualitygate.ws.QGatesCopyAction; +import org.sonar.server.qualitygate.ws.QGatesCreateAction; +import org.sonar.server.qualitygate.ws.QGatesCreateConditionAction; +import org.sonar.server.qualitygate.ws.QGatesDeleteConditionAction; +import org.sonar.server.qualitygate.ws.QGatesDeselectAction; +import org.sonar.server.qualitygate.ws.QGatesDestroyAction; +import org.sonar.server.qualitygate.ws.QGatesListAction; +import org.sonar.server.qualitygate.ws.QGatesRenameAction; +import org.sonar.server.qualitygate.ws.QGatesSearchAction; +import org.sonar.server.qualitygate.ws.QGatesSelectAction; +import org.sonar.server.qualitygate.ws.QGatesSetAsDefaultAction; +import org.sonar.server.qualitygate.ws.QGatesShowAction; +import org.sonar.server.qualitygate.ws.QGatesUnsetDefaultAction; +import org.sonar.server.qualitygate.ws.QGatesUpdateConditionAction; +import org.sonar.server.qualitygate.ws.QGatesWs; +import org.sonar.server.qualityprofile.ActiveRuleService; +import org.sonar.server.qualityprofile.DefaultProfilesCache; +import org.sonar.server.qualityprofile.ESActiveRule; +import org.sonar.server.qualityprofile.ProfilesManager; +import org.sonar.server.qualityprofile.QProfileActiveRuleOperations; +import org.sonar.server.qualityprofile.QProfileBackup; +import org.sonar.server.qualityprofile.QProfileLookup; +import org.sonar.server.qualityprofile.QProfileOperations; +import org.sonar.server.qualityprofile.QProfileProjectLookup; +import org.sonar.server.qualityprofile.QProfileProjectOperations; +import org.sonar.server.qualityprofile.QProfileRepositoryExporter; +import org.sonar.server.qualityprofile.QProfileRuleLookup; +import org.sonar.server.qualityprofile.QProfiles; +import org.sonar.server.qualityprofile.RuleActivationContextFactory; +import org.sonar.server.qualityprofile.ws.RuleActivationActions; import org.sonar.server.qualityprofile.ws.ProfilesWs; import org.sonar.server.qualityprofile.ws.QProfileRecreateBuiltInAction; import org.sonar.server.qualityprofile.ws.QProfilesWs; -import org.sonar.server.rule.*; -import org.sonar.server.rule.ws.*; +import org.sonar.server.rule.DeprecatedRulesDefinition; +import org.sonar.server.rule.ESRuleTags; +import org.sonar.server.rule.RegisterRules; +import org.sonar.server.rule.RubyRuleService; +import org.sonar.server.rule.RuleDefinitionsLoader; +import org.sonar.server.rule.RuleOperations; +import org.sonar.server.rule.RuleRegistry; +import org.sonar.server.rule.RuleRepositories; +import org.sonar.server.rule.RuleTagLookup; +import org.sonar.server.rule.RuleTagOperations; +import org.sonar.server.rule.RuleTags; +import org.sonar.server.rule.Rules; +import org.sonar.server.rule.ws.AddTagsWsHandler; +import org.sonar.server.rule.ws.RemoveTagsWsHandler; +import org.sonar.server.rule.ws.RuleSearchWsHandler; +import org.sonar.server.rule.ws.RuleShowWsHandler; +import org.sonar.server.rule.ws.RuleTagsWs; +import org.sonar.server.rule.ws.RulesWs; import org.sonar.server.rule2.RuleService; import org.sonar.server.rule2.ws.RulesWebService; import org.sonar.server.rule2.ws.SearchAction; +import org.sonar.server.rule2.ws.SetTagsAction; import org.sonar.server.rule2.ws.TagsAction; import org.sonar.server.search.IndexUtils; import org.sonar.server.source.CodeColorizers; @@ -133,7 +215,21 @@ import org.sonar.server.source.ws.ScmAction; import org.sonar.server.source.ws.ScmWriter; import org.sonar.server.source.ws.ShowAction; import org.sonar.server.source.ws.SourcesWs; -import org.sonar.server.startup.*; +import org.sonar.server.startup.CleanPreviewAnalysisCache; +import org.sonar.server.startup.CopyRequirementsFromCharacteristicsToRules; +import org.sonar.server.startup.GeneratePluginIndex; +import org.sonar.server.startup.GwtPublisher; +import org.sonar.server.startup.JdbcDriverDeployer; +import org.sonar.server.startup.LogServerId; +import org.sonar.server.startup.RegisterDashboards; +import org.sonar.server.startup.RegisterDebtModel; +import org.sonar.server.startup.RegisterMetrics; +import org.sonar.server.startup.RegisterNewMeasureFilters; +import org.sonar.server.startup.RegisterPermissionTemplates; +import org.sonar.server.startup.RegisterQualityProfiles; +import org.sonar.server.startup.RegisterServletFilters; +import org.sonar.server.startup.RenameDeprecatedPropertyKeys; +import org.sonar.server.startup.ServerMetadataPersister; import org.sonar.server.test.ws.TestsWs; import org.sonar.server.text.MacroInterpreter; import org.sonar.server.text.RubyTextService; @@ -142,9 +238,20 @@ import org.sonar.server.ui.JRubyProfiling; import org.sonar.server.ui.PageDecorations; import org.sonar.server.ui.Views; import org.sonar.server.updatecenter.ws.UpdateCenterWs; -import org.sonar.server.user.*; +import org.sonar.server.user.DefaultUserService; +import org.sonar.server.user.DoPrivileged; +import org.sonar.server.user.GroupMembershipFinder; +import org.sonar.server.user.GroupMembershipService; +import org.sonar.server.user.NewUserNotifier; +import org.sonar.server.user.SecurityRealmFactory; import org.sonar.server.user.ws.UsersWs; -import org.sonar.server.util.*; +import org.sonar.server.util.BooleanTypeValidation; +import org.sonar.server.util.FloatTypeValidation; +import org.sonar.server.util.IntegerTypeValidation; +import org.sonar.server.util.StringListTypeValidation; +import org.sonar.server.util.StringTypeValidation; +import org.sonar.server.util.TextTypeValidation; +import org.sonar.server.util.TypeValidations; import org.sonar.server.ws.ListingWs; import org.sonar.server.ws.WebServiceEngine; @@ -188,7 +295,8 @@ class ServerComponents { new TempFolderProvider(), System2.INSTANCE, org.sonar.server.rule2.RuleDao.class, - org.sonar.server.rule2.ActiveRuleDao.class + org.sonar.server.rule2.ActiveRuleDao.class, + DbClient.class )); components.addAll(CorePropertyDefinitions.all()); components.addAll(DatabaseMigrations.CLASSES); @@ -288,7 +396,7 @@ class ServerComponents { pico.addSingleton(QProfileRecreateBuiltInAction.class); pico.addSingleton(QProfilesWs.class); pico.addSingleton(ProfilesWs.class); - pico.addSingleton(ActivateRuleAction.class); + pico.addSingleton(RuleActivationActions.class); pico.addSingleton(ActiveRuleService.class); pico.addSingleton(RuleActivationContextFactory.class); @@ -317,6 +425,7 @@ class ServerComponents { pico.addSingleton(SearchAction.class); pico.addSingleton(org.sonar.server.rule2.ws.ShowAction.class); pico.addSingleton(TagsAction.class); + pico.addSingleton(SetTagsAction.class); // rule tags pico.addSingleton(ESRuleTags.class); diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleService.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleService.java index d732e9edc19..431204c8338 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleService.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleService.java @@ -26,11 +26,10 @@ import org.sonar.api.ServerComponent; import org.sonar.api.server.rule.RuleParamType; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.MyBatis; import org.sonar.core.qualityprofile.db.ActiveRuleDto; import org.sonar.core.qualityprofile.db.ActiveRuleKey; import org.sonar.core.rule.RuleParamDto; -import org.sonar.server.rule2.ActiveRuleDao; +import org.sonar.server.db.DbClient; import org.sonar.server.user.UserSession; import org.sonar.server.util.TypeValidations; @@ -41,15 +40,13 @@ import static com.google.common.collect.Lists.newArrayList; public class ActiveRuleService implements ServerComponent { - private final MyBatis myBatis; - private final ActiveRuleDao activeRuleDao; + private final DbClient db; private final TypeValidations typeValidations; private final RuleActivationContextFactory contextFactory; - public ActiveRuleService(MyBatis myBatis, ActiveRuleDao activeRuleDao, + public ActiveRuleService(DbClient db, RuleActivationContextFactory contextFactory, TypeValidations typeValidations) { - this.myBatis = myBatis; - this.activeRuleDao = activeRuleDao; + this.db = db; this.contextFactory = contextFactory; this.typeValidations = typeValidations; } @@ -58,10 +55,10 @@ public class ActiveRuleService implements ServerComponent { * Activate a rule on a Quality profile. Update configuration (severity/parameters) if the rule is already * activated. */ - public List activate(RuleActivation activation, UserSession userSession) { - verifyPermission(userSession); + public List activate(RuleActivation activation) { + verifyPermission(UserSession.get()); - DbSession dbSession = myBatis.openSession(false); + DbSession dbSession = db.openSession(false); List changes = Lists.newArrayList(); try { RuleActivationContext context = contextFactory.create(activation.getKey(), dbSession); @@ -94,12 +91,12 @@ public class ActiveRuleService implements ServerComponent { ActiveRuleDto activeRule = ActiveRuleDto.createFor(null, null /* TODO */) .setKey(change.getKey()) .setSeverity(change.getSeverity()); - activeRuleDao.insert(activeRule, dbSession); + db.activeRuleDao().insert(activeRule, dbSession); // TODO insert activeruelparams } else if (change.getType() == ActiveRuleChange.Type.DEACTIVATED) { - activeRuleDao.deleteByKey(change.getKey(), dbSession); + db.activeRuleDao().deleteByKey(change.getKey(), dbSession); } else if (change.getType() == ActiveRuleChange.Type.UPDATED) { @@ -110,13 +107,13 @@ public class ActiveRuleService implements ServerComponent { /** * Deactivate a rule on a Quality profile. Does nothing if the rule is not activated. */ - public List deactivate(ActiveRuleKey key, UserSession userSession) { - verifyPermission(userSession); + public List deactivate(ActiveRuleKey key) { + verifyPermission(UserSession.get()); throw new UnsupportedOperationException("TODO"); } - public List bulkActivate(BulkRuleActivation activation, UserSession userSession) { - verifyPermission(userSession); + public List bulkActivate(BulkRuleActivation activation) { + verifyPermission(UserSession.get()); throw new UnsupportedOperationException("TODO"); } diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java index e83a71f62fd..9cdc10b0033 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java @@ -48,7 +48,7 @@ public class RuleActivation { return this; } - public RuleActivation setParam(String key, @Nullable String value) { + public RuleActivation setParameter(String key, @Nullable String value) { String sanitizedValue = Strings.emptyToNull(value); if (value == null) { parameters.remove(key); @@ -58,6 +58,12 @@ public class RuleActivation { return this; } + public RuleActivation setParameters(Map m) { + parameters.clear(); + parameters.putAll(m); + return this; + } + public ActiveRuleKey getKey() { return key; } diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivationContextFactory.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivationContextFactory.java index 8d6240fd8db..8778f587172 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivationContextFactory.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivationContextFactory.java @@ -26,25 +26,19 @@ import org.sonar.core.persistence.DbSession; import org.sonar.core.qualityprofile.db.ActiveRuleDto; import org.sonar.core.qualityprofile.db.ActiveRuleKey; import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; -import org.sonar.core.qualityprofile.db.QualityProfileDao; import org.sonar.core.qualityprofile.db.QualityProfileDto; import org.sonar.core.qualityprofile.db.QualityProfileKey; import org.sonar.core.rule.RuleDto; -import org.sonar.server.rule2.ActiveRuleDao; -import org.sonar.server.rule2.RuleDao; +import org.sonar.server.db.DbClient; import java.util.Collection; public class RuleActivationContextFactory implements ServerComponent { - private final ActiveRuleDao activeRuleDao; - private final RuleDao ruleDao; - private final QualityProfileDao profileDao; + private final DbClient db; - public RuleActivationContextFactory(ActiveRuleDao activeRuleDao, RuleDao ruleDao, QualityProfileDao profileDao) { - this.activeRuleDao = activeRuleDao; - this.ruleDao = ruleDao; - this.profileDao = profileDao; + public RuleActivationContextFactory(DbClient db) { + this.db = db; } public RuleActivationContext create(ActiveRuleKey key, DbSession session) { @@ -68,7 +62,7 @@ public class RuleActivationContextFactory implements ServerComponent { } private RuleDto initRule(RuleKey ruleKey, RuleActivationContext context, DbSession dbSession) { - RuleDto rule = ruleDao.getByKey(ruleKey, dbSession); + RuleDto rule = db.ruleDao().getByKey(ruleKey, dbSession); if (rule == null) { throw new IllegalArgumentException("Rule not found: " + ruleKey); } @@ -76,12 +70,12 @@ public class RuleActivationContextFactory implements ServerComponent { throw new IllegalArgumentException("Rule was removed: " + ruleKey); } context.setRule(rule); - context.setRuleParams(ruleDao.findRuleParamsByRuleKey(rule.getKey(), dbSession)); + context.setRuleParams(db.ruleDao().findRuleParamsByRuleKey(rule.getKey(), dbSession)); return rule; } private QualityProfileDto initProfile(ActiveRuleKey key, RuleActivationContext context, DbSession session, boolean parent) { - QualityProfileDto profile = profileDao.selectByNameAndLanguage( + QualityProfileDto profile = db.qualityProfileDao().selectByNameAndLanguage( key.qProfile().name(), key.qProfile().lang()); if (profile == null) { throw new IllegalArgumentException("Quality profile not found: " + key.qProfile()); @@ -95,10 +89,10 @@ public class RuleActivationContextFactory implements ServerComponent { } private void initActiveRules(ActiveRuleKey key, RuleActivationContext context, DbSession session, boolean parent) { - ActiveRuleDto activeRule = activeRuleDao.getByKey(key, session); + ActiveRuleDto activeRule = db.activeRuleDao().getByKey(key, session); Collection activeRuleParams = null; if (activeRule != null) { - context.setActiveRuleParams(activeRuleDao.findParamsByActiveRule(activeRule, session)); + context.setActiveRuleParams(db.activeRuleDao().findParamsByActiveRule(activeRule, session)); } if (parent) { context.setParentActiveRule(activeRule); diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRuleAction.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRuleAction.java deleted file mode 100644 index 0b49d506377..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ActivateRuleAction.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.qualityprofile.ws; - -import org.sonar.api.rule.Severity; -import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.RequestHandler; -import org.sonar.api.server.ws.Response; -import org.sonar.api.server.ws.WebService; -import org.sonar.server.qualityprofile.ActiveRuleService; - -public class ActivateRuleAction implements RequestHandler { - - private final ActiveRuleService service; - - public ActivateRuleAction(ActiveRuleService service) { - this.service = service; - } - - void define(WebService.NewController controller) { - WebService.NewAction action = controller - .createAction("activate_rule") - .setDescription("Activate a rule on a Quality profile") - .setHandler(this) - .setPost(true) - .setSince("4.4"); - - action.createParam("profile_lang") - .setDescription("Profile language") - .setRequired(true) - .setExampleValue("java"); - - action.createParam("profile_name") - .setDescription("Profile name") - .setRequired(true) - .setExampleValue("My profile"); - - action.createParam("rule_repo") - .setDescription("Rule repository") - .setRequired(true) - .setExampleValue("squid"); - - action.createParam("rule_key") - .setDescription("Rule key") - .setRequired(true) - .setExampleValue("AvoidCycles"); - - action.createParam("severity") - .setDescription("Severity") - .setPossibleValues(Severity.ALL); - - action.createParam("params") - .setDescription("Parameters"); - } - - @Override - public void handle(Request request, Response response) throws Exception { - - } -} diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfilesWs.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfilesWs.java index d3dc74ae334..f15d3ef584f 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfilesWs.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfilesWs.java @@ -24,11 +24,11 @@ import org.sonar.api.server.ws.WebService; public class QProfilesWs implements WebService { private final QProfileRecreateBuiltInAction recreateBuiltInAction; - private final ActivateRuleAction activateRuleAction; + private final RuleActivationActions ruleActivationActions; - public QProfilesWs(QProfileRecreateBuiltInAction recreateBuiltInAction, ActivateRuleAction activateRuleAction) { + public QProfilesWs(QProfileRecreateBuiltInAction recreateBuiltInAction, RuleActivationActions ruleActivationActions) { this.recreateBuiltInAction = recreateBuiltInAction; - this.activateRuleAction = activateRuleAction; + this.ruleActivationActions = ruleActivationActions; } @Override @@ -38,7 +38,7 @@ public class QProfilesWs implements WebService { .setSince("4.4"); recreateBuiltInAction.define(controller); - activateRuleAction.define(controller); + ruleActivationActions.define(controller); controller.done(); } diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RuleActivationActions.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RuleActivationActions.java new file mode 100644 index 00000000000..9d80914d37e --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RuleActivationActions.java @@ -0,0 +1,127 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.qualityprofile.ws; + +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.RequestHandler; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.core.qualityprofile.db.ActiveRuleKey; +import org.sonar.core.qualityprofile.db.QualityProfileKey; +import org.sonar.server.qualityprofile.ActiveRuleService; +import org.sonar.server.qualityprofile.RuleActivation; + +public class RuleActivationActions { + + private final ActiveRuleService service; + + public RuleActivationActions(ActiveRuleService service) { + this.service = service; + } + + void define(WebService.NewController controller) { + defineActivateAction(controller); + defineDeactivateAction(controller); + } + + private void defineActivateAction(WebService.NewController controller) { + WebService.NewAction activate = controller + .createAction("activate_rule") + .setDescription("Activate a rule on a Quality profile") + .setHandler(new RequestHandler() { + @Override + public void handle(Request request, Response response) throws Exception { + activate(request, response); + } + }) + .setPost(true) + .setSince("4.4"); + + defineActiveRuleKeyParameters(activate); + + activate.createParam("severity") + .setDescription("Severity") + .setPossibleValues(Severity.ALL); + + activate.createParam("params") + .setDescription("Parameters"); + } + + private void defineDeactivateAction(WebService.NewController controller) { + WebService.NewAction deactivate = controller + .createAction("deactivate_rule") + .setDescription("Deactivate a rule on a Quality profile") + .setHandler(new RequestHandler() { + @Override + public void handle(Request request, Response response) throws Exception { + deactivate(request, response); + } + }) + .setPost(true) + .setSince("4.4"); + defineActiveRuleKeyParameters(deactivate); + } + + private void defineActiveRuleKeyParameters(WebService.NewAction action) { + action.createParam("profile_lang") + .setDescription("Profile language") + .setRequired(true) + .setExampleValue("java"); + + action.createParam("profile_name") + .setDescription("Profile name") + .setRequired(true) + .setExampleValue("My profile"); + + action.createParam("rule_repo") + .setDescription("Rule repository") + .setRequired(true) + .setExampleValue("squid"); + + action.createParam("rule_key") + .setDescription("Rule key") + .setRequired(true) + .setExampleValue("AvoidCycles"); + } + + private void activate(Request request, Response response) throws Exception { + ActiveRuleKey key = readKey(request); + RuleActivation activation = new RuleActivation(key); + activation.setSeverity(request.param("severity")); + String params = request.param("params"); + if (params != null) { + activation.setParameters(KeyValueFormat.parse(params)); + } + service.activate(activation); + } + + private void deactivate(Request request, Response response) throws Exception { + service.deactivate(readKey(request)); + } + + private ActiveRuleKey readKey(Request request) { + return ActiveRuleKey.of( + QualityProfileKey.of(request.mandatoryParam("profile_name"), request.mandatoryParam("profile_lang")), + RuleKey.of(request.mandatoryParam("rule_repo"), request.mandatoryParam("rule_key"))); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/ActiveRuleNormalizer.java b/sonar-server/src/main/java/org/sonar/server/rule2/ActiveRuleNormalizer.java index 93849fecd10..51a2ca8b531 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule2/ActiveRuleNormalizer.java +++ b/sonar-server/src/main/java/org/sonar/server/rule2/ActiveRuleNormalizer.java @@ -22,18 +22,16 @@ package org.sonar.server.rule2; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.common.xcontent.XContentBuilder; import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.MyBatis; import org.sonar.core.qualityprofile.db.ActiveRuleDto; import org.sonar.core.qualityprofile.db.ActiveRuleKey; import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; +import org.sonar.server.db.DbClient; import org.sonar.server.search.BaseNormalizer; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; public class ActiveRuleNormalizer extends BaseNormalizer { - private final ActiveRuleDao activeRuleDao; - public static enum ActiveRuleField { OVERRIDE("override"), INHERITANCE("inheritance"), @@ -78,16 +76,15 @@ public class ActiveRuleNormalizer extends BaseNormalizer implements BatchComponent, ServerComponent { +public class RuleDao extends BaseDao implements ServerComponent { public RuleDao() { super(new RuleIndexDefinition(), RuleMapper.class); @@ -78,7 +77,7 @@ public class RuleDao extends BaseDao implements Ba } @CheckForNull - public RuleDto getParent(RuleDto rule, DbSession session){ + public RuleDto getParent(RuleDto rule, DbSession session) { Preconditions.checkNotNull(rule.getParentId(), "Rule has no persisted parent!"); return mapper(session).selectById(rule.getParentId()); } @@ -96,9 +95,11 @@ public class RuleDao extends BaseDao implements Ba return keys; } - /** Finder methods for Rules */ + /** + * Finder methods for Rules + */ - public List findByNonManual(DbSession session){ + public List findByNonManual(DbSession session) { return mapper(session).selectNonManual(); } @@ -115,7 +116,9 @@ public class RuleDao extends BaseDao implements Ba return ImmutableList.of(mapper(session).selectByName(name)); } - /** Nested DTO RuleParams */ + /** + * Nested DTO RuleParams + */ public void addRuleParam(RuleDto rule, RuleParamDto paramDto, DbSession session) { Preconditions.checkNotNull(rule.getId(), "Rule id must be set"); @@ -130,7 +133,7 @@ public class RuleDao extends BaseDao implements Ba paramDto.setRuleId(rule.getId()); System.out.println("paramDto = " + paramDto); session.enqueue(new EmbeddedIndexAction(this.getIndexType(), IndexAction.Method.UPDATE, paramDto, rule.getKey())); - mapper(session).updateParameter(paramDto); + mapper(session).updateParameter(paramDto); return paramDto; } @@ -140,7 +143,9 @@ public class RuleDao extends BaseDao implements Ba session.enqueue(new EmbeddedIndexAction(this.getIndexType(), IndexAction.Method.DELETE, param, rule.getKey())); } - /** Finder methods for RuleParams */ + /** + * Finder methods for RuleParams + */ public List findAllRuleParams(DbSession session) { return mapper(session).selectAllParams(); @@ -152,7 +157,7 @@ public class RuleDao extends BaseDao implements Ba public List findRuleParamsByRules(List ruleDtos, DbSession session) { List ruleParamDtos = new ArrayList(); - for(RuleDto rule:ruleDtos){ + for (RuleDto rule : ruleDtos) { ruleParamDtos.addAll(findRuleParamsByRuleKey(rule.getKey(), session)); } return ruleParamDtos; diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleIndex.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleIndex.java index de064ba5ceb..319274d5fb5 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule2/RuleIndex.java +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleIndex.java @@ -32,8 +32,6 @@ import org.elasticsearch.search.facet.terms.TermsFacet; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; import org.sonar.core.cluster.WorkQueue; @@ -54,8 +52,6 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; public class RuleIndex extends BaseIndex { - private static final Logger LOG = LoggerFactory.getLogger(RuleIndex.class); - public static final Set PUBLIC_FIELDS = ImmutableSet.of( RuleField.KEY.key(), RuleField.NAME.key(), @@ -86,38 +82,38 @@ public class RuleIndex extends BaseIndex { protected XContentBuilder getIndexSettings() throws IOException { return jsonBuilder().startObject() .startObject("index") - .field("number_of_replicas", 0) - .field("number_of_shards", 3) - .startObject("mapper") - .field("dynamic", true) - .endObject() - .startObject("analysis") - .startObject("analyzer") - .startObject("path_analyzer") - .field("type", "custom") - .field("tokenizer", "path_hierarchy") - .endObject() - .startObject("sortable") - .field("type", "custom") - .field("tokenizer", "keyword") - .field("filter", "lowercase") - .endObject() - .startObject("rule_name") - .field("type", "custom") - .field("tokenizer", "standard") - .array("filter", "lowercase", "rule_name_ngram") - .endObject() - .endObject() - .startObject("filter") - .startObject("rule_name_ngram") - .field("type", "nGram") - .field("min_gram", 3) - .field("max_gram", 5) - .array("token_chars", "letter", "digit") - .endObject() - .endObject() - .endObject() - .endObject() + .field("number_of_replicas", 0) + .field("number_of_shards", 3) + .startObject("mapper") + .field("dynamic", true) + .endObject() + .startObject("analysis") + .startObject("analyzer") + .startObject("path_analyzer") + .field("type", "custom") + .field("tokenizer", "path_hierarchy") + .endObject() + .startObject("sortable") + .field("type", "custom") + .field("tokenizer", "keyword") + .field("filter", "lowercase") + .endObject() + .startObject("rule_name") + .field("type", "custom") + .field("tokenizer", "standard") + .array("filter", "lowercase", "rule_name_ngram") + .endObject() + .endObject() + .startObject("filter") + .startObject("rule_name_ngram") + .field("type", "nGram") + .field("min_gram", 3) + .field("max_gram", 5) + .array("token_chars", "letter", "digit") + .endObject() + .endObject() + .endObject() + .endObject() .endObject(); } @@ -158,19 +154,19 @@ public class RuleIndex extends BaseIndex { .endObject(); mapping.startObject(RuleField.NAME.key()) - .field("type", "multi_field") - .startObject("fields") - .startObject(RuleField.NAME.key()) - .field("type", "string") - .field("index", "analyzed") - .endObject() - .startObject("search") - .field("type", "string") - .field("index", "analyzed") - .field("index_analyzer", "rule_name") - .field("search_analyzer", "standard") - .endObject() - .endObject() + .field("type", "multi_field") + .startObject("fields") + .startObject(RuleField.NAME.key()) + .field("type", "string") + .field("index", "analyzed") + .endObject() + .startObject("search") + .field("type", "string") + .field("index", "analyzed") + .field("index_analyzer", "rule_name") + .field("search_analyzer", "standard") + .endObject() + .endObject() .endObject(); mapping.startObject(RuleField.ACTIVE.key()) @@ -194,20 +190,20 @@ public class RuleIndex extends BaseIndex { .setIndices(this.getIndexName()); /* Integrate Facets */ - if(options.isFacet()) { + if (options.isFacet()) { this.setFacets(esSearch); } /* integrate Query Sort */ - if(query.getSortField() != null){ + if (query.getSortField() != null) { FieldSortBuilder sort = SortBuilders.fieldSort(query.getSortField().field().key()); - if(query.isAscendingSort()){ + if (query.isAscendingSort()) { sort.order(SortOrder.ASC); } else { sort.order(SortOrder.DESC); } esSearch.addSort(sort); - } else if(query.getQueryText() != null && !query.getQueryText().isEmpty()){ + } else if (query.getQueryText() != null && !query.getQueryText().isEmpty()) { esSearch.addSort(SortBuilders.scoreSort()); } else { esSearch.addSort(RuleField.UPDATED_AT.key(), SortOrder.DESC); @@ -220,7 +216,7 @@ public class RuleIndex extends BaseIndex { /* integrate Option's Fields */ if (options.getFieldsToReturn() != null && !options.getFieldsToReturn().isEmpty()) { - for(String field:options.getFieldsToReturn()) { + for (String field : options.getFieldsToReturn()) { esSearch.addField(field); } } else { @@ -243,7 +239,7 @@ public class RuleIndex extends BaseIndex { qb = QueryBuilders.multiMatchQuery(query.getQueryText(), "_id", RuleField.NAME.key(), - RuleField.NAME.key()+".search", + RuleField.NAME.key() + ".search", RuleField.HTML_DESCRIPTION.key(), RuleField.KEY.key(), RuleField.LANGUAGE.key(), @@ -265,7 +261,7 @@ public class RuleIndex extends BaseIndex { this.addMultiFieldTermFilter(query.getTags(), fb, RuleField.TAGS.key(), RuleField.SYSTEM_TAGS.key()); - if(query.getStatuses() != null && !query.getStatuses().isEmpty()) { + if (query.getStatuses() != null && !query.getStatuses().isEmpty()) { Collection stringStatus = new ArrayList(); for (RuleStatus status : query.getStatuses()) { stringStatus.add(status.name()); @@ -273,7 +269,7 @@ public class RuleIndex extends BaseIndex { this.addTermFilter(RuleField.STATUS.key(), stringStatus, fb); } - if((query.getLanguages() != null && !query.getLanguages().isEmpty()) || + if ((query.getLanguages() != null && !query.getLanguages().isEmpty()) || (query.getRepositories() != null && !query.getRepositories().isEmpty()) || (query.getSeverities() != null && !query.getSeverities().isEmpty()) || (query.getTags() != null && !query.getTags().isEmpty()) || @@ -285,7 +281,7 @@ public class RuleIndex extends BaseIndex { } } - private void setFacets(SearchRequestBuilder query){ + private void setFacets(SearchRequestBuilder query) { //TODO there are no aggregation in 0.9!!! Must use facet... /* the Lang facet */ @@ -313,7 +309,7 @@ public class RuleIndex extends BaseIndex { @Override protected Rule getSearchResult(Map response) { - if(response == null){ + if (response == null) { throw new IllegalStateException("Cannot construct Rule with null map!!!"); } return new RuleDoc(response); diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleNormalizer.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleNormalizer.java index 3ad19f00720..c510894b561 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule2/RuleNormalizer.java +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleNormalizer.java @@ -27,14 +27,13 @@ import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleParamDto; +import org.sonar.server.db.DbClient; import org.sonar.server.search.BaseNormalizer; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; public class RuleNormalizer extends BaseNormalizer { - private final RuleDao ruleDao; - public static enum RuleField { KEY("key"), REPOSITORY("repo"), @@ -90,16 +89,15 @@ public class RuleNormalizer extends BaseNormalizer { } } - public RuleNormalizer(MyBatis myBatis, RuleDao ruleDao) { - super(myBatis); - this.ruleDao = ruleDao; + public RuleNormalizer(DbClient db) { + super(db); } @Override public UpdateRequest normalize(RuleKey key) { - DbSession dbSession = getMyBatis().openSession(false); + DbSession dbSession = db().openSession(false); try { - return normalize(ruleDao.getByKey(key, dbSession)); + return normalize(db().ruleDao().getByKey(key, dbSession)); } finally { dbSession.close(); } diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleService.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleService.java index 84d4c1de835..24bc177080f 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule2/RuleService.java +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleService.java @@ -21,32 +21,34 @@ package org.sonar.server.rule2; import org.sonar.api.ServerComponent; import org.sonar.api.rule.RuleKey; -import org.sonar.core.rule.RuleDao; -import org.sonar.server.search.Hit; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.rule.RuleDto; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.search.QueryOptions; import org.sonar.server.search.Result; +import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; -import java.util.List; +import java.util.Set; /** * @since 4.4 */ public class RuleService implements ServerComponent { - private RuleIndex index; + private final RuleIndex index; + private final DbClient db; - public RuleService(RuleIndex index) { + public RuleService(RuleIndex index, DbClient db) { this.index = index; + this.db = db; } @CheckForNull public Rule getByKey(RuleKey key) { - Rule hit = index.getByKey(key); - if (hit != null) { - return hit; - } - return null; + return index.getByKey(key); } public RuleQuery newRuleQuery() { @@ -63,10 +65,33 @@ public class RuleService implements ServerComponent { /** * List all tags */ - public List listTags() { + public Set listTags() { throw new UnsupportedOperationException("TODO"); } + public void setTags(RuleKey ruleKey, Set tags) { + checkAdminPermission(UserSession.get()); + + DbSession dbSession = db.openSession(false); + try { + RuleDto rule = db.ruleDao().getByKey(ruleKey, dbSession); + if (rule == null) { + throw new NotFoundException(String.format("Rule %s not found", ruleKey)); + } + boolean changed = RuleTagHelper.applyTags(rule, tags); + if (changed) { + db.ruleDao().update(rule, dbSession); + dbSession.commit(); + } + } finally { + dbSession.close(); + } + } + + private void checkAdminPermission(UserSession userSession) { + userSession.checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN); + } + public RuleService refresh() { this.index.refresh(); return this; diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleTagHelper.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleTagHelper.java new file mode 100644 index 00000000000..99c95352754 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleTagHelper.java @@ -0,0 +1,55 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.rule2; + +import com.google.common.base.Predicate; +import com.google.common.collect.Sets; +import org.sonar.api.server.rule.RuleTagFormat; +import org.sonar.core.rule.RuleDto; + +import javax.annotation.Nullable; +import java.util.Set; + +class RuleTagHelper { + + private RuleTagHelper() { + // only static stuff + } + + /** + * Validates tags and resolves conflicts between user and system tags. + */ + static boolean applyTags(RuleDto rule, Set tags) { + for (String tag : tags) { + RuleTagFormat.validate(tag); + } + + Set initialTags = rule.getTags(); + final Set systemTags = rule.getSystemTags(); + Set withoutSystemTags = Sets.filter(tags, new Predicate() { + @Override + public boolean apply(@Nullable String input) { + return input != null && !systemTags.contains(input); + } + }); + rule.setTags(withoutSystemTags); + return Sets.difference(initialTags, withoutSystemTags).size() > 0; + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/ws/RulesWebService.java b/sonar-server/src/main/java/org/sonar/server/rule2/ws/RulesWebService.java index 11756b8fb96..b665e0c2749 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule2/ws/RulesWebService.java +++ b/sonar-server/src/main/java/org/sonar/server/rule2/ws/RulesWebService.java @@ -26,11 +26,13 @@ public class RulesWebService implements WebService { private final SearchAction search; private final ShowAction show; private final TagsAction tags; + private final SetTagsAction setTags; - public RulesWebService(SearchAction search, ShowAction show, TagsAction tags) { + public RulesWebService(SearchAction search, ShowAction show, TagsAction tags, SetTagsAction setTags) { this.search = search; this.show = show; this.tags = tags; + this.setTags = setTags; } @Override @@ -42,6 +44,7 @@ public class RulesWebService implements WebService { search.define(controller); show.define(controller); tags.define(controller); + setTags.define(controller); controller.done(); } diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/ws/SetTagsAction.java b/sonar-server/src/main/java/org/sonar/server/rule2/ws/SetTagsAction.java new file mode 100644 index 00000000000..170f1b179c7 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule2/ws/SetTagsAction.java @@ -0,0 +1,70 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.rule2.ws; + +import com.google.common.collect.Sets; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.RequestHandler; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.server.rule2.RuleService; + +import java.util.Set; + +public class SetTagsAction implements RequestHandler { + + private final RuleService service; + + public SetTagsAction(RuleService service) { + this.service = service; + } + + void define(WebService.NewController controller) { + WebService.NewAction setTags = controller + .createAction("set_tags") + .setDescription("Set the tags of a coding rule") + .setSince("4.4") + .setPost(true) + .setHandler(this); + setTags + .createParam("repo") + .setRequired(true) + .setDescription("Repository key") + .setExampleValue("javascript"); + setTags + .createParam("key") + .setRequired(true) + .setDescription("Rule key") + .setExampleValue("EmptyBlock"); + setTags + .createParam("tags") + .setDescription("Comma-separated list of tags. Blank value is used to remove all tags.") + .setRequired(true) + .setExampleValue("java8,security"); + } + + @Override + public void handle(Request request, Response response) { + RuleKey key = RuleKey.of(request.mandatoryParam("repo"), request.mandatoryParam("key")); + Set tags = Sets.newHashSet(request.paramAsStrings("tags")); + service.setTags(key, tags); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/ws/TagsAction.java b/sonar-server/src/main/java/org/sonar/server/rule2/ws/TagsAction.java index 38f028afa23..e16fb8b51b5 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule2/ws/TagsAction.java +++ b/sonar-server/src/main/java/org/sonar/server/rule2/ws/TagsAction.java @@ -26,7 +26,7 @@ import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.text.JsonWriter; import org.sonar.server.rule2.RuleService; -import java.util.List; +import java.util.Set; public class TagsAction implements RequestHandler { @@ -39,14 +39,14 @@ public class TagsAction implements RequestHandler { void define(WebService.NewController controller) { controller .createAction("tags") - .setDescription("Search for a collection of relevant rules matching a specified query") + .setDescription("List all rule tags") .setSince("4.4") .setHandler(this); } @Override public void handle(Request request, Response response) { - List tags = service.listTags(); + Set tags = service.listTags(); JsonWriter json = response.newJsonWriter().beginObject(); json.name("tags").beginArray(); for (String tag : tags) { diff --git a/sonar-server/src/main/java/org/sonar/server/search/BaseNormalizer.java b/sonar-server/src/main/java/org/sonar/server/search/BaseNormalizer.java index 8cea549b397..d1bb3e914ba 100644 --- a/sonar-server/src/main/java/org/sonar/server/search/BaseNormalizer.java +++ b/sonar-server/src/main/java/org/sonar/server/search/BaseNormalizer.java @@ -24,21 +24,23 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.core.db.Dto; -import org.sonar.core.persistence.MyBatis; +import org.sonar.server.db.DbClient; import java.io.IOException; import java.io.Serializable; public abstract class BaseNormalizer, K extends Serializable> { - private MyBatis myBatis; + private static final Logger LOG = LoggerFactory.getLogger(BaseNormalizer.class); + + private final DbClient db; - protected BaseNormalizer(MyBatis mybatis) { - this.myBatis = mybatis; + protected BaseNormalizer(DbClient db) { + this.db = db; } - protected MyBatis getMyBatis() { - return myBatis; + protected DbClient db() { + return db; } public boolean canNormalize(Class objectClass, Class keyClass) { @@ -63,8 +65,6 @@ public abstract class BaseNormalizer, K extends Serializable> { public abstract UpdateRequest normalize(E dto); - private static final Logger LOG = LoggerFactory.getLogger(BaseNormalizer.class); - protected void indexField(String field, Object value, XContentBuilder document) { try { document.field(field, value); diff --git a/sonar-server/src/test/java/org/sonar/server/db/DbClientTest.java b/sonar-server/src/test/java/org/sonar/server/db/DbClientTest.java new file mode 100644 index 00000000000..ef296d23ecc --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/db/DbClientTest.java @@ -0,0 +1,58 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.db; + +import org.junit.Rule; +import org.junit.Test; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.persistence.TestDatabase; +import org.sonar.core.qualityprofile.db.QualityProfileDao; +import org.sonar.server.rule2.ActiveRuleDao; +import org.sonar.server.rule2.RuleDao; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class DbClientTest { + + @Rule + public TestDatabase db = new TestDatabase(); + + @Test + public void facade() throws Exception { + MyBatis myBatis = db.myBatis(); + RuleDao ruleDao = mock(RuleDao.class); + ActiveRuleDao activeRuleDao = mock(ActiveRuleDao.class); + QualityProfileDao qualityProfileDao = mock(QualityProfileDao.class); + + DbClient client = new DbClient(myBatis, ruleDao, activeRuleDao, qualityProfileDao); + + DbSession dbSession = client.openSession(true); + assertThat(dbSession).isNotNull(); + assertThat(dbSession.getConnection().isClosed()).isFalse(); + dbSession.close(); + + // DAO + assertThat(client.ruleDao()).isSameAs(ruleDao); + assertThat(client.activeRuleDao()).isSameAs(activeRuleDao); + assertThat(client.qualityProfileDao()).isSameAs(qualityProfileDao); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileRecreateBuiltInActionTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileRecreateBuiltInActionTest.java index 5426970eef9..e6de1bc70dd 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileRecreateBuiltInActionTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileRecreateBuiltInActionTest.java @@ -46,7 +46,7 @@ public class QProfileRecreateBuiltInActionTest { public void setUp() throws Exception { tester = new WsTester(new QProfilesWs( new QProfileRecreateBuiltInAction(qProfileBackup), - new ActivateRuleAction(mock(ActiveRuleService.class)))); + new RuleActivationActions(mock(ActiveRuleService.class)))); } @Test diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsTest.java index 8bb83cb9451..0e982e50a1f 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsTest.java @@ -36,7 +36,7 @@ public class QProfilesWsTest { @Before public void setUp() { - controller = new WsTester(new QProfilesWs(new QProfileRecreateBuiltInAction(mock(QProfileBackup.class)), new ActivateRuleAction(mock(ActiveRuleService.class)))) + controller = new WsTester(new QProfilesWs(new QProfileRecreateBuiltInAction(mock(QProfileBackup.class)), new RuleActivationActions(mock(ActiveRuleService.class)))) .controller("api/qualityprofiles"); } @@ -45,11 +45,11 @@ public class QProfilesWsTest { assertThat(controller).isNotNull(); assertThat(controller.path()).isEqualTo("api/qualityprofiles"); assertThat(controller.description()).isNotEmpty(); - assertThat(controller.actions()).hasSize(2); + assertThat(controller.actions()).hasSize(3); } @Test - public void define_recreate_built_in_action() throws Exception { + public void define_recreate_built_action() throws Exception { WebService.Action restoreProfiles = controller.action("recreate_built_in"); assertThat(restoreProfiles).isNotNull(); assertThat(restoreProfiles.isPost()).isTrue(); @@ -57,11 +57,19 @@ public class QProfilesWsTest { } @Test - public void define_activate_rule_in_action() throws Exception { + public void define_activate_rule_action() throws Exception { WebService.Action restoreProfiles = controller.action("activate_rule"); assertThat(restoreProfiles).isNotNull(); assertThat(restoreProfiles.isPost()).isTrue(); assertThat(restoreProfiles.params()).hasSize(6); } + @Test + public void define_deactivate_rule_action() throws Exception { + WebService.Action restoreProfiles = controller.action("deactivate_rule"); + assertThat(restoreProfiles).isNotNull(); + assertThat(restoreProfiles.isPost()).isTrue(); + assertThat(restoreProfiles.params()).hasSize(4); + } + } diff --git a/sonar-server/src/test/java/org/sonar/server/rule2/RuleServiceMediumTest.java b/sonar-server/src/test/java/org/sonar/server/rule2/RuleServiceMediumTest.java index 60a607955ba..4762d18919a 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule2/RuleServiceMediumTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule2/RuleServiceMediumTest.java @@ -21,9 +21,11 @@ package org.sonar.server.rule2; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Ignore; import org.junit.Test; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; @@ -31,25 +33,32 @@ import org.sonar.api.rule.Severity; import org.sonar.api.server.rule.RuleParamType; import org.sonar.api.utils.DateUtils; import org.sonar.check.Cardinality; +import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleParamDto; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.tester.ServerTester; +import org.sonar.server.user.MockUserSession; +import java.util.Collections; import java.util.List; +import java.util.Set; import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; public class RuleServiceMediumTest { @ClassRule - public static ServerTester tester = new ServerTester() - .setProperty("sonar.es.http.port","9200"); + public static ServerTester tester = new ServerTester(); MyBatis myBatis = tester.get(MyBatis.class); RuleDao dao = tester.get(RuleDao.class); RuleIndex index = tester.get(RuleIndex.class); + RuleService service = tester.get(RuleService.class); DbSession dbSession; @Before @@ -81,10 +90,6 @@ public class RuleServiceMediumTest { // verify that rule is indexed in es index.refresh(); - -// Thread.sleep(10000000); - - Rule hit = index.getByKey(ruleKey); assertThat(hit).isNotNull(); assertThat(hit.key().repository()).isEqualTo(ruleKey.repository()); @@ -100,7 +105,6 @@ public class RuleServiceMediumTest { assertThat(hit.template()).isFalse(); assertThat(hit.tags()).containsOnly("tag1", "tag2"); assertThat(hit.systemTags()).containsOnly("systag1", "systag2"); - } @Test @@ -135,7 +139,6 @@ public class RuleServiceMediumTest { assertThat(hit).isNotNull(); assertThat(hit.key()).isNotNull(); - RuleService service = tester.get(RuleService.class); Rule rule = service.getByKey(ruleKey); @@ -143,6 +146,55 @@ public class RuleServiceMediumTest { assertThat(Iterables.getLast(rule.params(), null).key()).isEqualTo("max"); } + @Test + @Ignore + public void setTags() { + MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + + // insert db + RuleKey rule1 = RuleKey.of("javascript", "S001"); + dao.insert(newRuleDto(rule1) + .setTags(Sets.newHashSet("security")) + .setSystemTags(Collections.emptySet()), + dbSession + ); + + RuleKey rule2 = RuleKey.of("java", "S001"); + dao.insert(newRuleDto(rule2) + .setTags(Sets.newHashSet("toberemoved")) + .setSystemTags(Sets.newHashSet("bug")), dbSession); + dbSession.commit(); + + service.setTags(rule2, Sets.newHashSet("bug", "security")); + + // verify that tags are indexed in es + service.refresh(); + Set tags = service.listTags(); + assertThat(tags).containsOnly("security", "java8", "bug"); + } + + @Test + public void setTags_fail_if_rule_does_not_exist() { + try { + MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + service.setTags(RuleKey.of("java", "S001"), Sets.newHashSet("bug", "security")); + fail(); + } catch (NotFoundException e) { + assertThat(e).hasMessage("Rule java:S001 not found"); + } + } + + @Test + public void setTags_fail_if_not_permitted() { + try { + MockUserSession.set(); + service.setTags(RuleKey.of("java", "S001"), Sets.newHashSet("bug", "security")); + fail(); + } catch (ForbiddenException e) { + assertThat(e).hasMessage("Insufficient privileges"); + } + } + private RuleDto newRuleDto(RuleKey ruleKey) { return new RuleDto() .setRuleKey(ruleKey.rule()) diff --git a/sonar-server/src/test/java/org/sonar/server/rule2/RuleTagHelperTest.java b/sonar-server/src/test/java/org/sonar/server/rule2/RuleTagHelperTest.java new file mode 100644 index 00000000000..fe51a8cb689 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/rule2/RuleTagHelperTest.java @@ -0,0 +1,72 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.rule2; + +import com.google.common.collect.Sets; +import org.junit.Test; +import org.sonar.core.rule.RuleDto; + +import java.util.Collections; + +import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; + +public class RuleTagHelperTest { + + @Test + public void applyTags() throws Exception { + RuleDto rule = new RuleDto().setTags(Sets.newHashSet("performance")); + RuleTagHelper.applyTags(rule, Sets.newHashSet("java8", "security")); + assertThat(rule.getTags()).containsOnly("java8", "security"); + } + + @Test + public void applyTags_remove_all_existing_tags() throws Exception { + RuleDto rule = new RuleDto().setTags(Sets.newHashSet("performance")); + RuleTagHelper.applyTags(rule, Collections.emptySet()); + assertThat(rule.getTags()).isEmpty(); + } + + @Test + public void applyTags_validate_format() throws Exception { + RuleDto rule = new RuleDto(); + RuleTagHelper.applyTags(rule, Sets.newHashSet("java8", "security")); + assertThat(rule.getTags()).containsOnly("java8", "security"); + + try { + RuleTagHelper.applyTags(rule, Sets.newHashSet("Java Eight")); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).startsWith("Tag 'Java Eight' is invalid"); + } + } + + @Test + public void applyTags_do_not_duplicate_system_tags() throws Exception { + RuleDto rule = new RuleDto() + .setTags(Sets.newHashSet("performance")) + .setSystemTags(Sets.newHashSet("security")); + + RuleTagHelper.applyTags(rule, Sets.newHashSet("java8", "security")); + + assertThat(rule.getTags()).containsOnly("java8"); + assertThat(rule.getSystemTags()).containsOnly("security"); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/rule2/ws/RulesWebServiceTest.java b/sonar-server/src/test/java/org/sonar/server/rule2/ws/RulesWebServiceTest.java index 82e49967258..3ab3e0751c2 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule2/ws/RulesWebServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule2/ws/RulesWebServiceTest.java @@ -67,10 +67,11 @@ public class RulesWebServiceTest { WebService.Controller controller = context.controller("api/rules2"); assertThat(controller).isNotNull(); - assertThat(controller.actions()).hasSize(3); + assertThat(controller.actions()).hasSize(4); assertThat(controller.action("search")).isNotNull(); assertThat(controller.action("show")).isNotNull(); assertThat(controller.action("tags")).isNotNull(); + assertThat(controller.action("set_tags")).isNotNull(); } @Test @@ -86,7 +87,6 @@ public class RulesWebServiceTest { @Test public void search_2_rules() throws Exception { - DbSession session = tester.get(MyBatis.class).openSession(false); ruleDao.insert(newRuleDto(RuleKey.of("javascript", "S001")), session); ruleDao.insert(newRuleDto(RuleKey.of("javascript", "S002")), session); @@ -96,10 +96,8 @@ public class RulesWebServiceTest { MockUserSession.set(); WsTester.TestRequest request = wsTester.newGetRequest("api/rules2", "search"); - System.out.println("request.toString() = " + request.toString()); - WsTester.Result result = request.execute(); - System.out.println("result.outputAsString() = " + result.outputAsString()); + //TODO }