import org.junit.Rule;
import org.junit.Test;
-import org.sonar.api.utils.System2;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.db.DbTester;
import org.sonar.db.user.UserDto;
import org.sonar.server.es.EsTester;
-import org.sonar.server.user.index.UserIndex;
-import org.sonar.server.user.index.UserIndexer;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
@Rule
public LogTester logTester = new LogTester();
- private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
@Test
public void load_login_for_scm_account() {
UserDto user = db.users().insertUser(u -> u.setScmAccounts(asList("charlie", "jesuis@charlie.com")));
- userIndexer.indexAll();
- UserIndex index = new UserIndex(es.client(), System2.INSTANCE);
- ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(index);
+ ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(db.getDbClient());
assertThat(underTest.load("missing")).isNull();
assertThat(underTest.load("jesuis@charlie.com")).isEqualTo(user.getUuid());
public void warn_if_multiple_users_share_the_same_scm_account() {
db.users().insertUser(u -> u.setLogin("charlie").setScmAccounts(asList("charlie", "jesuis@charlie.com")));
db.users().insertUser(u -> u.setLogin("another.charlie").setScmAccounts(asList("charlie")));
- userIndexer.indexAll();
- UserIndex index = new UserIndex(es.client(), System2.INSTANCE);
- ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(index);
+ ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(db.getDbClient());
assertThat(underTest.load("charlie")).isNull();
assertThat(logTester.logs(LoggerLevel.WARN)).contains("Multiple users share the SCM account 'charlie': another.charlie, charlie");
@Test
public void load_by_multiple_scm_accounts_is_not_supported_yet() {
- UserIndex index = new UserIndex(es.client(), System2.INSTANCE);
- ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(index);
+ ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(db.getDbClient());
try {
underTest.loadAll(emptyList());
fail();
package org.sonar.ce.task.projectanalysis.issue;
import com.google.common.base.Joiner;
-import com.google.common.collect.Ordering;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.ce.task.projectanalysis.util.cache.CacheLoader;
-import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.server.user.index.UserDoc;
-import org.sonar.server.user.index.UserIndex;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.user.UserDto;
/**
* Loads the association between a SCM account and a SQ user
private static final Logger LOGGER = Loggers.get(ScmAccountToUserLoader.class);
- private final UserIndex index;
+ private final DbClient dbClient;
- public ScmAccountToUserLoader(UserIndex index) {
- this.index = index;
+ public ScmAccountToUserLoader(DbClient dbClient) {
+ this.dbClient = dbClient;
}
@Override
public String load(String scmAccount) {
- List<UserDoc> users = index.getAtMostThreeActiveUsersForScmAccount(scmAccount);
- if (users.size() == 1) {
- return users.get(0).uuid();
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ List<String> userUuids = dbClient.userDao().selectUserUuidByScmAccountOrLoginOrEmail(dbSession, scmAccount);
+ if (userUuids.size() == 1) {
+ return userUuids.iterator().next();
+ }
+ if (!userUuids.isEmpty()) {
+ List<UserDto> userDtos = dbClient.userDao().selectByUuids(dbSession, userUuids);
+ Collection<String> logins = userDtos.stream()
+ .map(UserDto::getLogin)
+ .sorted()
+ .toList();
+ LOGGER.warn(String.format("Multiple users share the SCM account '%s': %s", scmAccount, Joiner.on(", ").join(logins)));
+ }
+ return null;
}
- if (!users.isEmpty()) {
- // multiple users are associated to the same SCM account, for example
- // the same email
- Collection<String> logins = users.stream()
- .map(UserDoc::login)
- .sorted(Ordering.natural())
- .collect(MoreCollectors.toList(users.size()));
- LOGGER.warn(String.format("Multiple users share the SCM account '%s': %s", scmAccount, Joiner.on(", ").join(logins)));
- }
- return null;
}
@Override
assertThat(userDto.getSortedScmAccounts()).containsExactlyElementsOf(scmAccountsUser1);
}
+ @Test
+ public void selectUserUuidByScmAccountOrLoginOrEmail_findsCorrectResults() {
+ String user1 = db.users().insertUser(user -> user.setLogin("user1").setEmail("toto@tata.com")).getUuid();
+ String user2 = db.users().insertUser(user -> user.setLogin("user2")).getUuid();
+ String user3 = db.users().insertUser(user -> user.setLogin("user3").setScmAccounts(List.of("scmuser3", "scmuser3bis"))).getUuid();
+ db.users().insertUser();
+ db.users().insertUser(user -> user.setLogin("inactive_user1").setActive(false));
+ db.users().insertUser(user -> user.setLogin("inactive_user2").setActive(false).setScmAccounts(List.of("inactive_user2")));
+
+ assertThat(underTest.selectUserUuidByScmAccountOrLoginOrEmail(session, "toto@tata.com")).containsExactly(user1);
+ assertThat(underTest.selectUserUuidByScmAccountOrLoginOrEmail(session, "user2")).containsExactly(user2);
+ assertThat(underTest.selectUserUuidByScmAccountOrLoginOrEmail(session, "scmuser3")).containsExactly(user3);
+ assertThat(underTest.selectUserUuidByScmAccountOrLoginOrEmail(session, "inactive_user1")).isEmpty();
+ assertThat(underTest.selectUserUuidByScmAccountOrLoginOrEmail(session, "inactive_user2")).isEmpty();
+ }
+
@Test
public void selectUserByLogin_ignore_inactive() {
db.users().insertUser(user -> user.setLogin("user1"));
.setLogin("john")
.setName("John")
.setEmail("jo@hn.com")
- .setScmAccounts(List.of("jo.hn", "john2", ""))
+ .setScmAccounts(List.of("jo.hn", "john2", "", "JoHn"))
.setActive(true)
.setResetPassword(true)
.setSalt("1234")
assertThat(user.getEmail()).isEqualTo("jo@hn.com");
assertThat(user.isActive()).isTrue();
assertThat(user.isResetPassword()).isTrue();
- assertThat(user.getSortedScmAccounts()).containsExactly("jo.hn", "john2");
+ assertThat(user.getSortedScmAccounts()).containsExactly("jo.hn", "john", "john2");
assertThat(user.getSalt()).isEqualTo("1234");
assertThat(user.getCryptedPassword()).isEqualTo("abcd");
assertThat(user.getHashMethod()).isEqualTo("SHA1");
public void update_scmAccounts() {
UserDto user = db.users().insertUser(u -> u.setScmAccounts(emptyList()));
- underTest.update(db.getSession(), user.setScmAccounts(List.of("jo.hn", "john2", "johndoo", "")));
+ underTest.update(db.getSession(), user.setScmAccounts(List.of("jo.hn", "john2", "johndooUpper", "")));
UserDto reloaded = Objects.requireNonNull(underTest.selectByUuid(db.getSession(), user.getUuid()));
- assertThat(reloaded.getSortedScmAccounts()).containsExactly("jo.hn", "john2", "johndoo");
+ assertThat(reloaded.getSortedScmAccounts()).containsExactly("jo.hn", "john2", "johndooupper");
underTest.update(db.getSession(), user.setScmAccounts(List.of("jo.hn", "john2")));
reloaded = Objects.requireNonNull(underTest.selectByUuid(db.getSession(), user.getUuid()));
private static void insertScmAccounts(DbSession session, String userUuid, List<String> scmAccounts) {
scmAccounts.stream()
.filter(StringUtils::isNotBlank)
- .forEach(scmAccount -> mapper(session).insertScmAccount(userUuid, scmAccount));
+ .forEach(scmAccount -> mapper(session).insertScmAccount(userUuid, scmAccount.toLowerCase(ENGLISH)));
}
public UserDto update(DbSession session, UserDto dto) {
return mapper(session).selectNullableByScmAccountOrLoginOrEmail(scmAccountOrLoginOrEmail);
}
+ /**
+ * This method is optimized for the first analysis: we tried to keep performance optimal (<10ms) for projects with large number of contributors
+ */
+ public List<String> selectUserUuidByScmAccountOrLoginOrEmail(DbSession session, String scmAccountOrLoginOrEmail) {
+ return mapper(session).selectUserUuidByScmAccountOrLoginOrEmail(scmAccountOrLoginOrEmail);
+ }
+
/**
* Search for an active user with the given emailCaseInsensitive exits in database
* Select is case insensitive. Result for searching 'mail@emailCaseInsensitive.com' or 'Mail@Email.com' is the same
@CheckForNull
List<UserDto> selectNullableByScmAccountOrLoginOrEmail(@Param("scmAccount") String scmAccountOrLoginOrEmail);
+ List<String> selectUserUuidByScmAccountOrLoginOrEmail(@Param("scmAccount") String scmAccountOrLoginOrEmail);
+
/**
* Select user by login. Note that disabled users are ignored.
*/
user_uuid = #{userUuid,jdbcType=VARCHAR}
</delete>
+ <select id="selectUserUuidByScmAccountOrLoginOrEmail" parameterType="String" resultType="String">
+ select user_uuid as uuid from scm_accounts sa
+ left join users u on sa.user_uuid = u.uuid
+ where u.active=${_true} and sa.scm_account = lower(#{scmAccount,jdbcType=VARCHAR})
+ union
+ select uuid from
+ users u
+ where active=${_true} and (login=#{scmAccount,jdbcType=VARCHAR} or email=#{scmAccount,jdbcType=VARCHAR} )
+ </select>
+
</mapper>
"SCM_ACCOUNT" CHARACTER VARYING(255) NOT NULL
);
ALTER TABLE "SCM_ACCOUNTS" ADD CONSTRAINT "PK_SCM_ACCOUNTS" PRIMARY KEY("USER_UUID", "SCM_ACCOUNT");
+CREATE INDEX "SCM_ACCOUNTS_SCM_ACCOUNT" ON "SCM_ACCOUNTS"("SCM_ACCOUNT" NULLS FIRST);
CREATE TABLE "SESSION_TOKENS"(
"UUID" CHARACTER VARYING(40) NOT NULL,
CREATE INDEX "USERS_UPDATED_AT" ON "USERS"("UPDATED_AT" NULLS FIRST);
CREATE UNIQUE INDEX "UNIQ_EXTERNAL_ID" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER" NULLS FIRST, "EXTERNAL_ID" NULLS FIRST);
CREATE UNIQUE INDEX "UNIQ_EXTERNAL_LOGIN" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER" NULLS FIRST, "EXTERNAL_LOGIN" NULLS FIRST);
+CREATE INDEX "USERS_EMAIL" ON "USERS"("EMAIL" NULLS FIRST);
CREATE TABLE "WEBHOOK_DELIVERIES"(
"UUID" CHARACTER VARYING(40) NOT NULL,
*/
package org.sonar.db.user;
+import java.util.Locale;
import javax.annotation.Nullable;
import static java.util.Collections.emptyList;
.setLogin(randomAlphanumeric(30))
.setName(randomAlphanumeric(30))
.setEmail(randomAlphanumeric(30))
- .setScmAccounts(singletonList(randomAlphanumeric(40)))
+ .setScmAccounts(singletonList(randomAlphanumeric(40).toLowerCase(Locale.ENGLISH)))
.setExternalId(randomAlphanumeric(40))
.setExternalLogin(randomAlphanumeric(40))
.setExternalIdentityProvider(randomAlphanumeric(40))
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.v100;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class CreateIndexForEmailOnUsersTable extends DdlChange {
+
+ @VisibleForTesting
+ static final String INDEX_NAME = "users_email";
+ @VisibleForTesting
+ static final String TABLE_NAME = "users";
+ @VisibleForTesting
+ static final String COLUMN_NAME = "email";
+
+ public CreateIndexForEmailOnUsersTable(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ try (Connection connection = getDatabase().getDataSource().getConnection()) {
+ createIndex(context, connection);
+ }
+ }
+
+ private static void createIndex(Context context, Connection connection) {
+ if (!DatabaseUtils.indexExistsIgnoreCase(TABLE_NAME, INDEX_NAME, connection)) {
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE_NAME)
+ .setName(INDEX_NAME)
+ .addColumn(COLUMN_NAME)
+ .setUnique(false)
+ .build());
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.v100;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.version.v100.CreateScmAccountsTable.SCM_ACCOUNT_COLUMN_NAME;
+import static org.sonar.server.platform.db.migration.version.v100.CreateScmAccountsTable.TABLE_NAME;
+
+public class CreateIndexForScmAccountOnScmAccountsTable extends DdlChange {
+
+ @VisibleForTesting
+ static final String INDEX_NAME = "scm_accounts_scm_account";
+
+ public CreateIndexForScmAccountOnScmAccountsTable(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ try (Connection connection = getDatabase().getDataSource().getConnection()) {
+ createIndex(context, connection);
+ }
+ }
+
+ private static void createIndex(Context context, Connection connection) {
+ if (!DatabaseUtils.indexExistsIgnoreCase(TABLE_NAME, INDEX_NAME, connection)) {
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE_NAME)
+ .setName(INDEX_NAME)
+ .addColumn(SCM_ACCOUNT_COLUMN_NAME)
+ .setUnique(false)
+ .build());
+ }
+ }
+}
.add(10_0_016, "Populate ncloc in 'Projects' table", PopulateNclocForForProjects.class)
.add(10_0_015, "Add 'scm_accounts' table", CreateScmAccountsTable.class)
.add(10_0_016, "Migrate scm accounts from 'users' to 'scm_accounts' table", MigrateScmAccountsFromUsersToScmAccounts.class)
+ .add(10_0_017, "Add index on 'scm_accounts.scm_account'", CreateIndexForScmAccountOnScmAccountsTable.class)
+ .add(10_0_018, "Add index on 'users.email'", CreateIndexForEmailOnUsersTable.class)
;
}
}
protected void execute(Context context) throws SQLException {
MassRowSplitter<ScmAccountRow> massRowSplitter = context.prepareMassRowSplitter();
- massRowSplitter.select("select u.uuid, u.scm_accounts from users u where u.active=? and not exists (select 1 from scm_accounts sa where sa.user_uuid = u.uuid)")
+ massRowSplitter.select("select u.uuid, lower(u.scm_accounts) from users u where u.active=? and not exists (select 1 from scm_accounts sa where sa.user_uuid = u.uuid)")
.setBoolean(1, true);
massRowSplitter.insert("insert into scm_accounts (user_uuid, scm_account) values (?, ?)");
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.v100;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+import static org.sonar.server.platform.db.migration.version.v100.CreateIndexForEmailOnUsersTable.COLUMN_NAME;
+import static org.sonar.server.platform.db.migration.version.v100.CreateIndexForEmailOnUsersTable.INDEX_NAME;
+import static org.sonar.server.platform.db.migration.version.v100.CreateIndexForEmailOnUsersTable.TABLE_NAME;
+
+public class CreateIndexForEmailOnUsersTableTest {
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(CreateIndexForEmailOnUsersTableTest.class, "schema.sql");
+
+ private final CreateIndexForEmailOnUsersTable createIndexForEmailOnUsersTable = new CreateIndexForEmailOnUsersTable(db.database());
+
+ @Test
+ public void migration_should_create_index() throws SQLException {
+ db.assertIndexDoesNotExist(TABLE_NAME, INDEX_NAME);
+
+ createIndexForEmailOnUsersTable.execute();
+
+ db.assertIndex(TABLE_NAME, INDEX_NAME, COLUMN_NAME);
+ }
+
+ @Test
+ public void migration_should_be_reentrant() throws SQLException {
+ createIndexForEmailOnUsersTable.execute();
+ createIndexForEmailOnUsersTable.execute();
+
+ db.assertIndex(TABLE_NAME, INDEX_NAME, COLUMN_NAME);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.v100;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+import static org.sonar.server.platform.db.migration.version.v100.CreateIndexForScmAccountOnScmAccountsTable.INDEX_NAME;
+import static org.sonar.server.platform.db.migration.version.v100.CreateScmAccountsTable.SCM_ACCOUNT_COLUMN_NAME;
+import static org.sonar.server.platform.db.migration.version.v100.CreateScmAccountsTable.TABLE_NAME;
+
+public class CreateIndexForScmAccountOnScmAccountsTableTest {
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(CreateIndexForScmAccountOnScmAccountsTableTest.class, "schema.sql");
+
+ private final CreateIndexForScmAccountOnScmAccountsTable createIndexForScmAccountOnScmAccountsTable = new CreateIndexForScmAccountOnScmAccountsTable(db.database());
+
+ @Test
+ public void migration_should_create_index() throws SQLException {
+ db.assertIndexDoesNotExist(TABLE_NAME, INDEX_NAME);
+
+ createIndexForScmAccountOnScmAccountsTable.execute();
+
+ db.assertIndex(TABLE_NAME, INDEX_NAME, SCM_ACCOUNT_COLUMN_NAME);
+ }
+
+ @Test
+ public void migration_should_be_reentrant() throws SQLException {
+ createIndexForScmAccountOnScmAccountsTable.execute();
+ createIndexForScmAccountOnScmAccountsTable.execute();
+
+ db.assertIndex(TABLE_NAME, INDEX_NAME, SCM_ACCOUNT_COLUMN_NAME);
+ }
+}
import java.sql.SQLException;
import java.util.HashMap;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
public class MigrateScmAccountsFromUsersToScmAccountsTest {
private static final UuidFactory UUID_FACTORY = UuidFactoryFast.getInstance();
- private static final String SCM_ACCOUNT1 = "scmAccount";
- private static final String SCM_ACCOUNT2 = "scmAccount2";
+ private static final String SCM_ACCOUNT1 = "scmaccount";
+ private static final String SCM_ACCOUNT2 = "scmaccount2";
+ private static final String SCM_ACCOUNT_CAMELCASE = "scmAccount3";
@Rule
public final CoreDbTester db = CoreDbTester.createForSchema(MigrateScmAccountsFromUsersToScmAccountsTest.class, "schema.sql");
assertThat(scmAccounts).containsExactly(new ScmAccountRow(userUuid, SCM_ACCOUNT1));
}
+ @Test
+ public void execute_whenUserHasOneScmAccountWithMixedCase_insertsInScmAccountsInLowerCase() throws SQLException {
+ String userUuid = insertUserAndGetUuid(format("%s%s%s", SCM_ACCOUNTS_SEPARATOR_CHAR, SCM_ACCOUNT_CAMELCASE, SCM_ACCOUNTS_SEPARATOR_CHAR));
+
+ migrateScmAccountsFromUsersToScmAccounts.execute();
+
+ Set<ScmAccountRow> scmAccounts = findAllScmAccounts();
+ assertThat(scmAccounts).containsExactly(new ScmAccountRow(userUuid, SCM_ACCOUNT_CAMELCASE.toLowerCase(Locale.ENGLISH)));
+ }
+
@Test
public void execute_whenUserHasTwoScmAccount_insertsInScmAccounts() throws SQLException {
String userUuid = insertUserAndGetUuid(format("%s%s%s%s%s",
--- /dev/null
+CREATE TABLE "USERS"(
+ "UUID" CHARACTER VARYING(255) NOT NULL,
+ "LOGIN" CHARACTER VARYING(255) NOT NULL,
+ "NAME" CHARACTER VARYING(200),
+ "EMAIL" CHARACTER VARYING(100),
+ "CRYPTED_PASSWORD" CHARACTER VARYING(100),
+ "SALT" CHARACTER VARYING(40),
+ "HASH_METHOD" CHARACTER VARYING(10),
+ "ACTIVE" BOOLEAN DEFAULT TRUE,
+ "SCM_ACCOUNTS" CHARACTER VARYING(4000),
+ "EXTERNAL_LOGIN" CHARACTER VARYING(255) NOT NULL,
+ "EXTERNAL_IDENTITY_PROVIDER" CHARACTER VARYING(100) NOT NULL,
+ "EXTERNAL_ID" CHARACTER VARYING(255) NOT NULL,
+ "USER_LOCAL" BOOLEAN NOT NULL,
+ "HOMEPAGE_TYPE" CHARACTER VARYING(40),
+ "HOMEPAGE_PARAMETER" CHARACTER VARYING(40),
+ "LAST_CONNECTION_DATE" BIGINT,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT,
+ "RESET_PASSWORD" BOOLEAN NOT NULL,
+ "LAST_SONARLINT_CONNECTION" BIGINT
+);
+ALTER TABLE "USERS" ADD CONSTRAINT "PK_USERS" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS"("LOGIN" NULLS FIRST);
+CREATE INDEX "USERS_UPDATED_AT" ON "USERS"("UPDATED_AT" NULLS FIRST);
+CREATE UNIQUE INDEX "UNIQ_EXTERNAL_ID" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER" NULLS FIRST, "EXTERNAL_ID" NULLS FIRST);
+CREATE UNIQUE INDEX "UNIQ_EXTERNAL_LOGIN" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER" NULLS FIRST, "EXTERNAL_LOGIN" NULLS FIRST);
--- /dev/null
+CREATE TABLE "SCM_ACCOUNTS"(
+ "USER_UUID" CHARACTER VARYING(255) NOT NULL,
+ "SCM_ACCOUNT" CHARACTER VARYING(255) NOT NULL
+);
+ALTER TABLE "SCM_ACCOUNTS" ADD CONSTRAINT "PK_SCM_ACCOUNTS" PRIMARY KEY("USER_UUID", "SCM_ACCOUNT");
.execute();
UserDto user = dbClient.userDao().selectByLogin(dbSession, "john");
- assertThat(user.getSortedScmAccounts()).containsExactly("Jon.1", "JON.2", "jon.3");
+ assertThat(user.getSortedScmAccounts()).containsExactly("jon.1", "jon.2", "jon.3");
}
@Test