@@ -123,7 +123,7 @@ public class ComputeEngineContainerImplTest { | |||
+ 26 // level 1 | |||
+ 60 // content of DaoModule | |||
+ 3 // content of EsModule | |||
+ 54 // content of CorePropertyDefinitions | |||
+ 53 // content of CorePropertyDefinitions | |||
+ 1 // StopFlagContainer | |||
); | |||
assertThat( |
@@ -1,4 +1,4 @@ | |||
INSERT INTO USERS(ID, UUID, LOGIN, NAME, EMAIL, EXTERNAL_ID, EXTERNAL_LOGIN, EXTERNAL_IDENTITY_PROVIDER, USER_LOCAL, CRYPTED_PASSWORD, SALT, HASH_METHOD, IS_ROOT, ONBOARDED, CREATED_AT, UPDATED_AT) VALUES (1, 'UuidnciQUUs7Zd3KPvFD', 'admin', 'Administrator', '', 'admin', 'admin', 'sonarqube', true, '$2a$12$uCkkXmhW5ThVK8mpBvnXOOJRLd64LJeHTeCkSuB3lfaR2N0AYBaSi', null, 'BCRYPT', false, false, '1418215735482', '1418215735482'); | |||
INSERT INTO USERS(ID, UUID, LOGIN, NAME, EMAIL, EXTERNAL_ID, EXTERNAL_LOGIN, EXTERNAL_IDENTITY_PROVIDER, USER_LOCAL, CRYPTED_PASSWORD, SALT, HASH_METHOD, IS_ROOT, ONBOARDED, CREATED_AT, UPDATED_AT) VALUES (1, 'UuidnciQUUs7Zd3KPvFD', 'admin', 'Administrator', '', 'admin', 'admin', 'sonarqube', true, '$2a$12$uCkkXmhW5ThVK8mpBvnXOOJRLd64LJeHTeCkSuB3lfaR2N0AYBaSi', null, 'BCRYPT', false, true, '1418215735482', '1418215735482'); | |||
ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2; | |||
INSERT INTO GROUPS(ID, ORGANIZATION_UUID, NAME, DESCRIPTION, CREATED_AT, UPDATED_AT) VALUES (1, 'AVdqnciQUUs7Zd3KPvFD', 'sonar-administrators', 'System administrators', '2011-09-26 22:27:51.0', '2011-09-26 22:27:51.0'); |
@@ -23,7 +23,6 @@ import java.sql.SQLException; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.step.DataChange; | |||
import org.sonar.server.platform.db.migration.step.Select; | |||
public class PopulateUsersOnboarded extends DataChange { | |||
@@ -41,14 +40,5 @@ public class PopulateUsersOnboarded extends DataChange { | |||
.setLong(2, system2.now()) | |||
.execute() | |||
.commit(); | |||
long users = context.prepareSelect("select count(u.id) from users u").get(Select.LONG_READER); | |||
if (users == 1) { | |||
context.prepareUpsert("update users set onboarded=?, updated_at=? where login=?") | |||
.setBoolean(1, false) | |||
.setLong(2, system2.now()) | |||
.setString(3, "admin") | |||
.execute() | |||
.commit(); | |||
} | |||
} | |||
} |
@@ -33,6 +33,8 @@ public class DbVersion76 implements DbVersion { | |||
.add(2503, "Delete useless 'sonar.dbcleaner.cleanDirectory' property", DeleteUselessProperty.class) | |||
.add(2504, "Delete useless module and folder level measures", DeleteModuleAndFolderMeasures.class) | |||
.add(2505, "Fix the direction values of certain metrics (prepare for migration of conditions)", FixDirectionOfMetrics.class) | |||
.add(2506, "Migrate quality gate conditions using warning, period and no more supported operations", MigrateNoMoreUsedQualityGateConditions.class); | |||
.add(2506, "Migrate quality gate conditions using warning, period and no more supported operations", MigrateNoMoreUsedQualityGateConditions.class) | |||
.add(2507, "Delete sonar.onboardingTutorial.showToNewUsers from settings", DeleteUselessOnboardingSetting.class) | |||
; | |||
} | |||
} |
@@ -0,0 +1,50 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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. | |||
* | |||
* This program 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.platform.db.migration.version.v76; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.step.DataChange; | |||
import org.sonar.server.platform.db.migration.step.MassUpdate; | |||
import java.sql.SQLException; | |||
/** | |||
* Remove the "sonar.onboardingTutorial.showToNewUsers" settings from the PROPERTIES table | |||
*/ | |||
public class DeleteUselessOnboardingSetting extends DataChange { | |||
public DeleteUselessOnboardingSetting(Database db) { | |||
super(db); | |||
} | |||
@Override | |||
public void execute(Context context) throws SQLException { | |||
MassUpdate massUpdate = context.prepareMassUpdate().rowPluralName("useless onboarding settings"); | |||
massUpdate.select("SELECT id FROM properties WHERE prop_key=?") | |||
.setString(1, "sonar.onboardingTutorial.showToNewUsers"); | |||
massUpdate.update("DELETE FROM properties WHERE id=?"); | |||
massUpdate.execute((row, update) -> { | |||
long propertyId = row.getLong(1); | |||
update.setLong(1, propertyId); | |||
return true; | |||
}); | |||
} | |||
} |
@@ -33,8 +33,8 @@ import static org.assertj.core.groups.Tuple.tuple; | |||
public class PopulateUsersOnboardedTest { | |||
private final static long PAST = 100_000_000_000l; | |||
private final static long NOW = 500_000_000_000l; | |||
private final static long PAST = 100_000_000_000L; | |||
private final static long NOW = 500_000_000_000L; | |||
private System2 system2 = new TestSystem2().setNow(NOW); | |||
@@ -54,24 +54,6 @@ public class PopulateUsersOnboardedTest { | |||
assertUsers(tuple("admin", true, NOW), tuple("user", true, NOW)); | |||
} | |||
@Test | |||
public void set_onboarded_to_false_when_single_admin_user() throws SQLException { | |||
insertUser("admin"); | |||
underTest.execute(); | |||
assertUsers(tuple("admin", false, NOW)); | |||
} | |||
@Test | |||
public void set_onboarded_to_true_when_single_user_but_not_admin() throws SQLException { | |||
insertUser("user"); | |||
underTest.execute(); | |||
assertUsers(tuple("user", true, NOW)); | |||
} | |||
private void insertUser(String login) { | |||
db.executeInsert("USERS", "LOGIN", login, "ONBOARDED", false, "IS_ROOT", true, "CREATED_AT", PAST, "UPDATED_AT", PAST); | |||
} |
@@ -35,7 +35,7 @@ public class DbVersion76Test { | |||
@Test | |||
public void verify_migration_count() { | |||
verifyMigrationCount(underTest, 7); | |||
verifyMigrationCount(underTest, 8); | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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. | |||
* | |||
* This program 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.platform.db.migration.version.v76; | |||
import com.google.common.collect.ImmutableMap; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.db.CoreDbTester; | |||
import java.sql.SQLException; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import static java.lang.String.valueOf; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class DeleteUselessOnboardingSettingTest { | |||
private static final String TABLE_PROPERTIES = "properties"; | |||
@Rule | |||
public CoreDbTester db = CoreDbTester.createForSchema(DeleteUselessOnboardingSettingTest.class, "properties.sql"); | |||
private DeleteUselessOnboardingSetting underTest = new DeleteUselessOnboardingSetting(db.database()); | |||
@Test | |||
public void migration_has_no_effect_on_empty_tables() throws SQLException { | |||
underTest.execute(); | |||
assertThat(db.countRowsOfTable(TABLE_PROPERTIES)).isZero(); | |||
} | |||
@Test | |||
public void migration_removes_onboarding_setting() throws SQLException { | |||
insertProperty("sonar.onboardingTutorial.showToNewUsers"); | |||
underTest.execute(); | |||
assertThat(db.countRowsOfTable(TABLE_PROPERTIES)).isZero(); | |||
} | |||
private void insertProperty(String key) { | |||
Map<String, Object> values = new HashMap<>(ImmutableMap.of( | |||
"PROP_KEY", key, | |||
"IS_EMPTY", false, | |||
"CREATED_AT", 456789)); | |||
db.executeInsert(TABLE_PROPERTIES, values); | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
CREATE TABLE "PROPERTIES" ( | |||
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), | |||
"PROP_KEY" VARCHAR(512) NOT NULL, | |||
"RESOURCE_ID" INTEGER, | |||
"USER_ID" INTEGER, | |||
"IS_EMPTY" BOOLEAN NOT NULL, | |||
"TEXT_VALUE" VARCHAR(4000), | |||
"CLOB_VALUE" CLOB, | |||
"CREATED_AT" BIGINT | |||
); | |||
CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES" ("PROP_KEY"); |
@@ -110,6 +110,7 @@ public class ProcessProperties { | |||
SONARCLOUD_ENABLED("sonar.sonarcloud.enabled", "false"), | |||
SONAR_PRISMIC_ACCESS_TOKEN("sonar.prismic.accessToken", ""), | |||
SONAR_ANALYTICS_TRACKING_ID("sonar.analytics.trackingId", ""), | |||
ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS("sonar.onboardingTutorial.showToNewUsers", "true"), | |||
BITBUCKETCLOUD_APP_KEY("sonar.bitbucketcloud.appKey", "sonarcloud"), | |||
BITBUCKETCLOUD_ENDPOINT("sonar.bitbucketcloud.endpoint", "https://api.bitbucket.org"), |
@@ -56,9 +56,9 @@ import static java.lang.String.format; | |||
import static java.util.Arrays.stream; | |||
import static java.util.stream.Stream.concat; | |||
import static org.sonar.api.CoreProperties.DEFAULT_ISSUE_ASSIGNEE; | |||
import static org.sonar.core.config.CorePropertyDefinitions.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS; | |||
import static org.sonar.core.util.Slug.slugify; | |||
import static org.sonar.core.util.stream.MoreCollectors.toList; | |||
import static org.sonar.process.ProcessProperties.Property.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS; | |||
import static org.sonar.server.ws.WsUtils.checkRequest; | |||
@ServerSide | |||
@@ -317,7 +317,7 @@ public class UserUpdater { | |||
} | |||
private void setOnboarded(UserDto userDto) { | |||
boolean showOnboarding = config.getBoolean(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS).orElse(false); | |||
boolean showOnboarding = config.getBoolean(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS.getKey()).orElse(false); | |||
userDto.setOnboarded(!showOnboarding); | |||
} | |||
@@ -19,9 +19,17 @@ | |||
*/ | |||
package org.sonar.server.user.ws; | |||
import org.sonar.api.config.Configuration; | |||
import org.sonar.core.platform.Module; | |||
import org.sonar.process.ProcessProperties; | |||
public class UsersWsModule extends Module { | |||
private final Configuration configuration; | |||
public UsersWsModule(Configuration configuration) { | |||
this.configuration = configuration; | |||
} | |||
@Override | |||
protected void configureModule() { | |||
add( | |||
@@ -36,9 +44,13 @@ public class UsersWsModule extends Module { | |||
IdentityProvidersAction.class, | |||
UserPropertiesWs.class, | |||
UserJsonWriter.class, | |||
SkipOnboardingTutorialAction.class, | |||
SetHomepageAction.class, | |||
HomepageTypesImpl.class, | |||
SetSettingAction.class); | |||
if (configuration.getBoolean(ProcessProperties.Property.SONARCLOUD_ENABLED.getKey()).orElse(false)) { | |||
// onboarding tutorial is available only in SonarCloud | |||
add(SkipOnboardingTutorialAction.class); | |||
} | |||
} | |||
} |
@@ -61,8 +61,8 @@ import static com.google.common.collect.Sets.newHashSet; | |||
import static java.util.Arrays.stream; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.sonar.core.config.CorePropertyDefinitions.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS; | |||
import static org.sonar.db.user.UserTesting.newUserDto; | |||
import static org.sonar.process.ProcessProperties.Property.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS; | |||
import static org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy.FORBID; | |||
import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC; | |||
import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException; | |||
@@ -214,7 +214,7 @@ public class UserRegistrarImplTest { | |||
@Test | |||
public void authenticate_new_user_sets_onboarded_flag_to_false_when_onboarding_setting_is_set_to_true() { | |||
organizationFlags.setEnabled(true); | |||
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, true); | |||
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS.getKey(), true); | |||
underTest.register(UserRegistration.builder() | |||
.setUserIdentity(USER_IDENTITY) | |||
@@ -230,7 +230,7 @@ public class UserRegistrarImplTest { | |||
@Test | |||
public void authenticate_new_user_sets_onboarded_flag_to_true_when_onboarding_setting_is_set_to_false() { | |||
organizationFlags.setEnabled(true); | |||
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, false); | |||
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS.getKey(), false); | |||
underTest.register(UserRegistration.builder() | |||
.setUserIdentity(USER_IDENTITY) |
@@ -59,8 +59,8 @@ import static org.mockito.ArgumentMatchers.any; | |||
import static org.mockito.ArgumentMatchers.eq; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.sonar.core.config.CorePropertyDefinitions.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS; | |||
import static org.sonar.db.user.UserTesting.newLocalUser; | |||
import static org.sonar.process.ProcessProperties.Property.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS; | |||
import static org.sonar.server.user.ExternalIdentity.SQ_AUTHORITY; | |||
public class UserUpdaterCreateTest { | |||
@@ -282,7 +282,7 @@ public class UserUpdaterCreateTest { | |||
@Test | |||
public void create_not_onboarded_user_if_onboarding_setting_is_set_to_false() { | |||
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, false); | |||
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS.getKey(), false); | |||
createDefaultGroup(); | |||
underTest.createAndCommit(db.getSession(), NewUser.builder() | |||
@@ -296,7 +296,7 @@ public class UserUpdaterCreateTest { | |||
@Test | |||
public void create_onboarded_user_if_onboarding_setting_is_set_to_true() { | |||
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, true); | |||
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS.getKey(), true); | |||
createDefaultGroup(); | |||
underTest.createAndCommit(db.getSession(), NewUser.builder() |
@@ -47,7 +47,7 @@ import static java.lang.String.format; | |||
import static java.util.Collections.singletonList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.sonar.core.config.CorePropertyDefinitions.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS; | |||
import static org.sonar.process.ProcessProperties.Property.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS; | |||
public class UserUpdaterReactivateTest { | |||
@@ -285,7 +285,7 @@ public class UserUpdaterReactivateTest { | |||
@Test | |||
public void reactivate_not_onboarded_user_if_onboarding_setting_is_set_to_false() { | |||
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, false); | |||
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS.getKey(), false); | |||
UserDto user = db.users().insertDisabledUser(u -> u.setOnboarded(false)); | |||
createDefaultGroup(); | |||
@@ -300,7 +300,7 @@ public class UserUpdaterReactivateTest { | |||
@Test | |||
public void reactivate_onboarded_user_if_onboarding_setting_is_set_to_true() { | |||
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, true); | |||
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS.getKey(), true); | |||
UserDto user = db.users().insertDisabledUser(u -> u.setOnboarded(true)); | |||
createDefaultGroup(); | |||
@@ -20,15 +20,29 @@ | |||
package org.sonar.server.user.ws; | |||
import org.junit.Test; | |||
import org.sonar.api.config.internal.ConfigurationBridge; | |||
import org.sonar.api.config.internal.MapSettings; | |||
import org.sonar.core.platform.ComponentContainer; | |||
import org.sonar.process.ProcessProperties; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class UsersWsModuleTest { | |||
private MapSettings settings = new MapSettings(); | |||
@Test | |||
public void verify_count_of_added_components() { | |||
ComponentContainer container = new ComponentContainer(); | |||
new UsersWsModule().configure(container); | |||
new UsersWsModule(new ConfigurationBridge(settings)).configure(container); | |||
assertThat(container.size()).isEqualTo(2 + 14); | |||
} | |||
@Test | |||
public void verify_count_of_added_components_in_sonarcloud() { | |||
settings.setProperty(ProcessProperties.Property.SONARCLOUD_ENABLED.getKey(), true); | |||
ComponentContainer container = new ComponentContainer(); | |||
new UsersWsModule(new ConfigurationBridge(settings)).configure(container); | |||
assertThat(container.size()).isEqualTo(2 + 15); | |||
} | |||
} |
@@ -140,18 +140,14 @@ export class StartupModal extends React.PureComponent<Props, State> { | |||
}; | |||
tryAutoOpenOnboarding = () => { | |||
const { currentUser, location } = this.props; | |||
if ( | |||
currentUser.showOnboardingTutorial && | |||
isSonarCloud() && | |||
this.props.currentUser.showOnboardingTutorial && | |||
!['/about', '/documentation', '/onboarding', '/projects/create', '/create-organization'].some( | |||
path => location.pathname.startsWith(path) | |||
path => this.props.location.pathname.startsWith(path) | |||
) | |||
) { | |||
if (isSonarCloud()) { | |||
this.openOnboarding(); | |||
} else { | |||
this.openProjectOnboarding(); | |||
} | |||
this.openOnboarding(); | |||
} | |||
}; | |||
@@ -51,8 +51,7 @@ const LOGGED_IN_USER: T.LoggedInUser = { | |||
isLoggedIn: true, | |||
login: 'luke', | |||
name: 'Skywalker', | |||
scmAccounts: [], | |||
showOnboardingTutorial: false | |||
scmAccounts: [] | |||
}; | |||
beforeEach(() => { |
@@ -77,8 +77,7 @@ const user: T.LoggedInUser = { | |||
isLoggedIn: true, | |||
login: 'luke', | |||
name: 'Skywalker', | |||
scmAccounts: [], | |||
showOnboardingTutorial: false | |||
scmAccounts: [] | |||
}; | |||
const fooAlmOrganization = { |
@@ -44,7 +44,6 @@ public class CorePropertyDefinitions { | |||
private static final String CATEGORY_ORGANIZATIONS = "organizations"; | |||
public static final String ORGANIZATIONS_ANYONE_CAN_CREATE = "sonar.organizations.anyoneCanCreate"; | |||
public static final String ORGANIZATIONS_CREATE_PERSONAL_ORG = "sonar.organizations.createPersonalOrg"; | |||
public static final String ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS = "sonar.onboardingTutorial.showToNewUsers"; | |||
public static final String DISABLE_NOTIFICATION_ON_BUILT_IN_QPROFILES = "sonar.builtInQualityProfiles.disableNotificationOnUpdate"; | |||
private CorePropertyDefinitions() { | |||
@@ -92,13 +91,6 @@ public class CorePropertyDefinitions { | |||
.multiValues(true) | |||
.defaultValue(CoreProperties.PREVIEW_EXCLUDE_PLUGINS_DEFAULT_VALUE) | |||
.build(), | |||
PropertyDefinition.builder(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS) | |||
.name("Show an onboarding tutorial to new users") | |||
.type(BOOLEAN) | |||
.description("Show an onboarding tutorial to new users, that explains how to analyze a first project, after logging in for the first time.") | |||
.category(CoreProperties.CATEGORY_GENERAL) | |||
.defaultValue(String.valueOf(false)) | |||
.build(), | |||
PropertyDefinition.builder("sonar.authenticator.downcase") | |||
.name("Downcase login") | |||
.description("Downcase login during user authentication, typically for Active Directory") |
@@ -30,7 +30,7 @@ public class CorePropertyDefinitionsTest { | |||
@Test | |||
public void all() { | |||
List<PropertyDefinition> defs = CorePropertyDefinitions.all(); | |||
assertThat(defs).hasSize(54); | |||
assertThat(defs).hasSize(53); | |||
} | |||
@Test |