diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-01-15 16:57:50 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-01-15 16:58:00 +0100 |
commit | 3d5df31e4f4089e90ce073961981729ccb15c528 (patch) | |
tree | 095a78305e8b64fd4a432a15a35fb8dcc475209f /sonar-server | |
parent | 8b6abcc7f9de839d1466dc057ce2d024c5a96e80 (diff) | |
download | sonarqube-3d5df31e4f4089e90ce073961981729ccb15c528.tar.gz sonarqube-3d5df31e4f4089e90ce073961981729ccb15c528.zip |
SONAR-4923 Restore profile now uses Java facade
Diffstat (limited to 'sonar-server')
15 files changed, 527 insertions, 63 deletions
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 0603f1025fb..1b13fb1ad9a 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 @@ -283,6 +283,7 @@ public final class Platform { servicesContainer.addSingleton(QProfileRuleOperations.class); servicesContainer.addSingleton(QProfileProjectOperations.class); servicesContainer.addSingleton(QProfileProjectLookup.class); + servicesContainer.addSingleton(QProfileBackup.class); // users servicesContainer.addSingleton(HibernateUserFinder.class); diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperations.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperations.java index 6296fb5d478..b3b0f22f536 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperations.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperations.java @@ -394,7 +394,7 @@ public class QProfileActiveRuleOperations implements ServerComponent { private void reindexInheritanceResult(ProfilesManager.RuleInheritanceActions actions, SqlSession session) { ruleRegistry.deleteActiveRules(actions.idsToDelete()); - ruleRegistry.bulkIndexActiveRules(actions.idsToIndex(), session); + ruleRegistry.bulkIndexActiveRuleIds(actions.idsToIndex(), session); } private void reindexActiveRule(ActiveRuleDto activeRuleDto, SqlSession session) { diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackup.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackup.java new file mode 100644 index 00000000000..e55d6b03e0b --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackup.java @@ -0,0 +1,159 @@ +/* + * 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.apache.ibatis.session.SqlSession; +import org.sonar.api.ServerComponent; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.profiles.XMLProfileParser; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.preview.PreviewCache; +import org.sonar.jpa.session.DatabaseSessionFactory; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.rule.RuleRegistry; +import org.sonar.server.user.UserSession; + +import java.io.StringReader; +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; + +public class QProfileBackup implements ServerComponent { + + private final DatabaseSessionFactory sessionFactory; + private final XMLProfileParser xmlProfileParser; + + private final MyBatis myBatis; + private final QProfileOperations qProfileOperations; + private final QProfileLookup qProfileLookup; + private final RuleRegistry ruleRegistry; + private final PreviewCache dryRunCache; + + public QProfileBackup(DatabaseSessionFactory sessionFactory, XMLProfileParser xmlProfileParser, MyBatis myBatis, + QProfileOperations qProfileOperations, QProfileLookup qProfileLookup, RuleRegistry ruleRegistry, PreviewCache dryRunCache) { + + this.sessionFactory = sessionFactory; + this.xmlProfileParser = xmlProfileParser; + this.myBatis = myBatis; + this.qProfileOperations = qProfileOperations; + this.qProfileLookup = qProfileLookup; + this.ruleRegistry = ruleRegistry; + this.dryRunCache = dryRunCache; + } + + public Result restore(String xmlBackup, boolean deleteExisting, UserSession userSession) { + checkPermission(userSession); + + Result result = new Result(); + ValidationMessages messages = ValidationMessages.create(); + RulesProfile importedProfile = xmlProfileParser.parse(new StringReader(xmlBackup), messages); + processValidationMessages(messages, result); + if (importedProfile != null) { + DatabaseSession hibernateSession = sessionFactory.getSession(); + checkProfileDoesNotExists(importedProfile, deleteExisting, hibernateSession); + hibernateSession.saveWithoutFlush(importedProfile); + hibernateSession.commit(); + + SqlSession session = myBatis.openSession(); + try { + QProfile profile = qProfileLookup.profile(importedProfile.getId()); + ruleRegistry.bulkIndexProfile(profile.id(), session); + result.setProfile(profile); + } finally { + MyBatis.closeQuietly(session); + } + dryRunCache.reportGlobalModification(); + } + return result; + } + + private void checkProfileDoesNotExists(RulesProfile importedProfile, boolean deleteExisting, DatabaseSession hibernateSession) { + RulesProfile existingProfile = hibernateSession.getSingleResult(RulesProfile.class, "name", importedProfile.getName(), "language", importedProfile.getLanguage()); + if (existingProfile != null && !deleteExisting) { + throw BadRequestException.of("The profile " + existingProfile + " already exists. Please delete it before restoring."); + } + if (existingProfile != null) { + // TODO use QProfileOperations to do that (For ES unindexing) + hibernateSession.removeWithoutFlush(existingProfile); + hibernateSession.commit(); + } + } + + private void processValidationMessages(ValidationMessages messages, Result result) { + if (!messages.getErrors().isEmpty()) { + List<BadRequestException.Message> errors = newArrayList(); + for (String error : messages.getErrors()) { + errors.add(BadRequestException.Message.of(error)); + } + throw BadRequestException.of("Fail to restore profile", errors); + } + result.setWarnings(messages.getWarnings()); + result.setInfos(messages.getInfos()); + } + + private void checkPermission(UserSession userSession) { + userSession.checkLoggedIn(); + userSession.checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN); + } + + public static class Result { + + private List<String> warnings; + private List<String> infos; + + private QProfile profile; + + public Result() { + warnings = newArrayList(); + infos = newArrayList(); + } + + public List<String> warnings() { + return warnings; + } + + public Result setWarnings(List<String> warnings) { + this.warnings = warnings; + return this; + } + + public List<String> infos() { + return infos; + } + + public Result setInfos(List<String> infos) { + this.infos = infos; + return this; + } + + public QProfile profile() { + return profile; + } + + public Result setProfile(QProfile profile) { + this.profile = profile; + return this; + } + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java index e74cbd9f1ae..f6d22438cea 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java @@ -87,20 +87,16 @@ public class QProfileOperations implements ServerComponent { } public NewProfileResult newProfile(String name, String language, Map<String, String> xmlProfilesByPlugin, UserSession userSession) { - checkPermission(userSession); SqlSession session = myBatis.openSession(); try { - checkNotAlreadyExists(name, language, session); + QProfile profile = newProfile(name, language, userSession, session); NewProfileResult result = new NewProfileResult(); List<RulesProfile> importProfiles = readProfilesFromXml(result, xmlProfilesByPlugin); - - QualityProfileDto dto = new QualityProfileDto().setName(name).setLanguage(language).setVersion(1).setUsed(false); - dao.insert(dto, session); for (RulesProfile rulesProfile : importProfiles) { - importProfile(dto, rulesProfile, session); + importProfile(profile.id(), rulesProfile, session); } - result.setProfile(QProfile.from(dto)); + result.setProfile(profile); session.commit(); dryRunCache.reportGlobalModification(); return result; @@ -109,6 +105,20 @@ public class QProfileOperations implements ServerComponent { } } + public QProfile newProfile(String name, String language, boolean failIfAlreadyExists, UserSession userSession, SqlSession session) { + checkPermission(userSession); + if (failIfAlreadyExists) { + checkNotAlreadyExists(name, language, session); + } + QualityProfileDto dto = new QualityProfileDto().setName(name).setLanguage(language).setVersion(1).setUsed(false); + dao.insert(dto, session); + return QProfile.from(dto); + } + + private QProfile newProfile(String name, String language, UserSession userSession, SqlSession session) { + return newProfile(name, language, true, userSession, session); + } + public void renameProfile(int profileId, String newName, UserSession userSession) { checkPermission(userSession); SqlSession session = myBatis.openSession(); @@ -147,7 +157,7 @@ public class QProfileOperations implements ServerComponent { ProfilesManager.RuleInheritanceActions actions = profilesManager.profileParentChanged(profile.getId(), parentName, userSession.name()); ruleRegistry.deleteActiveRules(actions.idsToDelete()); - ruleRegistry.bulkIndexActiveRules(actions.idsToIndex(), session); + ruleRegistry.bulkIndexActiveRuleIds(actions.idsToIndex(), session); profile.setParent(parentName); dao.update(profile, session); @@ -192,11 +202,11 @@ public class QProfileOperations implements ServerComponent { return profiles; } - private void importProfile(QualityProfileDto qualityProfileDto, RulesProfile rulesProfile, SqlSession sqlSession) { + public void importProfile(int profileId, RulesProfile rulesProfile, SqlSession sqlSession) { List<ActiveRuleDto> activeRuleDtos = newArrayList(); Multimap<Integer, ActiveRuleParamDto> paramsByActiveRule = ArrayListMultimap.create(); for (ActiveRule activeRule : rulesProfile.getActiveRules()) { - ActiveRuleDto activeRuleDto = toActiveRuleDto(activeRule, qualityProfileDto); + ActiveRuleDto activeRuleDto = toActiveRuleDto(activeRule, profileId); activeRuleDao.insert(activeRuleDto, sqlSession); activeRuleDtos.add(activeRuleDto); for (ActiveRuleParam activeRuleParam : activeRule.getActiveRuleParams()) { @@ -229,9 +239,9 @@ public class QProfileOperations implements ServerComponent { result.setInfos(messages.getInfos()); } - private ActiveRuleDto toActiveRuleDto(ActiveRule activeRule, QualityProfileDto dto) { + private ActiveRuleDto toActiveRuleDto(ActiveRule activeRule, int profileId) { return new ActiveRuleDto() - .setProfileId(dto.getId()) + .setProfileId(profileId) .setRuleId(activeRule.getRule().getId()) .setSeverity(toSeverityLevel(activeRule.getSeverity())); } 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 2afd30496de..e082dadaaa8 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 @@ -60,6 +60,7 @@ public class QProfiles implements ServerComponent { private final QProfileProjectOperations projectOperations; private final QProfileProjectLookup projectLookup; + private final QProfileBackup backup; private final QProfileLookup profileLookup; private final QProfileOperations operations; @@ -68,7 +69,7 @@ public class QProfiles implements ServerComponent { private final ProfileRules rules; public QProfiles(QualityProfileDao qualityProfileDao, ActiveRuleDao activeRuleDao, RuleDao ruleDao, ResourceDao resourceDao, - QProfileProjectOperations projectOperations, QProfileProjectLookup projectLookup, QProfileLookup profileLookup, + QProfileProjectOperations projectOperations, QProfileProjectLookup projectLookup, QProfileBackup backup, QProfileLookup profileLookup, QProfileOperations operations, QProfileActiveRuleOperations activeRuleOperations, QProfileRuleOperations ruleOperations, ProfileRules rules) { this.qualityProfileDao = qualityProfileDao; this.activeRuleDao = activeRuleDao; @@ -76,6 +77,7 @@ public class QProfiles implements ServerComponent { this.resourceDao = resourceDao; this.projectOperations = projectOperations; this.projectLookup = projectLookup; + this.backup = backup; this.profileLookup = profileLookup; this.operations = operations; this.activeRuleOperations = activeRuleOperations; @@ -145,6 +147,10 @@ public class QProfiles implements ServerComponent { operations.updateParentProfile(profileId, parentId, UserSession.get()); } + public QProfileBackup.Result restore(String xmlBackup, boolean deleteExisting) { + return backup.restore(xmlBackup, deleteExisting, UserSession.get()); + } + // PROJECTS diff --git a/sonar-server/src/main/java/org/sonar/server/rule/RuleRegistry.java b/sonar-server/src/main/java/org/sonar/server/rule/RuleRegistry.java index c8c284318c1..aba815f509e 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/RuleRegistry.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/RuleRegistry.java @@ -20,7 +20,9 @@ package org.sonar.server.rule; +import com.google.common.base.Function; import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import org.apache.ibatis.session.SqlSession; import org.elasticsearch.ElasticSearchException; @@ -255,10 +257,23 @@ public class RuleRegistry { } } - public void bulkIndexActiveRules(List<Integer> ids, SqlSession session) { - List<ActiveRuleDto> activeRules = activeRuleDao.selectByIds(ids, session); + public void bulkIndexProfile(int profileId, SqlSession session) { + bulkIndexActiveRules(activeRuleDao.selectByProfileId(profileId, session), session); + } + + public void bulkIndexActiveRuleIds(List<Integer> activeRulesIds, SqlSession session) { + bulkIndexActiveRules(activeRuleDao.selectByIds(activeRulesIds, session), session); + } + + private void bulkIndexActiveRules(List<ActiveRuleDto> activeRules, SqlSession session) { Multimap<Integer, ActiveRuleParamDto> paramsByActiveRule = ArrayListMultimap.create(); - for (ActiveRuleParamDto param : activeRuleDao.selectParamsByActiveRuleIds(ids, session)) { + List<Integer> activeRulesIdList = newArrayList(Iterables.transform(activeRules, new Function<ActiveRuleDto, Integer>() { + @Override + public Integer apply(ActiveRuleDto input) { + return input.getId(); + } + })); + for (ActiveRuleParamDto param : activeRuleDao.selectParamsByActiveRuleIds(activeRulesIdList, session)) { paramsByActiveRule.put(param.getActiveRuleId(), param); } bulkIndexActiveRules(activeRules, paramsByActiveRule); @@ -267,7 +282,7 @@ public class RuleRegistry { public void bulkIndexActiveRules(List<Integer> ids) { SqlSession session = myBatis.openSession(); try { - bulkIndexActiveRules(ids, session); + bulkIndexActiveRuleIds(ids, session); } finally { MyBatis.closeQuietly(session); } diff --git a/sonar-server/src/main/java/org/sonar/server/rules/ProfilesConsole.java b/sonar-server/src/main/java/org/sonar/server/rules/ProfilesConsole.java index 28dcdb31b2c..73383f964c9 100644 --- a/sonar-server/src/main/java/org/sonar/server/rules/ProfilesConsole.java +++ b/sonar-server/src/main/java/org/sonar/server/rules/ProfilesConsole.java @@ -64,6 +64,7 @@ public final class ProfilesConsole implements ServerComponent { this.importers.addAll(Arrays.asList(importers)); } + @Deprecated public String backupProfile(int profileId) { DatabaseSession session = sessionFactory.getSession(); RulesProfile profile = loadProfile(session, profileId); @@ -75,6 +76,7 @@ public final class ProfilesConsole implements ServerComponent { return null; } + @Deprecated public ValidationMessages restoreProfile(String xmlBackup, boolean deleteExisting) { ValidationMessages messages = ValidationMessages.create(); RulesProfile profile = xmlProfileParser.parse(new StringReader(xmlBackup), messages); @@ -120,6 +122,7 @@ public final class ProfilesConsole implements ServerComponent { return result; } + @Deprecated public String exportProfile(int profileId, String exporterKey) { DatabaseSession session = sessionFactory.getSession(); RulesProfile profile = loadProfile(session, profileId); diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index 0ba0f84edee..0de182a42b3 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -257,10 +257,12 @@ public final class JRubyFacade { return get(RulesConsole.class).getRepositoriesByLanguage(languageKey); } + // TODO move this to QProfiles public String backupProfile(int profileId) { return get(ProfilesConsole.class).backupProfile(profileId); } + // TODO move this to QProfiles public ValidationMessages restoreProfile(String xmlBackup, boolean deleteExisting) { return get(ProfilesConsole.class).restoreProfile(xmlBackup, deleteExisting); } @@ -269,6 +271,7 @@ public final class JRubyFacade { return get(ProfilesConsole.class).getProfileExportersForLanguage(language); } + // TODO move this to QProfiles public List<ProfileImporter> getProfileImportersForLanguage(String language) { return get(ProfilesConsole.class).getProfileImportersForLanguage(language); } @@ -276,6 +279,7 @@ public final class JRubyFacade { /** * @throws IllegalArgumentException if no such exporter */ + // TODO move this to QProfiles public String exportProfile(int profileId, String exporterKey) { return get(ProfilesConsole.class).exportProfile(profileId, exporterKey); } @@ -284,6 +288,7 @@ public final class JRubyFacade { return get(ProfilesConsole.class).getProfileExporter(exporterKey).getMimeType(); } + // TODO move this to QProfiles public void copyProfile(long profileId, String newProfileName) { getProfilesManager().copyProfile((int) profileId, newProfileName); } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/profiles_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/profiles_controller.rb index 92bdbde14f7..214c2e20404 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/profiles_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/profiles_controller.rb @@ -146,15 +146,12 @@ class Api::ProfilesController < Api::ApiController # curl -X POST -u admin:admin -F 'backup=@backup.xml' -v http://localhost:9000/api/profiles/restore def restore verify_post_request - access_denied unless has_role?(:profileadmin) require_parameters :backup - backup = Api::Utils.read_post_request_param(params[:backup]) + result = Internal.quality_profiles.restore(Api::Utils.read_post_request_param(params[:backup]), true) - messages=java_facade.restoreProfile(backup, true) - status=(messages.hasErrors() ? 400 : 200) respond_to do |format| - format.json { render :json => jsonp(validation_messages_to_json(messages)), :status => status } + format.json { render :json => jsonp(validation_result_to_json(result)), :status => 200 } end end @@ -168,6 +165,13 @@ class Api::ProfilesController < Api::ApiController hash end + def validation_result_to_json(result) + hash={} + hash[:warnings]=result.warnings().to_a.map { |message| message } + hash[:infos]=result.infos().to_a.map { |message| message } + hash + end + def filter_rules conditions=['active_rules.profile_id=?'] condition_values=[@profile.id] diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb index 4c004906dc6..b2aaace191e 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb @@ -54,19 +54,13 @@ class ProfilesController < ApplicationController end result = Internal.quality_profiles.newProfile(params[:name], params[:language], files_by_key) flash[:notice] = message('quality_profiles.profile_x_created', :params => result.profile.name) - - # only 4 messages are kept each time to avoid cookie overflow. - unless result.infos.empty? - flash[:notice] += result.infos.to_a[0...4].join('<br/>') - end - unless result.warnings.empty? - flash[:warning] = result.warnings.to_a[0...4].join('<br/>') - end + flash_result(result) end redirect_to :action => 'index' end # POST /profiles/delete/<id> + # TODO use QProfiles facade instead def delete verify_post_request access_denied unless has_role?(:profileadmin) @@ -93,6 +87,7 @@ class ProfilesController < ApplicationController # GET /profiles/copy_form/<profile id> + # TODO use QProfiles facade instead def copy_form access_denied unless has_role?(:profileadmin) require_parameters 'id' @@ -101,6 +96,7 @@ class ProfilesController < ApplicationController end # POST /profiles/copy/<id>?name=<name of new profile> + # TODO use QProfiles facade instead def copy verify_post_request verify_ajax_request @@ -124,6 +120,7 @@ class ProfilesController < ApplicationController # the backup action is allow to non-admin users : see http://jira.codehaus.org/browse/SONAR-2039 # POST /profiles/backup?id=<profile id> + # TODO use QProfiles facade instead def backup verify_post_request require_parameters 'id' @@ -144,19 +141,20 @@ class ProfilesController < ApplicationController # POST /profiles/restore?backup=<file> def restore - verify_post_request - access_denied unless has_role?(:profileadmin) if params[:backup].blank? - flash[:warning]=message('quality_profiles.please_upload_backup_file') + flash[:warning] = message('quality_profiles.please_upload_backup_file') else - messages=java_facade.restoreProfile(Api::Utils.read_post_request_param(params[:backup]), false) - flash_messages(messages) + call_backend do + result = Internal.quality_profiles.restore(Api::Utils.read_post_request_param(params[:backup]), false) + flash_result(result) + end end redirect_to :action => 'index' end # GET /profiles/export?name=<profile name>&language=<language>&format<exporter key> + # TODO use QProfiles facade instead def export language = params[:language] if (params[:name].blank?) @@ -257,6 +255,7 @@ class ProfilesController < ApplicationController # GET /profiles/permalinks?id=<profile id> # # + # TODO use QProfiles facade instead def permalinks require_parameters 'id' @profile = Profile.find(params[:id]) @@ -516,6 +515,17 @@ class ProfilesController < ApplicationController end end + def flash_result(result) + # only 4 messages are kept each time to avoid cookie overflow. + unless result.infos.empty? + flash[:notice] += result.infos.to_a[0...4].join('<br/>') + end + unless result.warnings.empty? + flash[:warning] = result.warnings.to_a[0...4].join('<br/>') + end + + end + def set_profile_breadcrumbs add_breadcrumbs ProfilesController::root_breadcrumb, Api::Utils.language_name(@profile.language), {:name => @profile.name, :url => {:controller => 'rules_configuration', :action => 'index', :id => @profile.id}} end diff --git a/sonar-server/src/test/java/org/sonar/server/exceptions/BadRequestExceptionTest.java b/sonar-server/src/test/java/org/sonar/server/exceptions/BadRequestExceptionTest.java index ce8518641ea..8f8948c7189 100644 --- a/sonar-server/src/test/java/org/sonar/server/exceptions/BadRequestExceptionTest.java +++ b/sonar-server/src/test/java/org/sonar/server/exceptions/BadRequestExceptionTest.java @@ -28,21 +28,13 @@ import static org.fest.assertions.Assertions.assertThat; public class BadRequestExceptionTest { @Test - public void test_message() throws Exception { + public void text_error() throws Exception { BadRequestException exception = BadRequestException.of("error"); assertThat(exception.getMessage()).isEqualTo("error"); } @Test - public void test_l10n_errors() throws Exception { - BadRequestException exception = BadRequestException.ofL10n("issue.error.123", "10"); - assertThat(exception.getMessage()).isNull(); - assertThat(exception.l10nKey()).isEqualTo("issue.error.123"); - assertThat(exception.l10nParams()).containsOnly("10"); - } - - @Test - public void test_text_error_message() throws Exception { + public void text_error_with_message() throws Exception { BadRequestException exception = BadRequestException.of("error", newArrayList(BadRequestException.Message.of("new error"))); assertThat(exception.errors()).hasSize(1); @@ -50,7 +42,15 @@ public class BadRequestExceptionTest { } @Test - public void test_l10n_message() throws Exception { + public void l10n_errors() throws Exception { + BadRequestException exception = BadRequestException.ofL10n("issue.error.123", "10"); + assertThat(exception.getMessage()).isNull(); + assertThat(exception.l10nKey()).isEqualTo("issue.error.123"); + assertThat(exception.l10nParams()).containsOnly("10"); + } + + @Test + public void test_equals_and_hashcode() throws Exception { BadRequestException.Message msg = BadRequestException.Message.ofL10n("error.123", "10"); BadRequestException.Message sameMsg = BadRequestException.Message.ofL10n("error.123", "10"); BadRequestException.Message msg2 = BadRequestException.Message.ofL10n("error.123", "200"); diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperationsTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperationsTest.java index 5622e2bfa9a..a07c9508a54 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperationsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileActiveRuleOperationsTest.java @@ -155,7 +155,7 @@ public class QProfileActiveRuleOperationsTest { verify(session).commit(); verify(profilesManager).activated(eq(1), anyInt(), eq("Nicolas")); verify(ruleRegistry).deleteActiveRules(eq(newArrayList(idActiveRuleToDelete))); - verify(ruleRegistry).bulkIndexActiveRules(eq(newArrayList(idActiveRuleToUpdate)), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(eq(newArrayList(idActiveRuleToUpdate)), eq(session)); } @Test @@ -173,7 +173,7 @@ public class QProfileActiveRuleOperationsTest { verify(session).commit(); verify(profilesManager).ruleSeverityChanged(eq(1), eq(5), eq(RulePriority.MINOR), eq(RulePriority.MAJOR), eq("Nicolas")); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); } @Test @@ -221,7 +221,7 @@ public class QProfileActiveRuleOperationsTest { verify(session).commit(); verify(profilesManager).activated(eq(1), anyInt(), eq("Nicolas")); verify(ruleRegistry).deleteActiveRules(eq(newArrayList(idActiveRuleToDelete))); - verify(ruleRegistry).bulkIndexActiveRules(eq(newArrayList(idActiveRuleToUpdate)), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(eq(newArrayList(idActiveRuleToUpdate)), eq(session)); } @Test @@ -240,7 +240,7 @@ public class QProfileActiveRuleOperationsTest { verify(session).commit(); verify(profilesManager).deactivated(eq(1), anyInt(), eq("Nicolas")); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); } @Test @@ -277,7 +277,7 @@ public class QProfileActiveRuleOperationsTest { verify(session).commit(); verify(profilesManager).deactivated(eq(1), anyInt(), eq("Nicolas")); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); } @Test @@ -300,7 +300,7 @@ public class QProfileActiveRuleOperationsTest { verify(session).commit(); verify(profilesManager).ruleParamChanged(eq(1), eq(5), eq("max"), eq((String) null), eq("30"), eq("Nicolas")); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); } @Test @@ -341,7 +341,7 @@ public class QProfileActiveRuleOperationsTest { verify(session).commit(); verify(profilesManager).ruleParamChanged(eq(1), eq(5), eq("max"), eq("20"), eq("30"), eq("Nicolas")); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); } @Test @@ -361,7 +361,7 @@ public class QProfileActiveRuleOperationsTest { verify(activeRuleDao).deleteParameter(100, session); verify(profilesManager).ruleParamChanged(eq(1), eq(5), eq("max"), eq("20"), eq((String) null), eq("Nicolas")); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); } @Test @@ -384,7 +384,7 @@ public class QProfileActiveRuleOperationsTest { verify(session, times(2)).commit(); verify(profilesManager).ruleSeverityChanged(eq(1), eq(5), eq(RulePriority.MINOR), eq(RulePriority.MAJOR), eq("Nicolas")); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); verify(ruleRegistry).save(eq(activeRule), anyListOf(ActiveRuleParamDto.class)); } @@ -433,7 +433,7 @@ public class QProfileActiveRuleOperationsTest { verify(session, times(2)).commit(); verify(profilesManager).ruleParamChanged(eq(1), eq(5), eq("max"), eq("20"), eq("15"), eq("Nicolas")); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); verify(ruleRegistry).save(eq(activeRule), anyListOf(ActiveRuleParamDto.class)); } @@ -461,7 +461,7 @@ public class QProfileActiveRuleOperationsTest { verify(session, times(2)).commit(); verify(profilesManager).ruleParamChanged(eq(1), eq(5), eq("format"), eq("abc"), eq((String) null), eq("Nicolas")); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); verify(ruleRegistry).save(eq(activeRule), anyListOf(ActiveRuleParamDto.class)); } @@ -492,7 +492,7 @@ public class QProfileActiveRuleOperationsTest { verify(session, times(2)).commit(); verify(profilesManager).ruleParamChanged(eq(1), eq(5), eq("minimum"), eq((String) null), eq("2"), eq("Nicolas")); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); verify(ruleRegistry).save(eq(activeRule), anyListOf(ActiveRuleParamDto.class)); } diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackupTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackupTest.java new file mode 100644 index 00000000000..d18db593871 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackupTest.java @@ -0,0 +1,240 @@ +/* + * 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.apache.ibatis.session.SqlSession; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.profiles.XMLProfileParser; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.preview.PreviewCache; +import org.sonar.jpa.session.DatabaseSessionFactory; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.rule.RuleRegistry; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.user.UserSession; + +import java.io.Reader; + +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.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class QProfileBackupTest { + + @Mock + DatabaseSessionFactory sessionFactory; + + @Mock + DatabaseSession hibernateSession; + + @Mock + MyBatis myBatis; + + @Mock + SqlSession session; + + @Mock + XMLProfileParser xmlProfileParser; + + @Mock + QProfileOperations qProfileOperations; + + @Mock + QProfileLookup qProfileLookup; + + @Mock + RuleRegistry ruleRegistry; + + @Mock + PreviewCache dryRunCache; + + QProfileBackup backup; + + UserSession userSession = MockUserSession.create().setLogin("nicolas").setName("Nicolas").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + + @Before + public void setUp() throws Exception { + when(myBatis.openSession()).thenReturn(session); + when(sessionFactory.getSession()).thenReturn(hibernateSession); + + backup = new QProfileBackup(sessionFactory, xmlProfileParser, myBatis, qProfileOperations, qProfileLookup, ruleRegistry, dryRunCache); + } + + @Test + public void restore() throws Exception { + RulesProfile profile = mock(RulesProfile.class); + when(profile.getName()).thenReturn("Default"); + when(profile.getLanguage()).thenReturn("java"); + when(profile.getId()).thenReturn(1); + when(xmlProfileParser.parse(any(Reader.class), any(ValidationMessages.class))).thenReturn(profile); + when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(null); + when(qProfileLookup.profile(anyInt())).thenReturn(new QProfile().setId(1)); + + QProfileBackup.Result result = backup.restore("<xml/>", false, userSession); + + assertThat(result.profile()).isNotNull(); + verify(hibernateSession).saveWithoutFlush(profile); + verify(ruleRegistry).bulkIndexProfile(anyInt(), eq(session)); + verify(dryRunCache).reportGlobalModification(); + } + + @Test + public void fail_to_restore_without_profile_admin_permission() throws Exception { + try { + backup.restore("<xml/>", false, MockUserSession.create().setLogin("nicolas").setName("Nicolas")); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(ForbiddenException.class); + } + verify(hibernateSession, never()).saveWithoutFlush(any(RulesProfile.class)); + verifyZeroInteractions(ruleRegistry); + verifyZeroInteractions(dryRunCache); + } + + @Test + public void fail_to_restore_if_profile_already_exist() throws Exception { + RulesProfile profile = mock(RulesProfile.class); + when(profile.getName()).thenReturn("Default"); + when(profile.getLanguage()).thenReturn("java"); + when(xmlProfileParser.parse(any(Reader.class), any(ValidationMessages.class))).thenReturn(profile); + when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(RulesProfile.create("Default", "java")); + when(qProfileLookup.profile(anyInt())).thenReturn(new QProfile().setId(1)); + + try { + backup.restore("<xml/>", false, userSession); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("The profile [name=Default,language=java] already exists. Please delete it before restoring."); + } + + verify(hibernateSession, never()).saveWithoutFlush(any(RulesProfile.class)); + verifyZeroInteractions(ruleRegistry); + verifyZeroInteractions(dryRunCache); + } + + @Test + public void restore_should_delete_existing_profile() throws Exception { + RulesProfile profile = mock(RulesProfile.class); + when(profile.getName()).thenReturn("Default"); + when(profile.getLanguage()).thenReturn("java"); + when(profile.getId()).thenReturn(1); + when(xmlProfileParser.parse(any(Reader.class), any(ValidationMessages.class))).thenReturn(profile); + + RulesProfile existingProfile = RulesProfile.create("Default", "java"); + when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(existingProfile); + when(qProfileLookup.profile(anyInt())).thenReturn(new QProfile().setId(1)); + + QProfileBackup.Result result = backup.restore("<xml/>", true, userSession); + + assertThat(result.profile()).isNotNull(); + verify(hibernateSession).removeWithoutFlush(eq(existingProfile)); + verify(ruleRegistry).bulkIndexProfile(anyInt(), eq(session)); + verify(dryRunCache).reportGlobalModification(); + } + + @Test + public void restore_should_add_warnings_and_infos_from_xml_parsing() throws Exception { + final RulesProfile profile = mock(RulesProfile.class); + when(profile.getName()).thenReturn("Default"); + when(profile.getLanguage()).thenReturn("java"); + when(profile.getId()).thenReturn(1); + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + ValidationMessages validationMessages = (ValidationMessages) args[1]; + validationMessages.addInfoText("an info message"); + validationMessages.addWarningText("a warning message"); + return profile; + } + }).when(xmlProfileParser).parse(any(Reader.class), any(ValidationMessages.class)); + + when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(null); + when(qProfileLookup.profile(anyInt())).thenReturn(new QProfile().setId(1)); + + QProfileBackup.Result result = backup.restore("<xml/>", true, userSession); + + assertThat(result.profile()).isNotNull(); + assertThat(result.warnings()).isNotEmpty(); + assertThat(result.infos()).isNotEmpty(); + } + + @Test + public void restore_should_fail_if_errors_when_parsing_xml() throws Exception { + final RulesProfile profile = mock(RulesProfile.class); + when(profile.getName()).thenReturn("Default"); + when(profile.getLanguage()).thenReturn("java"); + when(profile.getId()).thenReturn(1); + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + ValidationMessages validationMessages = (ValidationMessages) args[1]; + validationMessages.addErrorText("error!"); + return profile; + } + }).when(xmlProfileParser).parse(any(Reader.class), any(ValidationMessages.class)); + + when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(null); + when(qProfileLookup.profile(anyInt())).thenReturn(new QProfile().setId(1)); + + try { + backup.restore("<xml/>", false, userSession); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("Fail to restore profile"); + BadRequestException badRequestException = (BadRequestException) e; + assertThat(badRequestException.errors()).hasSize(1); + assertThat(badRequestException.errors().get(0).text()).isEqualTo("error!"); + } + + verify(hibernateSession, never()).saveWithoutFlush(any(RulesProfile.class)); + verifyZeroInteractions(ruleRegistry); + verifyZeroInteractions(dryRunCache); + } + + @Test + public void do_not_restore_if_xml_is_empty() throws Exception { + when(xmlProfileParser.parse(any(Reader.class), any(ValidationMessages.class))).thenReturn(null); + when(hibernateSession.getSingleResult(any(Class.class), eq("name"), eq("Default"), eq("language"), eq("java"))).thenReturn(null); + when(qProfileLookup.profile(anyInt())).thenReturn(new QProfile().setId(1)); + + QProfileBackup.Result result = backup.restore("<xml/>", false, userSession); + + assertThat(result.profile()).isNull(); + verify(hibernateSession, never()).saveWithoutFlush(any(RulesProfile.class)); + verifyZeroInteractions(ruleRegistry); + verifyZeroInteractions(dryRunCache); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileOperationsTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileOperationsTest.java index fa055fd62d5..060bd288af9 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileOperationsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileOperationsTest.java @@ -115,6 +115,14 @@ public class QProfileOperationsTest { return null; } }).when(activeRuleDao).insert(any(ActiveRuleDto.class), any(SqlSession.class)); + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + QualityProfileDto dto = (QualityProfileDto) args[0]; + dto.setId(currentId++); + return null; + } + }).when(qualityProfileDao).insert(any(QualityProfileDto.class), any(SqlSession.class)); operations = new QProfileOperations(myBatis, qualityProfileDao, activeRuleDao, propertiesDao, importers, dryRunCache, ruleRegistry, profilesManager); } @@ -291,7 +299,7 @@ public class QProfileOperationsTest { verify(session).commit(); verify(profilesManager).profileParentChanged(1, "Parent", "Nicolas"); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); } @Test @@ -311,7 +319,7 @@ public class QProfileOperationsTest { verify(session).commit(); verify(profilesManager).profileParentChanged(1, "Parent", "Nicolas"); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); } @Test @@ -332,7 +340,7 @@ public class QProfileOperationsTest { verify(session).commit(); verify(profilesManager).profileParentChanged(1, null, "Nicolas"); verify(ruleRegistry).deleteActiveRules(anyListOf(Integer.class)); - verify(ruleRegistry).bulkIndexActiveRules(anyListOf(Integer.class), eq(session)); + verify(ruleRegistry).bulkIndexActiveRuleIds(anyListOf(Integer.class), eq(session)); } @Test 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 61db2d53d9f..6314f457079 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 @@ -88,13 +88,16 @@ public class QProfilesTest { QProfileRuleOperations ruleOperations; @Mock + QProfileBackup backup; + + @Mock ProfileRules rules; QProfiles qProfiles; @Before public void setUp() throws Exception { - qProfiles = new QProfiles(qualityProfileDao, activeRuleDao, ruleDao, resourceDao, projectOperations, projectLookup, profileLookup, service, activeRuleOperations, ruleOperations, rules); + qProfiles = new QProfiles(qualityProfileDao, activeRuleDao, ruleDao, resourceDao, projectOperations, projectLookup, backup, profileLookup, service, activeRuleOperations, ruleOperations, rules); } @Test |