From 87c105ee6c52ec891fc44581f636548921c5bbe4 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Thu, 16 Mar 2017 16:17:33 +0100 Subject: [PATCH] SONAR-8857 clean-up backup/restore Do not load target profile twice when copying a profile --- .../qualityprofile/QProfileBackuper.java | 9 +- .../qualityprofile/QProfileBackuperImpl.java | 37 ++- .../server/qualityprofile/QProfileCopier.java | 21 +- .../server/qualityprofile/QProfileReset.java | 5 +- .../qualityprofile/QProfileResetImpl.java | 18 +- .../QProfileRestoreSummary.java | 9 +- .../qualityprofile/ws/RestoreAction.java | 6 +- .../qualityprofile/ws/BackupActionTest.java | 2 +- .../qualityprofile/ws/CopyActionTest.java | 256 +++++++++++++----- .../qualityprofile/ws/ExportActionTest.java | 5 + .../qualityprofile/ws/RestoreActionTest.java | 9 +- .../ws/CopyActionTest/copy_nominal.json | 8 - .../ws/CopyActionTest/copy_with_parent.json | 9 - 13 files changed, 251 insertions(+), 143 deletions(-) delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/CopyActionTest/copy_nominal.json delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/CopyActionTest/copy_with_parent.json diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuper.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuper.java index 553804bbef0..015b94e10ad 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuper.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuper.java @@ -34,9 +34,14 @@ public interface QProfileBackuper { void backup(DbSession dbSession, QualityProfileDto profile, Writer backupWriter); /** - * Restore a profile backup in the specified organization. The parameter {@code overriddenProfileName} - * is the name of the profile to be used. If null then name is loaded from backup. + * Restore backup on a profile in the specified organization. The parameter {@code overriddenProfileName} + * is the name of the profile to be used. If the parameter is null, then the name is loaded from the backup. + * The profile is created if it does not exist. */ QProfileRestoreSummary restore(DbSession dbSession, Reader backup, OrganizationDto organization, @Nullable String overriddenProfileName); + /** + * Restore backup on an existing profile. + */ + QProfileRestoreSummary restore(DbSession dbSession, Reader backup, QualityProfileDto profile); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuperImpl.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuperImpl.java index 45158135e6d..80ce9142bb4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuperImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileBackuperImpl.java @@ -20,7 +20,6 @@ package org.sonar.server.qualityprofile; import com.google.common.base.Joiner; -import com.google.common.base.MoreObjects; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -31,6 +30,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import javax.annotation.Nullable; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; @@ -49,17 +49,21 @@ import org.sonar.db.qualityprofile.ActiveRuleDto; import org.sonar.db.qualityprofile.ActiveRuleParamDto; import org.sonar.db.qualityprofile.QualityProfileDto; +import static com.google.common.base.Preconditions.checkArgument; + @ServerSide public class QProfileBackuperImpl implements QProfileBackuper { private static final Joiner RULE_KEY_JOINER = Joiner.on(", ").skipNulls(); - private final QProfileReset reset; private final DbClient db; + private final QProfileReset profileReset; + private final QProfileFactory profileFactory; - public QProfileBackuperImpl(QProfileReset reset, DbClient db) { - this.reset = reset; + public QProfileBackuperImpl(DbClient db, QProfileReset profileReset, QProfileFactory profileFactory) { this.db = db; + this.profileReset = profileReset; + this.profileFactory = profileFactory; } @Override @@ -97,6 +101,25 @@ public class QProfileBackuperImpl implements QProfileBackuper { @Override public QProfileRestoreSummary restore(DbSession dbSession, Reader backup, OrganizationDto organization, @Nullable String overriddenProfileName) { + return restore(dbSession, backup, nameInBackup -> { + QProfileName targetName = nameInBackup; + if (overriddenProfileName != null) { + targetName = new QProfileName(nameInBackup.getLanguage(), overriddenProfileName); + } + return profileFactory.getOrCreate(dbSession, organization, targetName); + }); + } + + @Override + public QProfileRestoreSummary restore(DbSession dbSession, Reader backup, QualityProfileDto profile) { + return restore(dbSession, backup, nameInBackup -> { + checkArgument(profile.getLanguage().equals(nameInBackup.getLanguage()), + "Can't restore %s backup on %s profile with key [%s]. Languages are different.", nameInBackup.getLanguage(), profile.getLanguage(), profile.getKey()); + return profile; + }); + } + + private QProfileRestoreSummary restore(DbSession dbSession, Reader backup, Function profileLoader) { try { String profileLang = null; String profileName = null; @@ -122,8 +145,10 @@ public class QProfileBackuperImpl implements QProfileBackuper { } } - QProfileName target = new QProfileName(profileLang, MoreObjects.firstNonNull(overriddenProfileName, profileName)); - return reset.reset(dbSession, organization, target, ruleActivations); + QProfileName targetName = new QProfileName(profileLang, profileName); + QualityProfileDto targetProfile = profileLoader.apply(targetName); + BulkChangeResult changes = profileReset.reset(dbSession, targetProfile, ruleActivations); + return new QProfileRestoreSummary(targetProfile, changes); } catch (XMLStreamException e) { throw new IllegalStateException("Fail to restore Quality profile backup", e); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileCopier.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileCopier.java index 11ac59f7419..8ea3e37967b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileCopier.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileCopier.java @@ -33,7 +33,6 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualityprofile.QualityProfileDto; -import org.sonar.server.qualityprofile.ws.QProfileWsSupport; import static java.nio.charset.StandardCharsets.UTF_8; @@ -44,25 +43,23 @@ public class QProfileCopier { private final QProfileFactory factory; private final QProfileBackuper backuper; private final TempFolder temp; - private final QProfileWsSupport qProfileWsSupport; - public QProfileCopier(DbClient db, QProfileFactory factory, QProfileBackuper backuper, TempFolder temp, QProfileWsSupport qProfileWsSupport) { + public QProfileCopier(DbClient db, QProfileFactory factory, QProfileBackuper backuper, TempFolder temp) { this.db = db; this.factory = factory; this.backuper = backuper; this.temp = temp; - this.qProfileWsSupport = qProfileWsSupport; } public QualityProfileDto copyToName(DbSession dbSession, String fromKey, String toName) { QualityProfileDto from = db.qualityProfileDao().selectOrFailByKey(dbSession, fromKey); OrganizationDto organization = db.organizationDao().selectByUuid(dbSession, from.getOrganizationUuid()) - .orElseThrow(() -> new IllegalStateException("Organization with UUID [" + from.getOrganizationUuid() + "] does not exist")); + .orElseThrow(() -> new IllegalStateException("Organization with UUID [" + from.getOrganizationUuid() + "] does not exist")); QualityProfileDto to = prepareTarget(dbSession, organization, from, toName); File backupFile = temp.newFile(); try { backup(dbSession, from, backupFile); - restore(dbSession, backupFile, organization, to.getName()); + restore(dbSession, backupFile, to); return to; } finally { org.sonar.core.util.FileUtils.deleteQuietly(backupFile); @@ -74,8 +71,6 @@ public class QProfileCopier { verify(from, toProfileName); QualityProfileDto toProfile = db.qualityProfileDao().selectByNameAndLanguage(organization, toProfileName.getName(), toProfileName.getLanguage(), dbSession); if (toProfile == null) { - // Do not delegate creation to QProfileBackuper because we need to keep - // the parent-child association, if exists. toProfile = factory.checkAndCreate(dbSession, organization, toProfileName); toProfile.setParentKee(from.getParentKee()); db.qualityProfileDao().update(dbSession, toProfile); @@ -87,12 +82,12 @@ public class QProfileCopier { private void verify(QualityProfileDto fromProfile, QProfileName toProfileName) { if (!StringUtils.equals(fromProfile.getLanguage(), toProfileName.getLanguage())) { throw new IllegalArgumentException(String.format( - "Source and target profiles do not have the same language: %s and %s", - fromProfile.getLanguage(), toProfileName.getLanguage())); + "Source and target profiles do not have the same language: %s and %s", + fromProfile.getLanguage(), toProfileName.getLanguage())); } if (fromProfile.getName().equals(toProfileName.getName())) { throw new IllegalArgumentException(String.format("Source and target profiles are equal: %s", - fromProfile.getName())); + fromProfile.getName())); } } @@ -104,9 +99,9 @@ public class QProfileCopier { } } - private void restore(DbSession dbSession, File backupFile, OrganizationDto org, String profileName) { + private void restore(DbSession dbSession, File backupFile, QualityProfileDto profile) { try (Reader reader = new InputStreamReader(FileUtils.openInputStream(backupFile), UTF_8)) { - backuper.restore(dbSession, reader, org, profileName); + backuper.restore(dbSession, reader, profile); } catch (IOException e) { throw new IllegalStateException("Fail to create temporary backup file: " + backupFile, e); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileReset.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileReset.java index 0e884329816..a8447bbb2b0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileReset.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileReset.java @@ -22,6 +22,7 @@ package org.sonar.server.qualityprofile; import java.util.Collection; import org.sonar.db.DbSession; import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.qualityprofile.QualityProfileDto; public interface QProfileReset { /** @@ -31,7 +32,7 @@ public interface QProfileReset { void resetLanguage(DbSession dbSession, OrganizationDto organization, String language); /** - * Reset the profile, which is created if it does not exist + * Reset the rules of the specified profile. */ - QProfileRestoreSummary reset(DbSession dbSession, OrganizationDto organization, QProfileName profileName, Collection activations); + BulkChangeResult reset(DbSession dbSession, QualityProfileDto profile, Collection activations); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResetImpl.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResetImpl.java index 62b6df3a3b8..caf56c4d4e0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResetImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileResetImpl.java @@ -19,7 +19,6 @@ */ package org.sonar.server.qualityprofile; -import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; @@ -46,6 +45,7 @@ import org.sonar.db.rule.RuleParamDto; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; +import static java.util.Objects.requireNonNull; import static org.sonar.server.ws.WsUtils.checkRequest; @ServerSide @@ -92,23 +92,13 @@ public class QProfileResetImpl implements QProfileReset { activations.add(activation); } } - doReset(dbSession, profile, activations); + reset(dbSession, profile, activations); } } @Override - public QProfileRestoreSummary reset(DbSession dbSession, OrganizationDto organization, QProfileName profileName, Collection activations) { - QualityProfileDto profile = factory.getOrCreate(dbSession, organization, profileName); - BulkChangeResult ruleChanges = doReset(dbSession, profile, activations); - return new QProfileRestoreSummary(organization, profile, ruleChanges); - } - - /** - * @param dbSession - * @param profile must exist - */ - private BulkChangeResult doReset(DbSession dbSession, QualityProfileDto profile, Collection activations) { - Preconditions.checkNotNull(profile.getId(), "Quality profile must be persisted"); + public BulkChangeResult reset(DbSession dbSession, QualityProfileDto profile, Collection activations) { + requireNonNull(profile.getId(), "Quality profile must be persisted"); BulkChangeResult result = new BulkChangeResult(); Set ruleToBeDeactivated = Sets.newHashSet(); // Keep reference to all the activated rules before backup restore diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRestoreSummary.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRestoreSummary.java index a703f75ebb7..6f687e8bce7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRestoreSummary.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileRestoreSummary.java @@ -19,26 +19,19 @@ */ package org.sonar.server.qualityprofile; -import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualityprofile.QualityProfileDto; import static java.util.Objects.requireNonNull; public final class QProfileRestoreSummary { - private final OrganizationDto organization; private final QualityProfileDto profile; private final BulkChangeResult ruleChanges; - public QProfileRestoreSummary(OrganizationDto organization, QualityProfileDto profile, BulkChangeResult ruleChanges) { - this.organization = requireNonNull(organization); + public QProfileRestoreSummary(QualityProfileDto profile, BulkChangeResult ruleChanges) { this.profile = requireNonNull(profile); this.ruleChanges = requireNonNull(ruleChanges); } - public OrganizationDto getOrganization() { - return organization; - } - public QualityProfileDto getProfile() { return profile; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RestoreAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RestoreAction.java index 1576014c2b6..5a58beca806 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RestoreAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/RestoreAction.java @@ -94,21 +94,21 @@ public class RestoreAction implements QProfileWsAction { userSession.checkPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); QProfileRestoreSummary summary = backuper.restore(dbSession, reader, organization, null); - writeResponse(response.newJsonWriter(), summary); + writeResponse(response.newJsonWriter(), organization, summary); } finally { IOUtils.closeQuietly(reader); IOUtils.closeQuietly(backup); } } - private void writeResponse(JsonWriter json, QProfileRestoreSummary summary) { + private void writeResponse(JsonWriter json, OrganizationDto organization, QProfileRestoreSummary summary) { QualityProfileDto profile = summary.getProfile(); String languageKey = profile.getLanguage(); Language language = languages.get(languageKey); JsonWriter jsonProfile = json.beginObject().name("profile").beginObject(); jsonProfile - .prop("organization", summary.getOrganization().getKey()) + .prop("organization", organization.getKey()) .prop("key", profile.getKey()) .prop("name", profile.getName()) .prop("language", languageKey) diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/BackupActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/BackupActionTest.java index 1120cb0830d..5bec6d057bc 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/BackupActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/BackupActionTest.java @@ -51,7 +51,7 @@ public class BackupActionTest { @Rule public UserSessionRule userSession = UserSessionRule.standalone(); - private QProfileBackuper backuper = new QProfileBackuperImpl(null, db.getDbClient()); + private QProfileBackuper backuper = new QProfileBackuperImpl(db.getDbClient(), null, null); private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); private QProfileWsSupport wsSupport = new QProfileWsSupport(db.getDbClient(), userSession, defaultOrganizationProvider); private Languages languages = LanguageTesting.newLanguages(A_LANGUAGE); diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CopyActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CopyActionTest.java index e228b37f05d..f14312dca52 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CopyActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CopyActionTest.java @@ -19,147 +19,255 @@ */ package org.sonar.server.qualityprofile.ws; -import org.junit.Before; +import java.io.Reader; +import java.io.Writer; +import javax.annotation.Nullable; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.resources.Languages; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.internal.JUnitTempFolder; +import org.sonar.core.util.SequenceUuidFactory; import org.sonar.db.DbSession; import org.sonar.db.DbTester; +import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.permission.OrganizationPermission; import org.sonar.db.qualityprofile.QualityProfileDto; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.language.LanguageTesting; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.organization.TestDefaultOrganizationProvider; +import org.sonar.server.qualityprofile.BulkChangeResult; +import org.sonar.server.qualityprofile.QProfileBackuper; import org.sonar.server.qualityprofile.QProfileCopier; +import org.sonar.server.qualityprofile.QProfileFactory; +import org.sonar.server.qualityprofile.QProfileRestoreSummary; import org.sonar.server.tester.UserSessionRule; -import org.sonar.server.ws.WsTester; +import org.sonar.server.ws.TestResponse; +import org.sonar.server.ws.WsActionTester; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES; +import static org.sonar.test.JsonAssert.assertJson; -@RunWith(MockitoJUnitRunner.class) public class CopyActionTest { - private static final String DEFAULT_ORG_UUID = "U1"; + private static final String LANGUAGE_1 = "lang1"; + private static final String LANGUAGE_2 = "lang2"; @Rule public DbTester db = DbTester.create(); - @Rule public ExpectedException expectedException = ExpectedException.none(); - @Rule - public UserSessionRule userSessionRule = UserSessionRule.standalone(); + public UserSessionRule userSession = UserSessionRule.standalone(); + @Rule + public JUnitTempFolder tempDir = new JUnitTempFolder(); + private QProfileFactory profileFactory = new QProfileFactory(db.getDbClient(), new SequenceUuidFactory(), System2.INSTANCE); + private TestBackuper backuper = new TestBackuper(); + private QProfileCopier profileCopier = new QProfileCopier(db.getDbClient(), profileFactory, backuper, tempDir); + private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); + private Languages languages = LanguageTesting.newLanguages(LANGUAGE_1, LANGUAGE_2); + private QProfileWsSupport wsSupport = new QProfileWsSupport(db.getDbClient(), userSession, defaultOrganizationProvider); + private CopyAction underTest = new CopyAction(db.getDbClient(), profileCopier, languages, wsSupport); + private WsActionTester tester = new WsActionTester(underTest); - private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.fromUuid(DEFAULT_ORG_UUID); - private WsTester tester; + @Test + public void test_definition() { + WebService.Action definition = tester.getDef(); - // TODO Replace with proper DbTester + EsTester medium test during removal of DaoV2 - @Mock - private QProfileCopier qProfileCopier; + assertThat(definition.key()).isEqualTo("copy"); + assertThat(definition.isInternal()).isFalse(); + assertThat(definition.since()).isEqualTo("5.2"); + assertThat(definition.isPost()).isTrue(); - @Before - public void setUp() { - tester = new WsTester(new QProfilesWs( - mock(RuleActivationActions.class), - mock(BulkRuleActivationActions.class), - new CopyAction(db.getDbClient(), qProfileCopier, LanguageTesting.newLanguages("xoo"), new QProfileWsSupport(db.getDbClient(), userSessionRule, defaultOrganizationProvider)))); + assertThat(definition.params()).extracting(WebService.Param::key).containsOnly("fromKey", "toName"); + assertThat(definition.param("fromKey").isRequired()).isTrue(); + assertThat(definition.param("toName").isRequired()).isTrue(); } @Test - public void copy_nominal() throws Exception { + public void create_profile_with_specified_name_and_copy_rules_from_source_profile() throws Exception { logInAsQProfileAdministrator(); + QualityProfileDto sourceProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(LANGUAGE_1)); + TestResponse response = tester.newRequest() + .setMethod("POST") + .setParam("fromKey", sourceProfile.getKey()) + .setParam("toName", "target-name") + .execute(); - String fromProfileKey = "xoo-sonar-way-23456"; - String toName = "Other Sonar Way"; + String generatedUuid = "1"; + assertJson(response.getInput()).isSimilarTo("{" + + " \"key\": \"" + generatedUuid + "\"," + + " \"name\": \"target-name\"," + + " \"language\": \"lang1\"," + + " \"languageName\": \"Lang1\"," + + " \"isDefault\": false," + + " \"isInherited\": false" + + "}"); + QualityProfileDto loadedProfile = db.getDbClient().qualityProfileDao().selectByNameAndLanguage(db.getDefaultOrganization(), "target-name", sourceProfile.getLanguage(), + db.getSession()); + assertThat(loadedProfile.getKey()).isEqualTo(generatedUuid); + assertThat(loadedProfile.isDefault()).isFalse(); + assertThat(loadedProfile.getParentKee()).isNull(); + + assertThat(backuper.backupedProfile.getKey()).isEqualTo(sourceProfile.getKey()); + assertThat(backuper.restoredProfile.getOrganizationUuid()).isEqualTo(sourceProfile.getOrganizationUuid()); + assertThat(backuper.restoredProfile.getLanguage()).isEqualTo(sourceProfile.getLanguage()); + assertThat(backuper.restoredProfile.getName()).isEqualTo("target-name"); + assertThat(backuper.restoredProfile.getKey()).isEqualTo(generatedUuid); + assertThat(backuper.restoredProfile.getParentKee()).isNull(); + } - when(qProfileCopier.copyToName(any(DbSession.class), eq(fromProfileKey), eq(toName))).thenReturn( - QualityProfileDto.createFor("xoo-other-sonar-way-12345") - .setName(toName) - .setLanguage("xoo")); + @Test + public void copy_rules_on_existing_profile() throws Exception { + logInAsQProfileAdministrator(); + QualityProfileDto sourceProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(LANGUAGE_1)); + QualityProfileDto targetProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(LANGUAGE_1)); - tester.newPostRequest("api/qualityprofiles", "copy") - .setParam("fromKey", fromProfileKey) - .setParam("toName", toName) - .execute().assertJson(getClass(), "copy_nominal.json"); + TestResponse response = tester.newRequest() + .setMethod("POST") + .setParam("fromKey", sourceProfile.getKey()) + .setParam("toName", targetProfile.getName()) + .execute(); - verify(qProfileCopier).copyToName(any(DbSession.class), eq(fromProfileKey), eq(toName)); + assertJson(response.getInput()).isSimilarTo("{" + + " \"key\": \"" + targetProfile.getKey() + "\"," + + " \"name\": \"" + targetProfile.getName() + "\"," + + " \"language\": \"lang1\"," + + " \"languageName\": \"Lang1\"," + + " \"isDefault\": " + targetProfile.isDefault() + "," + + " \"isInherited\": false" + + "}"); + QualityProfileDto loadedProfile = db.getDbClient().qualityProfileDao().selectByKey(db.getSession(), targetProfile.getKey()); + assertThat(loadedProfile).isNotNull(); + + assertThat(backuper.backupedProfile.getKey()).isEqualTo(sourceProfile.getKey()); + assertThat(backuper.restoredProfile.getKey()).isEqualTo(targetProfile.getKey()); } @Test - public void copy_with_parent() throws Exception { + public void create_profile_with_same_parent_as_source_profile() throws Exception { logInAsQProfileAdministrator(); - String fromProfileKey = "xoo-sonar-way-23456"; - String toName = "Other Sonar Way"; - - when(qProfileCopier.copyToName(any(DbSession.class), eq(fromProfileKey), eq(toName))).thenReturn( - QualityProfileDto.createFor("xoo-other-sonar-way-12345") - .setName(toName) - .setLanguage("xoo") - .setParentKee("xoo-parent-profile-01324")); + QualityProfileDto parentProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(LANGUAGE_1)); + QualityProfileDto sourceProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(LANGUAGE_1), p -> p.setParentKee(parentProfile.getKey())); - tester.newPostRequest("api/qualityprofiles", "copy") - .setParam("fromKey", fromProfileKey) - .setParam("toName", toName) - .execute().assertJson(getClass(), "copy_with_parent.json"); + TestResponse response = tester.newRequest() + .setMethod("POST") + .setParam("fromKey", sourceProfile.getKey()) + .setParam("toName", "target-name") + .execute(); - verify(qProfileCopier).copyToName(any(DbSession.class), eq(fromProfileKey), eq(toName)); + String generatedUuid = "1"; + assertJson(response.getInput()).isSimilarTo("{" + + " \"key\": \"" + generatedUuid + "\"," + + " \"name\": \"target-name\"," + + " \"language\": \"lang1\"," + + " \"languageName\": \"Lang1\"," + + " \"isDefault\": false," + + " \"isInherited\": true" + + "}"); + QualityProfileDto loadedProfile = db.getDbClient().qualityProfileDao().selectByNameAndLanguage(db.getDefaultOrganization(), "target-name", sourceProfile.getLanguage(), + db.getSession()); + assertThat(loadedProfile.getKey()).isEqualTo(generatedUuid); + assertThat(loadedProfile.isDefault()).isFalse(); + assertThat(loadedProfile.getParentKee()).isEqualTo(parentProfile.getKey()); + + assertThat(backuper.backupedProfile.getKey()).isEqualTo(sourceProfile.getKey()); + assertThat(backuper.restoredProfile.getOrganizationUuid()).isEqualTo(sourceProfile.getOrganizationUuid()); + assertThat(backuper.restoredProfile.getLanguage()).isEqualTo(sourceProfile.getLanguage()); + assertThat(backuper.restoredProfile.getName()).isEqualTo("target-name"); + assertThat(backuper.restoredProfile.getKey()).isEqualTo(generatedUuid); + assertThat(backuper.restoredProfile.getParentKee()).isEqualTo(parentProfile.getKey()); } @Test - public void fail_if_parameter_fromKey_is_missing() throws Exception { - logInAsQProfileAdministrator(); + public void throw_UnauthorizedException_if_not_logged_in() { + userSession.anonymous(); - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("The 'fromKey' parameter is missing"); + expectedException.expect(UnauthorizedException.class); + expectedException.expectMessage("Authentication is required"); - tester.newPostRequest("api/qualityprofiles", "copy") - .setParam("toName", "Other Sonar Way") + tester.newRequest() + .setMethod("POST") + .setParam("fromKey", "foo") + .setParam("toName", "bar") .execute(); } @Test - public void fail_if_parameter_toName_is_missing() throws Exception { - logInAsQProfileAdministrator(); + public void throw_ForbiddenException_if_not_profile_administrator() { + userSession.logIn().addPermission(OrganizationPermission.SCAN, db.getDefaultOrganization()); - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("The 'toName' parameter is missing"); + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage("Insufficient privileges"); - tester.newPostRequest("api/qualityprofiles", "copy") - .setParam("fromKey", "sonar-way-xoo1-13245") + tester.newRequest() + .setMethod("POST") + .setParam("fromKey", "foo") + .setParam("toName", "bar") .execute(); } @Test - public void throw_ForbiddenException_if_not_profile_administrator() throws Exception { - userSessionRule.logIn(); + public void fail_if_parameter_fromKey_is_missing() throws Exception { + logInAsQProfileAdministrator(); - expectedException.expect(ForbiddenException.class); - expectedException.expectMessage("Insufficient privileges"); + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The 'fromKey' parameter is missing"); - tester.newPostRequest("api/qualityprofiles", "copy").execute(); + tester.newRequest() + .setParam("toName", "bar") + .execute(); } @Test - public void throw_UnauthorizedException_if_not_logged_in() throws Exception { - expectedException.expect(UnauthorizedException.class); - expectedException.expectMessage("Authentication is required"); + public void fail_if_parameter_toName_is_missing() throws Exception { + logInAsQProfileAdministrator(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The 'toName' parameter is missing"); - tester.newPostRequest("api/qualityprofiles", "copy").execute(); + tester.newRequest() + .setParam("fromKey", "foo") + .execute(); } private void logInAsQProfileAdministrator() { - userSessionRule + userSession .logIn() .addPermission(ADMINISTER_QUALITY_PROFILES, defaultOrganizationProvider.get().getUuid()); } + + private static class TestBackuper implements QProfileBackuper { + + private QualityProfileDto backupedProfile; + private QualityProfileDto restoredProfile; + + @Override + public void backup(DbSession dbSession, QualityProfileDto profile, Writer backupWriter) { + if (this.backupedProfile != null) { + throw new IllegalStateException("Already backup-ed/backed-up"); + } + this.backupedProfile = profile; + } + + @Override + public QProfileRestoreSummary restore(DbSession dbSession, Reader backup, OrganizationDto organization, @Nullable String overriddenProfileName) { + throw new UnsupportedOperationException(); + } + + @Override + public QProfileRestoreSummary restore(DbSession dbSession, Reader backup, QualityProfileDto profile) { + if (this.restoredProfile != null) { + throw new IllegalStateException("Already restored"); + } + this.restoredProfile = profile; + return new QProfileRestoreSummary(profile, new BulkChangeResult()); + } + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ExportActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ExportActionTest.java index 5d1746e875c..ebed78f6fef 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ExportActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ExportActionTest.java @@ -259,5 +259,10 @@ public class ExportActionTest { public QProfileRestoreSummary restore(DbSession dbSession, Reader backup, OrganizationDto organization, @Nullable String overriddenProfileName) { throw new UnsupportedOperationException(); } + + @Override + public QProfileRestoreSummary restore(DbSession dbSession, Reader backup, QualityProfileDto profile) { + throw new UnsupportedOperationException(); + } } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RestoreActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RestoreActionTest.java index 6bb5c652afa..b52bfef68b8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RestoreActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/RestoreActionTest.java @@ -92,7 +92,6 @@ public class RestoreActionTest { logInAsQProfileAdministrator(db.getDefaultOrganization()); TestResponse response = restore("", null); - assertThat(backuper.restoredSummary.getOrganization().getUuid()).isEqualTo(db.getDefaultOrganization().getUuid()); assertThat(backuper.restoredBackup).isEqualTo(""); assertThat(backuper.restoredSummary.getProfile().getName()).isEqualTo("the-name-in-backup"); JsonAssert.assertJson(response.getInput()).isSimilarTo("{" + @@ -115,7 +114,6 @@ public class RestoreActionTest { logInAsQProfileAdministrator(org); TestResponse response = restore("", org.getKey()); - assertThat(backuper.restoredSummary.getOrganization().getUuid()).isEqualTo(org.getUuid()); assertThat(backuper.restoredBackup).isEqualTo(""); assertThat(backuper.restoredSummary.getProfile().getName()).isEqualTo("the-name-in-backup"); JsonAssert.assertJson(response.getInput()).isSimilarTo("{" + @@ -226,8 +224,13 @@ public class RestoreActionTest { .setDefault(false) .setLanguage("xoo") .setName(overriddenProfileName != null ? overriddenProfileName : "the-name-in-backup"); - restoredSummary = new QProfileRestoreSummary(organization, profile, new BulkChangeResult()); + restoredSummary = new QProfileRestoreSummary(profile, new BulkChangeResult()); return restoredSummary; } + + @Override + public QProfileRestoreSummary restore(DbSession dbSession, Reader backup, QualityProfileDto profile) { + throw new UnsupportedOperationException(); + } } } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/CopyActionTest/copy_nominal.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/CopyActionTest/copy_nominal.json deleted file mode 100644 index bbf7ad61dc2..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/CopyActionTest/copy_nominal.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "key": "xoo-other-sonar-way-12345", - "name": "Other Sonar Way", - "language": "xoo", - "languageName": "Xoo", - "isDefault": false, - "isInherited": false -} \ No newline at end of file diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/CopyActionTest/copy_with_parent.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/CopyActionTest/copy_with_parent.json deleted file mode 100644 index 9fefdca7912..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/CopyActionTest/copy_with_parent.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "key": "xoo-other-sonar-way-12345", - "name": "Other Sonar Way", - "language": "xoo", - "languageName": "Xoo", - "isDefault": false, - "isInherited": true, - "parentKey": "xoo-parent-profile-01324" -} \ No newline at end of file -- 2.39.5