aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server
diff options
context:
space:
mode:
authorEric Hartmann <hartmann.eric@gmail.com>2017-06-19 16:41:19 +0200
committerSimon Brandhof <simon.brandhof@sonarsource.com>2017-07-05 21:02:58 +0200
commit884e73d80752e779949284176173028f5feb0100 (patch)
treee341f05bb80751f4056a7615d360b6fc9dd7d32e /server/sonar-server
parente964104ca94fdd7b975dedbbe8b15eb578b41f53 (diff)
downloadsonarqube-884e73d80752e779949284176173028f5feb0100.tar.gz
sonarqube-884e73d80752e779949284176173028f5feb0100.zip
MMF-935 experiment ES resilience of user creation
Diffstat (limited to 'server/sonar-server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticator.java39
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexer.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/es/BaseIndexer.java91
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/es/BulkIndexer.java66
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/es/RecoveryIndexer.java161
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/es/StartupIndexer.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/es/queue/package-info.java23
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexer.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexer.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationCreationImpl.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/organization/ws/AddMemberAction.java3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/organization/ws/RemoveMemberAction.java3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/permission/index/PermissionIndexer.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/permission/index/PermissionIndexerDao.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/property/InternalProperties.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexer.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexer.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java77
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/index/UserDoc.java19
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexDefinition.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexer.java122
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/index/UserResultSetIterator.java53
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/ws/CreateAction.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/ws/DeactivateAction.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/ws/SkipOnboardingTutorialAction.java7
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/ws/UpdateAction.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/view/index/ViewIndexer.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/SsoAuthenticatorTest.java11
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/UserIdentityAuthenticatorTest.java41
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/es/BulkIndexerTest.java48
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/es/RecoveryIndexerTest.java394
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/organization/OrganizationCreationImplTest.java7
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/organization/ws/RemoveMemberActionTest.java9
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchMembersActionTest.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java468
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/index/UserIndexTest.java10
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/index/UserIndexerTest.java59
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/index/UserResultSetIteratorTest.java101
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java13
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/ws/CreateActionTest.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/ws/DeactivateActionTest.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/ws/SearchActionTest.java9
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/ws/SkipOnboardingTutorialActionTest.java18
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/ws/UpdateActionTest.java5
50 files changed, 1202 insertions, 743 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticator.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticator.java
index 5b7ecb54220..396cea50b03 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticator.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticator.java
@@ -86,21 +86,21 @@ public class UserIdentityAuthenticator {
}
}
- private UserDto registerNewUser(DbSession dbSession, UserIdentity user, IdentityProvider provider, AuthenticationEvent.Source source) {
+ private UserDto registerNewUser(DbSession dbSession, UserIdentity identity, IdentityProvider provider, AuthenticationEvent.Source source) {
if (!provider.allowsUsersToSignUp()) {
throw AuthenticationException.newBuilder()
.setSource(source)
- .setLogin(user.getLogin())
+ .setLogin(identity.getLogin())
.setMessage(format("User signup disabled for provider '%s'", provider.getKey()))
.setPublicMessage(format("'%s' users are not allowed to sign up", provider.getKey()))
.build();
}
- String email = user.getEmail();
+ String email = identity.getEmail();
if (email != null && dbClient.userDao().doesEmailExist(dbSession, email)) {
throw AuthenticationException.newBuilder()
.setSource(source)
- .setLogin(user.getLogin())
+ .setLogin(identity.getLogin())
.setMessage(format("Email '%s' is already used", email))
.setPublicMessage(format(
"You can't sign up because email '%s' is already used by an existing user. This means that you probably already registered with another account.",
@@ -108,25 +108,22 @@ public class UserIdentityAuthenticator {
.build();
}
- String userLogin = user.getLogin();
- userUpdater.create(dbSession, NewUser.builder()
+ String userLogin = identity.getLogin();
+ return userUpdater.createAndCommit(dbSession, NewUser.builder()
.setLogin(userLogin)
- .setEmail(user.getEmail())
- .setName(user.getName())
- .setExternalIdentity(new ExternalIdentity(provider.getKey(), user.getProviderLogin()))
- .build());
- UserDto newUser = dbClient.userDao().selectOrFailByLogin(dbSession, userLogin);
- syncGroups(dbSession, user, newUser);
- return newUser;
+ .setEmail(identity.getEmail())
+ .setName(identity.getName())
+ .setExternalIdentity(new ExternalIdentity(provider.getKey(), identity.getProviderLogin()))
+ .build(), u -> syncGroups(dbSession, identity, u));
}
- private void registerExistingUser(DbSession dbSession, UserDto userDto, UserIdentity user, IdentityProvider provider) {
- userUpdater.update(dbSession, UpdateUser.create(userDto.getLogin())
- .setEmail(user.getEmail())
- .setName(user.getName())
- .setExternalIdentity(new ExternalIdentity(provider.getKey(), user.getProviderLogin()))
- .setPassword(null));
- syncGroups(dbSession, user, userDto);
+ private void registerExistingUser(DbSession dbSession, UserDto userDto, UserIdentity identity, IdentityProvider provider) {
+ UpdateUser update = UpdateUser.create(userDto.getLogin())
+ .setEmail(identity.getEmail())
+ .setName(identity.getName())
+ .setExternalIdentity(new ExternalIdentity(provider.getKey(), identity.getProviderLogin()))
+ .setPassword(null);
+ userUpdater.updateAndCommit(dbSession, update, u -> syncGroups(dbSession, identity, u));
}
private void syncGroups(DbSession dbSession, UserIdentity userIdentity, UserDto userDto) {
@@ -149,8 +146,6 @@ public class UserIdentityAuthenticator {
addGroups(dbSession, userDto, groupsToAdd, groupsByName);
removeGroups(dbSession, userDto, groupsToRemove, groupsByName);
-
- dbSession.commit();
}
private void addGroups(DbSession dbSession, UserDto userDto, Collection<String> groupsToAdd, Map<String, GroupDto> groupsByName) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexer.java
index 429ae9dedf6..d845806e06b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/index/ComponentIndexer.java
@@ -59,7 +59,7 @@ public class ComponentIndexer implements ProjectIndexer, NeedAuthorizationIndexe
}
@Override
- public void indexOnStartup(Set<IndexType> emptyIndexTypes) {
+ public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
doIndexByProjectUuid(null, Size.LARGE);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/BaseIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/es/BaseIndexer.java
deleted file mode 100644
index f83fa993468..00000000000
--- a/server/sonar-server/src/main/java/org/sonar/server/es/BaseIndexer.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.es;
-
-import com.google.common.base.Throwables;
-import com.google.common.util.concurrent.Uninterruptibles;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import org.picocontainer.Startable;
-import org.sonar.api.utils.System2;
-
-public abstract class BaseIndexer implements Startable {
-
- private final System2 system2;
- private final ThreadPoolExecutor executor;
- private final IndexType indexType;
- protected final EsClient esClient;
- private final String dateFieldName;
- private volatile long lastUpdatedAt = -1L;
-
- protected BaseIndexer(System2 system2, EsClient client, long threadKeepAliveSeconds, IndexType indexType,
- String dateFieldName) {
- this.system2 = system2;
- this.indexType = indexType;
- this.dateFieldName = dateFieldName;
- this.esClient = client;
- this.executor = new ThreadPoolExecutor(0, 1,
- threadKeepAliveSeconds, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
- }
-
- public void index(final IndexerTask task) {
- final long requestedAt = system2.now();
- Future submit = executor.submit(() -> {
- if (lastUpdatedAt == -1L) {
- lastUpdatedAt = esClient.getMaxFieldValue(indexType, dateFieldName);
- }
- if (requestedAt > lastUpdatedAt) {
- long l = task.index(lastUpdatedAt);
- // l can be 0 if no documents were indexed
- lastUpdatedAt = Math.max(l, lastUpdatedAt);
- }
- });
- try {
- Uninterruptibles.getUninterruptibly(submit);
- } catch (ExecutionException e) {
- Throwables.propagate(e);
- }
- }
-
- public void index() {
- index(this::doIndex);
- }
-
- protected abstract long doIndex(long lastUpdatedAt);
-
- @Override
- public void start() {
- // nothing to do at startup
- }
-
- @Override
- public void stop() {
- executor.shutdown();
- }
-
- @FunctionalInterface
- public interface IndexerTask {
- long index(long lastUpdatedAt);
- }
-
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/BulkIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/es/BulkIndexer.java
index 3d5b5439495..8aaff572899 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/es/BulkIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/es/BulkIndexer.java
@@ -20,10 +20,15 @@
package org.sonar.server.es;
import com.google.common.annotations.VisibleForTesting;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
+import javax.annotation.Nullable;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequestBuilder;
@@ -43,12 +48,15 @@ import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHitField;
import org.elasticsearch.search.sort.SortOrder;
-import org.picocontainer.Startable;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.core.util.ProgressLogger;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.es.EsQueueDto;
import static java.lang.String.format;
+import static java.util.stream.Collectors.toList;
/**
* Helper to bulk requests in an efficient way :
@@ -57,7 +65,7 @@ import static java.lang.String.format;
* <li>on large table indexing, replicas and automatic refresh can be temporarily disabled</li>
* </ul>
*/
-public class BulkIndexer implements Startable {
+public class BulkIndexer {
private static final Logger LOGGER = Loggers.get(BulkIndexer.class);
private static final ByteSizeValue FLUSH_BYTE_SIZE = new ByteSizeValue(1, ByteSizeUnit.MB);
@@ -69,13 +77,22 @@ public class BulkIndexer implements Startable {
private final String indexName;
private final BulkProcessor bulkProcessor;
private final AtomicLong counter = new AtomicLong(0L);
+ private final AtomicLong successCounter = new AtomicLong(0L);
private final SizeHandler sizeHandler;
+ private final BulkProcessorListener bulkProcessorListener;
+ @Nullable
+ private DbClient dbClient;
+ @Nullable
+ private DbSession dbSession;
+ private Collection<EsQueueDto> esQueueDtos;
public BulkIndexer(EsClient client, String indexName, Size size) {
+ this.dbClient = null;
this.client = client;
this.indexName = indexName;
this.sizeHandler = size.createHandler(Runtime2.INSTANCE);
- this.bulkProcessor = BulkProcessor.builder(client.nativeClient(), new BulkProcessorListener())
+ this.bulkProcessorListener = new BulkProcessorListener();
+ this.bulkProcessor = BulkProcessor.builder(client.nativeClient(), bulkProcessorListener)
.setBackoffPolicy(BackoffPolicy.exponentialBackoff())
.setBulkSize(FLUSH_BYTE_SIZE)
.setBulkActions(FLUSH_ACTIONS)
@@ -83,22 +100,36 @@ public class BulkIndexer implements Startable {
.build();
}
- @Override
public void start() {
sizeHandler.beforeStart(this);
counter.set(0L);
+ successCounter.set(0L);
}
- @Override
- public void stop() {
+ public void start(DbSession dbSession, DbClient dbClient, Collection<EsQueueDto> esQueueDtos) {
+ this.dbClient = dbClient;
+ this.dbSession = dbSession;
+ this.esQueueDtos = esQueueDtos;
+ sizeHandler.beforeStart(this);
+ counter.set(0L);
+ successCounter.set(0L);
+ }
+
+ /**
+ * @return the number of documents successfully indexed
+ */
+ public long stop() {
try {
- bulkProcessor.awaitClose(10, TimeUnit.MINUTES);
+ bulkProcessor.awaitClose(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
- throw new IllegalStateException("Elasticsearch bulk requests still being executed after 10 minutes", e);
+ throw new IllegalStateException("Elasticsearch bulk requests still being executed after 1 minute", e);
+ } finally {
+ dbSession = null;
}
client.prepareRefresh(indexName).get();
sizeHandler.afterStop(this);
+ return successCounter.get();
}
public void add(ActionRequest<?> request) {
@@ -161,7 +192,6 @@ public class BulkIndexer implements Startable {
}
private final class BulkProcessorListener implements Listener {
-
@Override
public void beforeBulk(long executionId, BulkRequest request) {
// no action required
@@ -174,14 +204,31 @@ public class BulkIndexer implements Startable {
for (BulkItemResponse item : response.getItems()) {
if (item.isFailed()) {
LOGGER.error("index [{}], type [{}], id [{}], message [{}]", item.getIndex(), item.getType(), item.getId(), item.getFailureMessage());
+ } else {
+ successCounter.incrementAndGet();
}
}
+
+ deleteSuccessfulItems(response);
}
@Override
public void afterBulk(long executionId, BulkRequest req, Throwable e) {
LOGGER.error("Fail to execute bulk index request: " + req, e);
}
+
+ private void deleteSuccessfulItems(BulkResponse bulkResponse) {
+ if (esQueueDtos != null) {
+ List<EsQueueDto> itemsToDelete = Arrays.stream(bulkResponse.getItems())
+ .filter(b -> !b.isFailed())
+ .map(b -> esQueueDtos.stream().filter(t -> b.getId().equals(t.getDocUuid())).findFirst().orElse(null))
+ .filter(Objects::nonNull)
+ .collect(toList());
+
+ dbClient.esQueueDao().delete(dbSession, itemsToDelete);
+ dbSession.commit();
+ }
+ }
}
public enum Size {
@@ -293,5 +340,4 @@ public class BulkIndexer implements Startable {
req.get();
}
}
-
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/RecoveryIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/es/RecoveryIndexer.java
new file mode 100644
index 00000000000..c7ffdb9b2d6
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/es/RecoveryIndexer.java
@@ -0,0 +1,161 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.es;
+
+import com.google.common.collect.ListMultimap;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import org.apache.commons.lang.math.RandomUtils;
+import org.sonar.api.Startable;
+import org.sonar.api.config.Settings;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.api.utils.log.Profiler;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.es.EsQueueDto;
+import org.sonar.server.user.index.UserIndexer;
+
+import static java.lang.String.format;
+
+public class RecoveryIndexer implements Startable {
+
+ private static final Logger LOGGER = Loggers.get(RecoveryIndexer.class);
+ private static final String LOG_PREFIX = "Elasticsearch recovery - ";
+ private static final String PROPERTY_INITIAL_DELAY = "sonar.search.recovery.initialDelayInMs";
+ private static final String PROPERTY_DELAY = "sonar.search.recovery.delayInMs";
+ private static final String PROPERTY_MIN_AGE = "sonar.search.recovery.minAgeInMs";
+ private static final String PROPERTY_LOOP_LIMIT = "sonar.search.recovery.loopLimit";
+ private static final long DEFAULT_DELAY_IN_MS = 5L * 60 * 1000;
+ private static final long DEFAULT_MIN_AGE_IN_MS = 5L * 60 * 1000;
+ private static final int DEFAULT_LOOP_LIMIT = 10_000;
+ private static final double CIRCUIT_BREAKER_IN_PERCENT = 0.3;
+
+ private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1,
+ new ThreadFactoryBuilder()
+ .setPriority(Thread.MIN_PRIORITY)
+ .setNameFormat("RecoveryIndexer-%d")
+ .build());
+ private final System2 system2;
+ private final Settings settings;
+ private final DbClient dbClient;
+ private final UserIndexer userIndexer;
+ private final long minAgeInMs;
+ private final long loopLimit;
+
+ public RecoveryIndexer(System2 system2, Settings settings, DbClient dbClient, UserIndexer userIndexer) {
+ this.system2 = system2;
+ this.settings = settings;
+ this.dbClient = dbClient;
+ this.userIndexer = userIndexer;
+ this.minAgeInMs = getSetting(PROPERTY_MIN_AGE, DEFAULT_MIN_AGE_IN_MS);
+ this.loopLimit = getSetting(PROPERTY_LOOP_LIMIT, DEFAULT_LOOP_LIMIT);
+ }
+
+ @Override
+ public void start() {
+ long delayInMs = getSetting(PROPERTY_DELAY, DEFAULT_DELAY_IN_MS);
+
+ // in the cluster mode, avoid (but not prevent!) simultaneous executions of recovery
+ // indexers so that a document is not handled multiple times.
+ long initialDelayInMs = getSetting(PROPERTY_INITIAL_DELAY, RandomUtils.nextInt(1 + (int) (delayInMs / 2)));
+
+ executorService.scheduleAtFixedRate(
+ this::recover,
+ initialDelayInMs,
+ delayInMs,
+ TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void stop() {
+ try {
+ executorService.shutdown();
+ executorService.awaitTermination(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ LOGGER.error(LOG_PREFIX + "Unable to stop recovery indexer in timely fashion", e);
+ executorService.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ void recover() {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ Profiler profiler = Profiler.create(LOGGER).start();
+ long beforeDate = system2.now() - minAgeInMs;
+ long total = 0L;
+ long totalSuccess = 0L;
+
+ Collection<EsQueueDto> items = dbClient.esQueueDao().selectForRecovery(dbSession, beforeDate, loopLimit);
+ while (!items.isEmpty()) {
+ total += items.size();
+ long loopSuccess = 0L;
+
+ ListMultimap<EsQueueDto.Type, EsQueueDto> itemsByType = groupItemsByType(items);
+ for (Map.Entry<EsQueueDto.Type, Collection<EsQueueDto>> entry : itemsByType.asMap().entrySet()) {
+ loopSuccess += doIndex(dbSession, entry.getKey(), entry.getValue());
+ }
+
+ totalSuccess += loopSuccess;
+ if (1.0d * (items.size() - loopSuccess) / items.size() >= CIRCUIT_BREAKER_IN_PERCENT) {
+ LOGGER.error(LOG_PREFIX + "too many failures [{}/{} documents], waiting for next run", items.size() - loopSuccess, items.size());
+ break;
+ }
+ items = dbClient.esQueueDao().selectForRecovery(dbSession, beforeDate, loopLimit);
+ }
+ if (total > 0L) {
+ profiler.stopInfo(LOG_PREFIX + format("%d documents processed [%d failures]", total, total - totalSuccess));
+ }
+ } catch (Throwable t) {
+ LOGGER.error(LOG_PREFIX + "fail to recover documents", t);
+ }
+ }
+
+ private long doIndex(DbSession dbSession, EsQueueDto.Type type, Collection<EsQueueDto> typeItems) {
+ LOGGER.trace(LOG_PREFIX + "processing {} {}", typeItems.size(), type);
+ switch (type) {
+ case USER:
+ return userIndexer.index(dbSession, typeItems);
+ default:
+ LOGGER.error(LOG_PREFIX + "ignore {} documents with unsupported type {}", typeItems.size(), type);
+ return 0;
+ }
+ }
+
+ private static ListMultimap<EsQueueDto.Type, EsQueueDto> groupItemsByType(Collection<EsQueueDto> items) {
+ return items.stream().collect(MoreCollectors.index(EsQueueDto::getDocType));
+ }
+
+ private long getSetting(String key, long defaultValue) {
+ long val = settings.getLong(key);
+ if (val <= 0) {
+ val = defaultValue;
+ }
+ LOGGER.debug(LOG_PREFIX + "{}={}", key, val);
+ return val;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/StartupIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/es/StartupIndexer.java
index d007e402713..a1fef5fabf6 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/es/StartupIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/es/StartupIndexer.java
@@ -28,9 +28,9 @@ public interface StartupIndexer {
/**
* This reindexing method will only be called on startup, and only,
- * if there is at least one empty types.
+ * if there is at least one uninitialized type.
*/
- void indexOnStartup(Set<IndexType> emptyIndexTypes);
+ void indexOnStartup(Set<IndexType> uninitializedIndexTypes);
Set<IndexType> getIndexTypes();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/queue/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/es/queue/package-info.java
new file mode 100644
index 00000000000..89f951738d6
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/es/queue/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.es.queue;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexer.java
index 4eaa6523a30..5dbf88356bb 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexer.java
@@ -69,7 +69,7 @@ public class IssueIndexer implements ProjectIndexer, NeedAuthorizationIndexer, S
}
@Override
- public void indexOnStartup(Set<IndexType> emptyIndexTypes) {
+ public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
doIndex(createBulkIndexer(Size.LARGE), (String) null);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexer.java
index e7851052f49..84d52030769 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexer.java
@@ -59,7 +59,7 @@ public class ProjectMeasuresIndexer implements ProjectIndexer, NeedAuthorization
}
@Override
- public void indexOnStartup(Set<IndexType> emptyIndexTypes) {
+ public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
doIndex(createBulkIndexer(Size.LARGE), (String) null);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationCreationImpl.java b/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationCreationImpl.java
index 25a4939746b..010d6dfafad 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationCreationImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationCreationImpl.java
@@ -106,11 +106,10 @@ public class OrganizationCreationImpl implements OrganizationCreation {
addCurrentUserToGroup(dbSession, ownerGroup, userCreator.getId());
addCurrentUserToGroup(dbSession, defaultGroup, userCreator.getId());
- dbSession.commit();
batchDbSession.commit();
// Elasticsearch is updated when DB session is committed
- userIndexer.index(userCreator.getLogin());
+ userIndexer.commitAndIndex(dbSession, userCreator);
return organization;
}
@@ -144,11 +143,10 @@ public class OrganizationCreationImpl implements OrganizationCreation {
insertQualityProfiles(dbSession, batchDbSession, organization);
addCurrentUserToGroup(dbSession, defaultGroup, newUser.getId());
- dbSession.commit();
batchDbSession.commit();
// Elasticsearch is updated when DB session is committed
- userIndexer.index(newUser.getLogin());
+ userIndexer.commitAndIndex(dbSession, newUser);
return Optional.of(organization);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/AddMemberAction.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/AddMemberAction.java
index 7c6c1242ded..535ace66812 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/AddMemberAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/AddMemberAction.java
@@ -118,8 +118,7 @@ public class AddMemberAction implements OrganizationsWsAction {
.setUserId(user.getId()));
dbClient.userGroupDao().insert(dbSession,
new UserGroupDto().setGroupId(defaultGroupFinder.findDefaultGroup(dbSession, organization.getUuid()).getId()).setUserId(user.getId()));
- dbSession.commit();
- userIndexer.index(user.getLogin());
+ userIndexer.commitAndIndex(dbSession, user);
}
private AddMemberWsResponse buildResponse(UserDto user, int groups) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java
index bd99338c81f..f3c4f596446 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.organization.ws;
+import java.util.Collection;
import java.util.List;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
@@ -138,11 +139,10 @@ public class DeleteAction implements OrganizationsWsAction {
}
private void deleteOrganization(DbSession dbSession, OrganizationDto organization) {
- List<String> logins = dbClient.organizationMemberDao().selectLoginsByOrganizationUuid(dbSession, organization.getUuid());
+ Collection<String> logins = dbClient.organizationMemberDao().selectLoginsByOrganizationUuid(dbSession, organization.getUuid());
dbClient.organizationMemberDao().deleteByOrganizationUuid(dbSession, organization.getUuid());
dbClient.organizationDao().deleteByUuid(dbSession, organization.getUuid());
- dbSession.commit();
- userIndexer.index(logins);
+ userIndexer.commitAndIndexByLogins(dbSession, logins);
}
private static void preventDeletionOfDefaultOrganization(String key, DefaultOrganization defaultOrganization) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/RemoveMemberAction.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/RemoveMemberAction.java
index 5c81f8b17bb..a927bdfcbd5 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/RemoveMemberAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/RemoveMemberAction.java
@@ -103,8 +103,7 @@ public class RemoveMemberAction implements OrganizationsWsAction {
dbClient.propertiesDao().deleteByOrganizationAndMatchingLogin(dbSession, organizationUuid, user.getLogin(), singletonList(DEFAULT_ISSUE_ASSIGNEE));
dbClient.organizationMemberDao().delete(dbSession, organizationUuid, userId);
- dbSession.commit();
- userIndexer.index(user.getLogin());
+ userIndexer.commitAndIndex(dbSession, user);
}
private void ensureLastAdminIsNotRemoved(DbSession dbSession, OrganizationDto organizationDto, UserDto user) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/index/PermissionIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/permission/index/PermissionIndexer.java
index 41dd5b64ba4..8c7ad3e6693 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/permission/index/PermissionIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/permission/index/PermissionIndexer.java
@@ -80,9 +80,9 @@ public class PermissionIndexer implements ProjectIndexer, StartupIndexer {
}
@Override
- public void indexOnStartup(Set<IndexType> emptyIndexTypes) {
+ public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
List<Dto> authorizations = getAllAuthorizations();
- Stream<AuthorizationScope> scopes = getScopes(emptyIndexTypes);
+ Stream<AuthorizationScope> scopes = getScopes(uninitializedIndexTypes);
index(authorizations, scopes, Size.LARGE);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/index/PermissionIndexerDao.java b/server/sonar-server/src/main/java/org/sonar/server/permission/index/PermissionIndexerDao.java
index 81c98dc39c8..55d575a8269 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/permission/index/PermissionIndexerDao.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/permission/index/PermissionIndexerDao.java
@@ -32,8 +32,8 @@ import org.apache.commons.lang.StringUtils;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
+import static org.apache.commons.lang.StringUtils.repeat;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;
-import static org.sonar.db.DatabaseUtils.repeatCondition;
/**
* No streaming because of union of joins -> no need to use ResultSetIterator
@@ -200,7 +200,7 @@ public class PermissionIndexerDao {
if (projectUuids.isEmpty()) {
sql = StringUtils.replace(SQL_TEMPLATE, "{projectsCondition}", "");
} else {
- sql = StringUtils.replace(SQL_TEMPLATE, "{projectsCondition}", " AND (" + repeatCondition("projects.uuid = ?", projectUuids.size(), "OR") + ")");
+ sql = StringUtils.replace(SQL_TEMPLATE, "{projectsCondition}", " AND (" + repeat("projects.uuid = ?", " or ", projectUuids.size()) + ")");
}
PreparedStatement stmt = dbClient.getMyBatis().newScrollingSelectStatement(session, sql);
int index = 1;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index ac0aa79be26..434eab1d84e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -53,6 +53,7 @@ import org.sonar.server.duplication.ws.ShowResponseBuilder;
import org.sonar.server.email.ws.EmailsWsModule;
import org.sonar.server.es.IndexCreator;
import org.sonar.server.es.IndexDefinitions;
+import org.sonar.server.es.RecoveryIndexer;
import org.sonar.server.event.NewAlerts;
import org.sonar.server.favorite.FavoriteModule;
import org.sonar.server.issue.AddTagsAction;
@@ -520,7 +521,9 @@ public class PlatformLevel4 extends PlatformLevel {
WebhooksWsModule.class,
// Http Request ID
- HttpRequestIdModule.class);
+ HttpRequestIdModule.class,
+
+ RecoveryIndexer.class);
addAll(level4AddedComponents);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/property/InternalProperties.java b/server/sonar-server/src/main/java/org/sonar/server/property/InternalProperties.java
index 1d84ad9441a..42925dbb097 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/property/InternalProperties.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/property/InternalProperties.java
@@ -34,6 +34,8 @@ public interface InternalProperties {
String ORGANIZATION_ENABLED = "organization.enabled";
+ String ES_INDEX_INITIALIZING_PREFIX = "es.initializing.";
+
/**
* Read the value of the specified property.
*
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexer.java
index 1cd6a8f8c63..f61b6089a68 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexer.java
@@ -58,7 +58,7 @@ public class ActiveRuleIndexer implements StartupIndexer {
}
@Override
- public void indexOnStartup(Set<IndexType> emptyIndexTypes) {
+ public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
try (DbSession dbSession = dbClient.openSession(false)) {
ActiveRuleIterator dbCursor = activeRuleIteratorFactory.createForAll(dbSession);
scrollDbAndIndex(dbCursor, Size.LARGE);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexer.java
index 4e0eccebfcb..30919221b0a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/test/index/TestIndexer.java
@@ -77,7 +77,7 @@ public class TestIndexer implements ProjectIndexer, StartupIndexer {
}
@Override
- public void indexOnStartup(Set<IndexType> emptyIndexTypes) {
+ public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
doIndex(null, Size.LARGE);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java
index b92fae91e36..b29394d3845 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java
@@ -22,16 +22,17 @@ package org.sonar.server.user;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import java.security.SecureRandom;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Random;
+import java.util.function.Consumer;
import javax.annotation.Nullable;
import org.apache.commons.codec.digest.DigestUtils;
import org.sonar.api.config.Configuration;
import org.sonar.api.platform.NewUserHandler;
import org.sonar.api.server.ServerSide;
-import org.sonar.api.utils.System2;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@@ -73,19 +74,17 @@ public class UserUpdater {
private final NewUserNotifier newUserNotifier;
private final DbClient dbClient;
private final UserIndexer userIndexer;
- private final System2 system2;
private final OrganizationFlags organizationFlags;
private final DefaultOrganizationProvider defaultOrganizationProvider;
private final OrganizationCreation organizationCreation;
private final DefaultGroupFinder defaultGroupFinder;
private final Configuration config;
- public UserUpdater(NewUserNotifier newUserNotifier, DbClient dbClient, UserIndexer userIndexer, System2 system2, OrganizationFlags organizationFlags,
+ public UserUpdater(NewUserNotifier newUserNotifier, DbClient dbClient, UserIndexer userIndexer, OrganizationFlags organizationFlags,
DefaultOrganizationProvider defaultOrganizationProvider, OrganizationCreation organizationCreation, DefaultGroupFinder defaultGroupFinder, Configuration config) {
this.newUserNotifier = newUserNotifier;
this.dbClient = dbClient;
this.userIndexer = userIndexer;
- this.system2 = system2;
this.organizationFlags = organizationFlags;
this.defaultOrganizationProvider = defaultOrganizationProvider;
this.organizationCreation = organizationCreation;
@@ -93,14 +92,17 @@ public class UserUpdater {
this.config = config;
}
- public UserDto create(DbSession dbSession, NewUser newUser) {
+ public UserDto createAndCommit(DbSession dbSession, NewUser newUser, Consumer<UserDto> beforeCommit) {
String login = newUser.login();
UserDto userDto = dbClient.userDao().selectByLogin(dbSession, newUser.login());
if (userDto == null) {
- userDto = saveUser(dbSession, createNewUserDto(dbSession, newUser));
+ userDto = saveUser(dbSession, createDto(dbSession, newUser));
} else {
reactivateUser(dbSession, userDto, login, newUser);
}
+ beforeCommit.accept(userDto);
+ userIndexer.commitAndIndex(dbSession, userDto);
+
notifyNewUser(userDto.getLogin(), userDto.getName(), newUser.email());
return userDto;
}
@@ -120,26 +122,31 @@ public class UserUpdater {
// Hack to allow to change the password of the user
existingUser.setLocal(true);
setOnboarded(existingUser);
- updateUserDto(dbSession, updateUser, existingUser);
+ updateDto(dbSession, updateUser, existingUser);
updateUser(dbSession, existingUser);
addUserToDefaultOrganizationAndDefaultGroup(dbSession, existingUser);
- dbSession.commit();
}
- public void update(DbSession dbSession, UpdateUser updateUser) {
- UserDto user = dbClient.userDao().selectByLogin(dbSession, updateUser.login());
- checkFound(user, "User with login '%s' has not been found", updateUser.login());
- boolean isUserUpdated = updateUserDto(dbSession, updateUser, user);
- if (!isUserUpdated) {
- return;
+ public void updateAndCommit(DbSession dbSession, UpdateUser updateUser, Consumer<UserDto> beforeCommit) {
+ UserDto dto = dbClient.userDao().selectByLogin(dbSession, updateUser.login());
+ checkFound(dto, "User with login '%s' has not been found", updateUser.login());
+ boolean isUserUpdated = updateDto(dbSession, updateUser, dto);
+ if (isUserUpdated) {
+ // at least one change. Database must be updated and Elasticsearch re-indexed
+ updateUser(dbSession, dto);
+ beforeCommit.accept(dto);
+ userIndexer.commitAndIndex(dbSession, dto);
+ notifyNewUser(dto.getLogin(), dto.getName(), dto.getEmail());
+ } else {
+ // no changes but still execute the consumer
+ beforeCommit.accept(dto);
+ dbSession.commit();
}
- updateUser(dbSession, user);
- notifyNewUser(user.getLogin(), user.getName(), user.getEmail());
}
- private UserDto createNewUserDto(DbSession dbSession, NewUser newUser) {
+ private UserDto createDto(DbSession dbSession, NewUser newUser) {
UserDto userDto = new UserDto();
- List<String> messages = newArrayList();
+ List<String> messages = new ArrayList<>();
String login = newUser.login();
if (validateLoginFormat(login, messages)) {
@@ -158,7 +165,7 @@ public class UserUpdater {
String password = newUser.password();
if (password != null && validatePasswords(password, messages)) {
- setEncryptedPassWord(password, userDto);
+ setEncryptedPassword(password, userDto);
}
List<String> scmAccounts = sanitizeScmAccounts(newUser.scmAccounts());
@@ -173,13 +180,13 @@ public class UserUpdater {
return userDto;
}
- private boolean updateUserDto(DbSession dbSession, UpdateUser updateUser, UserDto userDto) {
+ private boolean updateDto(DbSession dbSession, UpdateUser update, UserDto dto) {
List<String> messages = newArrayList();
- boolean changed = updateName(updateUser, userDto, messages);
- changed |= updateEmail(updateUser, userDto, messages);
- changed |= updateExternalIdentity(updateUser, userDto);
- changed |= updatePassword(updateUser, userDto, messages);
- changed |= updateScmAccounts(dbSession, updateUser, userDto, messages);
+ boolean changed = updateName(update, dto, messages);
+ changed |= updateEmail(update, dto, messages);
+ changed |= updateExternalIdentity(update, dto);
+ changed |= updatePassword(update, dto, messages);
+ changed |= updateScmAccounts(dbSession, update, dto, messages);
checkRequest(messages.isEmpty(), messages);
return changed;
}
@@ -216,7 +223,7 @@ public class UserUpdater {
private static boolean updatePassword(UpdateUser updateUser, UserDto userDto, List<String> messages) {
String password = updateUser.password();
if (!updateUser.isExternalIdentityChanged() && updateUser.isPasswordChanged() && validatePasswords(password, messages) && checkPasswordChangeAllowed(userDto, messages)) {
- setEncryptedPassWord(password, userDto);
+ setEncryptedPassword(password, userDto);
return true;
}
return false;
@@ -355,25 +362,19 @@ public class UserUpdater {
}
private UserDto saveUser(DbSession dbSession, UserDto userDto) {
- long now = system2.now();
- userDto.setActive(true).setCreatedAt(now).setUpdatedAt(now);
+ userDto.setActive(true);
UserDto res = dbClient.userDao().insert(dbSession, userDto);
addUserToDefaultOrganizationAndDefaultGroup(dbSession, userDto);
organizationCreation.createForUser(dbSession, userDto);
- dbSession.commit();
- userIndexer.index(userDto.getLogin());
return res;
}
- private void updateUser(DbSession dbSession, UserDto userDto) {
- long now = system2.now();
- userDto.setActive(true).setUpdatedAt(now);
- dbClient.userDao().update(dbSession, userDto);
- dbSession.commit();
- userIndexer.index(userDto.getLogin());
+ private void updateUser(DbSession dbSession, UserDto dto) {
+ dto.setActive(true);
+ dbClient.userDao().update(dbSession, dto);
}
- private static void setEncryptedPassWord(String password, UserDto userDto) {
+ private static void setEncryptedPassword(String password, UserDto userDto) {
Random random = new SecureRandom();
byte[] salt = new byte[32];
random.nextBytes(salt);
@@ -382,7 +383,7 @@ public class UserUpdater {
userDto.setCryptedPassword(encryptPassword(password, saltHex));
}
- private void notifyNewUser(String login, String name, String email) {
+ private void notifyNewUser(String login, String name, @Nullable String email) {
newUserNotifier.onNewUser(NewUserHandler.Context.builder()
.setLogin(login)
.setName(name)
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/index/UserDoc.java b/server/sonar-server/src/main/java/org/sonar/server/user/index/UserDoc.java
index 1b7591d0f55..153619e2d3f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/index/UserDoc.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/index/UserDoc.java
@@ -20,7 +20,6 @@
package org.sonar.server.user.index;
import com.google.common.collect.Maps;
-import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
@@ -83,14 +82,6 @@ public class UserDoc extends BaseDoc implements User {
return getField(FIELD_ORGANIZATION_UUIDS);
}
- public long createdAt() {
- return getFieldAsDate(UserIndexDefinition.FIELD_CREATED_AT).getTime();
- }
-
- public long updatedAt() {
- return getFieldAsDate(UserIndexDefinition.FIELD_UPDATED_AT).getTime();
- }
-
public UserDoc setLogin(@Nullable String s) {
setField(UserIndexDefinition.FIELD_LOGIN, s);
return this;
@@ -120,14 +111,4 @@ public class UserDoc extends BaseDoc implements User {
setField(FIELD_ORGANIZATION_UUIDS, organizationUuids);
return this;
}
-
- public UserDoc setCreatedAt(long l) {
- setField(UserIndexDefinition.FIELD_CREATED_AT, new Date(l));
- return this;
- }
-
- public UserDoc setUpdatedAt(long l) {
- setField(UserIndexDefinition.FIELD_UPDATED_AT, new Date(l));
- return this;
- }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexDefinition.java b/server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexDefinition.java
index d64f2fd8145..4178d607ead 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexDefinition.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexDefinition.java
@@ -36,8 +36,6 @@ public class UserIndexDefinition implements IndexDefinition {
public static final String FIELD_LOGIN = "login";
public static final String FIELD_NAME = "name";
public static final String FIELD_EMAIL = "email";
- public static final String FIELD_CREATED_AT = "createdAt";
- public static final String FIELD_UPDATED_AT = "updatedAt";
public static final String FIELD_ACTIVE = "active";
public static final String FIELD_SCM_ACCOUNTS = "scmAccounts";
public static final String FIELD_ORGANIZATION_UUIDS = "organizationUuids";
@@ -59,8 +57,6 @@ public class UserIndexDefinition implements IndexDefinition {
mapping.stringFieldBuilder(FIELD_LOGIN).addSubFields(USER_SEARCH_GRAMS_ANALYZER).build();
mapping.stringFieldBuilder(FIELD_NAME).addSubFields(USER_SEARCH_GRAMS_ANALYZER).build();
mapping.stringFieldBuilder(FIELD_EMAIL).addSubFields(USER_SEARCH_GRAMS_ANALYZER, SORTABLE_ANALYZER).build();
- mapping.createDateTimeField(FIELD_CREATED_AT);
- mapping.createDateTimeField(FIELD_UPDATED_AT);
mapping.createBooleanField(FIELD_ACTIVE);
mapping.stringFieldBuilder(FIELD_SCM_ACCOUNTS).disableNorms().addSubFields(SORTABLE_ANALYZER).build();
mapping.stringFieldBuilder(FIELD_ORGANIZATION_UUIDS).disableNorms().build();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexer.java
index 65241119240..6f99292e7cb 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndexer.java
@@ -19,14 +19,20 @@
*/
package org.sonar.server.user.index;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableSet;
-import java.util.Iterator;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Maps;
+import java.util.Collection;
import java.util.List;
import java.util.Set;
-import javax.annotation.Nullable;
import org.elasticsearch.action.index.IndexRequest;
+import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
+import org.sonar.db.es.EsQueueDto;
+import org.sonar.db.user.UserDto;
import org.sonar.server.es.BulkIndexer;
import org.sonar.server.es.BulkIndexer.Size;
import org.sonar.server.es.EsClient;
@@ -35,7 +41,7 @@ import org.sonar.server.es.StartupIndexer;
import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
-import static org.sonar.db.DatabaseUtils.executeLargeInputsWithoutOutput;
+import static org.sonar.core.util.stream.MoreCollectors.toHashSet;
import static org.sonar.server.user.index.UserIndexDefinition.INDEX_TYPE_USER;
public class UserIndexer implements StartupIndexer {
@@ -54,56 +60,98 @@ public class UserIndexer implements StartupIndexer {
}
@Override
- public void indexOnStartup(Set<IndexType> emptyIndexTypes) {
- doIndex(newBulkIndexer(Size.LARGE), null);
+ public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ ListMultimap<String, String> organizationUuidsByLogin = ArrayListMultimap.create();
+ dbClient.organizationMemberDao().selectAllForUserIndexing(dbSession, organizationUuidsByLogin::put);
+
+ BulkIndexer bulkIndexer = newBulkIndexer(Size.LARGE);
+ bulkIndexer.start();
+ dbClient.userDao().scrollAll(dbSession,
+ // only index requests, no deletion requests.
+ // Deactivated users are not deleted but updated.
+ u -> bulkIndexer.add(newIndexRequest(u, organizationUuidsByLogin)));
+ bulkIndexer.stop();
+ }
}
- public void index(String login) {
- requireNonNull(login);
- doIndex(newBulkIndexer(Size.REGULAR), singletonList(login));
+ public void commitAndIndex(DbSession dbSession, UserDto user) {
+ commitAndIndexByLogins(dbSession, singletonList(user.getLogin()));
}
- public void index(List<String> logins) {
- requireNonNull(logins);
- if (logins.isEmpty()) {
- return;
- }
-
- doIndex(newBulkIndexer(Size.REGULAR), logins);
+ public void commitAndIndex(DbSession dbSession, Collection<UserDto> users) {
+ commitAndIndexByLogins(dbSession, Collections2.transform(users, UserDto::getLogin));
}
- private void doIndex(BulkIndexer bulk, @Nullable List<String> logins) {
- try (DbSession dbSession = dbClient.openSession(false)) {
- if (logins == null) {
- processLogins(bulk, dbSession, null);
- } else {
- executeLargeInputsWithoutOutput(logins, l -> processLogins(bulk, dbSession, l));
- }
- }
+ public void commitAndIndexByLogins(DbSession dbSession, Collection<String> logins) {
+ List<EsQueueDto> items = logins.stream()
+ .map(l -> EsQueueDto.create(EsQueueDto.Type.USER, l))
+ .collect(MoreCollectors.toArrayList());
+
+ dbClient.esQueueDao().insert(dbSession, items);
+ dbSession.commit();
+ postCommit(dbSession, logins, items);
}
- private void processLogins(BulkIndexer bulk, DbSession dbSession, @Nullable List<String> logins) {
- try (UserResultSetIterator rowIt = UserResultSetIterator.create(dbClient, dbSession, logins)) {
- processResultSet(bulk, rowIt);
- }
+ /**
+ * Entry point for Byteman tests. See directory tests/resilience.
+ * The parameter "logins" is used only by the Byteman script.
+ */
+ private void postCommit(DbSession dbSession, Collection<String> logins, Collection<EsQueueDto> items) {
+ index(dbSession, items);
}
- private static void processResultSet(BulkIndexer bulk, Iterator<UserDoc> users) {
- bulk.start();
- while (users.hasNext()) {
- UserDoc user = users.next();
- bulk.add(newIndexRequest(user));
+ /**
+ * @return the number of items that have been successfully indexed
+ */
+ public long index(DbSession dbSession, Collection<EsQueueDto> items) {
+ if (items.isEmpty()) {
+ return 0L;
}
- bulk.stop();
+ Set<String> logins = items
+ .stream()
+ .filter(i -> {
+ requireNonNull(i.getDocUuid(), () -> "BUG - " + i + " has not been persisted before indexing");
+ return true;
+ })
+ .map(EsQueueDto::getDocUuid)
+ .collect(toHashSet(items.size()));
+
+ ListMultimap<String, String> organizationUuidsByLogin = ArrayListMultimap.create();
+ dbClient.organizationMemberDao().selectForUserIndexing(dbSession, logins, organizationUuidsByLogin::put);
+
+ BulkIndexer bulkIndexer = newBulkIndexer(Size.REGULAR);
+ bulkIndexer.start(dbSession, dbClient, items);
+ dbClient.userDao().scrollByLogins(dbSession, logins,
+ // only index requests, no deletion requests.
+ // Deactivated users are not deleted but updated.
+ u -> {
+ logins.remove(u.getLogin());
+ bulkIndexer.add(newIndexRequest(u, organizationUuidsByLogin));
+ });
+
+ // the remaining logins reference rows that don't exist in db. They must
+ // be deleted from index.
+ logins.forEach(l -> bulkIndexer.addDeletion(UserIndexDefinition.INDEX_TYPE_USER, l));
+ return bulkIndexer.stop();
}
private BulkIndexer newBulkIndexer(Size bulkSize) {
return new BulkIndexer(esClient, UserIndexDefinition.INDEX_TYPE_USER.getIndex(), bulkSize);
}
- private static IndexRequest newIndexRequest(UserDoc user) {
- return new IndexRequest(UserIndexDefinition.INDEX_TYPE_USER.getIndex(), UserIndexDefinition.INDEX_TYPE_USER.getType(), user.login())
- .source(user.getFields());
+ private static IndexRequest newIndexRequest(UserDto user, ListMultimap<String, String> organizationUuidsByLogins) {
+ UserDoc doc = new UserDoc(Maps.newHashMapWithExpectedSize(8));
+ // all the keys must be present, even if value is null
+ doc.setLogin(user.getLogin());
+ doc.setName(user.getName());
+ doc.setEmail(user.getEmail());
+ doc.setActive(user.isActive());
+ doc.setScmAccounts(UserDto.decodeScmAccounts(user.getScmAccounts()));
+ doc.setOrganizationUuids(organizationUuidsByLogins.get(user.getLogin()));
+
+ return new IndexRequest(UserIndexDefinition.INDEX_TYPE_USER.getIndex(), UserIndexDefinition.INDEX_TYPE_USER.getType())
+ .id(doc.getId())
+ .source(doc.getFields());
}
-
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/index/UserResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/user/index/UserResultSetIterator.java
index dc1a707310d..3bdf905f729 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/index/UserResultSetIterator.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/index/UserResultSetIterator.java
@@ -19,22 +19,25 @@
*/
package org.sonar.server.user.index;
-import com.google.common.base.Joiner;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.Collection;
import java.util.List;
-import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.ResultSetIterator;
+import org.sonar.db.es.EsQueueDto;
import org.sonar.db.user.UserDto;
+import static org.apache.commons.lang.StringUtils.repeat;
+import static org.sonar.core.util.stream.MoreCollectors.toArrayList;
+
/**
* Scrolls over table USERS and reads documents to populate the user index
*/
@@ -52,8 +55,6 @@ class UserResultSetIterator extends ResultSetIterator<UserDoc> {
};
private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from users u ";
- private static final String LOGIN_FILTER = "u.login=?";
- private static final Joiner OR_JOINER = Joiner.on(" or ");
private final ListMultimap<String, String> organizationUuidsByLogins;
@@ -62,16 +63,25 @@ class UserResultSetIterator extends ResultSetIterator<UserDoc> {
this.organizationUuidsByLogins = organizationUuidsByLogins;
}
- static UserResultSetIterator create(DbClient dbClient, DbSession session, @Nullable List<String> logins) {
+ static UserResultSetIterator create(DbClient dbClient, DbSession session, @Nullable Collection<EsQueueDto> esQueueDtos) {
try {
- String sql = createSql(logins);
+ String sql = SQL_ALL;
+ List<String> logins = null;
+ if (esQueueDtos != null) {
+ logins = esQueueDtos.stream()
+ .filter(i -> i.getDocType() == EsQueueDto.Type.USER)
+ .map(EsQueueDto::getDocUuid).collect(toArrayList());
+ sql += "where (" + repeat("u.login=?", " or ", logins.size()) + ")";
+ }
+
PreparedStatement stmt = dbClient.getMyBatis().newScrollingSelectStatement(session, sql);
setParameters(stmt, logins);
ListMultimap<String, String> organizationUuidsByLogin = ArrayListMultimap.create();
- if (logins == null) {
+ if (esQueueDtos == null) {
dbClient.organizationMemberDao().selectAllForUserIndexing(session, organizationUuidsByLogin::put);
} else {
+
dbClient.organizationMemberDao().selectForUserIndexing(session, logins, organizationUuidsByLogin::put);
}
@@ -81,35 +91,21 @@ class UserResultSetIterator extends ResultSetIterator<UserDoc> {
}
}
- private static String createSql(@Nullable List<String> logins) {
- if (logins == null) {
- return SQL_ALL;
- }
-
- List<String> sqlLogins = logins.stream()
- .map(l -> LOGIN_FILTER)
- .collect(Collectors.toList());
-
- String sql = SQL_ALL;
- sql += " WHERE ";
- sql += "(" + OR_JOINER.join(sqlLogins) + ")";
-
- return sql;
- }
-
- private static void setParameters(PreparedStatement stmt, @Nullable List<String> logins) throws SQLException {
+ private static void setParameters(PreparedStatement stmt, @Nullable Collection<String> logins) throws SQLException {
if (logins == null) {
return;
}
- for (int i = 0; i < logins.size(); i++) {
- stmt.setString(i + 1, logins.get(i));
+ int paramIndex = 1;
+ for (String login : logins) {
+ stmt.setString(paramIndex, login);
+ paramIndex++;
}
}
@Override
protected UserDoc read(ResultSet rs) throws SQLException {
- UserDoc doc = new UserDoc(Maps.newHashMapWithExpectedSize(8));
+ UserDoc doc = new UserDoc(Maps.newHashMapWithExpectedSize(6));
String login = rs.getString(1);
@@ -119,10 +115,7 @@ class UserResultSetIterator extends ResultSetIterator<UserDoc> {
doc.setEmail(rs.getString(3));
doc.setActive(rs.getBoolean(4));
doc.setScmAccounts(UserDto.decodeScmAccounts(rs.getString(5)));
- doc.setCreatedAt(rs.getLong(6));
- doc.setUpdatedAt(rs.getLong(7));
doc.setOrganizationUuids(organizationUuidsByLogins.get(login));
return doc;
}
-
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java
index bd973387892..5faca4c8879 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java
@@ -90,7 +90,7 @@ public class ChangePasswordAction implements UsersWsAction {
String password = request.mandatoryParam(PARAM_PASSWORD);
UpdateUser updateUser = UpdateUser.create(login).setPassword(password);
- userUpdater.update(dbSession, updateUser);
+ userUpdater.updateAndCommit(dbSession, updateUser, u -> {});
}
response.noContent();
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/CreateAction.java
index d0d43c426e7..7feb6dc48d9 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/ws/CreateAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/ws/CreateAction.java
@@ -127,9 +127,8 @@ public class CreateAction implements UsersWsAction {
if (!request.isLocal()) {
newUser.setExternalIdentity(new ExternalIdentity(SQ_AUTHORITY, request.getLogin()));
}
- UserDto userDto = userUpdater.create(dbSession, newUser.build());
- dbSession.commit();
- return buildResponse(userDto);
+ UserDto createdUser = userUpdater.createAndCommit(dbSession, newUser.build(), u -> {});
+ return buildResponse(createdUser);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/DeactivateAction.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/DeactivateAction.java
index 1419e4abebc..94ec4c6926a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/ws/DeactivateAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/ws/DeactivateAction.java
@@ -101,11 +101,10 @@ public class DeactivateAction implements UsersWsAction {
dbClient.userPermissionDao().deleteByUserId(dbSession, userId);
dbClient.permissionTemplateDao().deleteUserPermissionsByUserId(dbSession, userId);
dbClient.organizationMemberDao().deleteByUserId(dbSession, userId);
- dbClient.userDao().deactivateUserById(dbSession, userId);
- dbSession.commit();
+ dbClient.userDao().deactivateUser(dbSession, user);
+ userIndexer.commitAndIndex(dbSession, user);
}
- userIndexer.index(login);
writeResponse(response, login);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/SkipOnboardingTutorialAction.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/SkipOnboardingTutorialAction.java
index 003208121d2..2caddc3379e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/ws/SkipOnboardingTutorialAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/ws/SkipOnboardingTutorialAction.java
@@ -35,12 +35,10 @@ public class SkipOnboardingTutorialAction implements UsersWsAction {
private final UserSession userSession;
private final DbClient dbClient;
- private final System2 system2;
- public SkipOnboardingTutorialAction(UserSession userSession, DbClient dbClient, System2 system2) {
+ public SkipOnboardingTutorialAction(UserSession userSession, DbClient dbClient) {
this.userSession = userSession;
this.dbClient = dbClient;
- this.system2 = system2;
}
@Override
@@ -63,7 +61,8 @@ public class SkipOnboardingTutorialAction implements UsersWsAction {
checkState(userDto != null, "User login '%s' cannot be found", userLogin);
if (!userDto.isOnboarded()) {
userDto.setOnboarded(true);
- userDto.setUpdatedAt(system2.now());
+ // no need to update Elasticsearch, the field onBoarded
+ // is not indexed
dbClient.userDao().update(dbSession, userDto);
dbSession.commit();
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/UpdateAction.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/UpdateAction.java
index b9292bd4f73..dc50009b16a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/ws/UpdateAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/ws/UpdateAction.java
@@ -118,7 +118,7 @@ public class UpdateAction implements UsersWsAction {
if (!request.getScmAccounts().isEmpty()) {
updateUser.setScmAccounts(request.getScmAccounts());
}
- userUpdater.update(dbSession, updateUser);
+ userUpdater.updateAndCommit(dbSession, updateUser, u -> {});
}
private void writeUser(DbSession dbSession, Response response, String login) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/view/index/ViewIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/view/index/ViewIndexer.java
index 14b5ee7d892..2c92637976c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/view/index/ViewIndexer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/view/index/ViewIndexer.java
@@ -53,7 +53,7 @@ public class ViewIndexer implements StartupIndexer {
}
@Override
- public void indexOnStartup(Set<IndexType> emptyIndexTypes) {
+ public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
try (DbSession dbSession = dbClient.openSession(false)) {
Map<String, String> viewAndProjectViewUuidMap = newHashMap();
for (UuidWithProjectUuidDto uuidWithProjectUuidDto : dbClient.componentDao().selectAllViewsAndSubViews(dbSession)) {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/SsoAuthenticatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/SsoAuthenticatorTest.java
index 5875442d41d..6094bac37d4 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/authentication/SsoAuthenticatorTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/SsoAuthenticatorTest.java
@@ -40,12 +40,14 @@ import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.authentication.event.AuthenticationEvent.Source;
+import org.sonar.server.es.EsTester;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.organization.OrganizationCreation;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.organization.TestOrganizationFlags;
import org.sonar.server.user.NewUserNotifier;
import org.sonar.server.user.UserUpdater;
+import org.sonar.server.user.index.UserIndexDefinition;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.usergroups.DefaultGroupFinder;
@@ -65,11 +67,14 @@ import static org.sonar.server.authentication.event.AuthenticationExceptionMatch
public class SsoAuthenticatorTest {
+ private MapSettings settings = new MapSettings();
+
@Rule
public ExpectedException expectedException = none();
-
@Rule
public DbTester db = DbTester.create(new AlwaysIncreasingSystem2());
+ @Rule
+ public EsTester es = new EsTester(new UserIndexDefinition(settings.asConfig()));
private static final String DEFAULT_LOGIN = "john";
private static final String DEFAULT_NAME = "John";
@@ -93,14 +98,14 @@ public class SsoAuthenticatorTest {
private GroupDto sonarUsers;
private System2 system2 = mock(System2.class);
- private MapSettings settings = new MapSettings();
private OrganizationCreation organizationCreation = mock(OrganizationCreation.class);
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
+ private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
private UserIdentityAuthenticator userIdentityAuthenticator = new UserIdentityAuthenticator(
db.getDbClient(),
- new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), mock(UserIndexer.class), System2.INSTANCE, organizationFlags, defaultOrganizationProvider, organizationCreation,
+ new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), userIndexer, organizationFlags, defaultOrganizationProvider, organizationCreation,
new DefaultGroupFinder(db.getDbClient()), settings.asConfig()),
defaultOrganizationProvider, organizationFlags, new DefaultGroupFinder(db.getDbClient()));
diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/UserIdentityAuthenticatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/UserIdentityAuthenticatorTest.java
index 40638401375..1371534a98f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/authentication/UserIdentityAuthenticatorTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/UserIdentityAuthenticatorTest.java
@@ -35,12 +35,14 @@ import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.event.AuthenticationEvent.Method;
import org.sonar.server.authentication.event.AuthenticationEvent.Source;
+import org.sonar.server.es.EsTester;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.organization.OrganizationCreation;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.organization.TestOrganizationFlags;
import org.sonar.server.user.NewUserNotifier;
import org.sonar.server.user.UserUpdater;
+import org.sonar.server.user.index.UserIndexDefinition;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.usergroups.DefaultGroupFinder;
@@ -69,32 +71,33 @@ public class UserIdentityAuthenticatorTest {
.setEnabled(true)
.setAllowsUsersToSignUp(true);
+ private MapSettings settings = new MapSettings();
+
@Rule
public ExpectedException thrown = ExpectedException.none();
-
@Rule
public DbTester db = DbTester.create(new AlwaysIncreasingSystem2());
-
+ @Rule
+ public EsTester es = new EsTester(new UserIndexDefinition(settings.asConfig()));
+ private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private OrganizationCreation organizationCreation = mock(OrganizationCreation.class);
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
- private MapSettings settings = new MapSettings();
-
private UserUpdater userUpdater = new UserUpdater(
mock(NewUserNotifier.class),
db.getDbClient(),
- mock(UserIndexer.class),
- System2.INSTANCE,
+ userIndexer,
organizationFlags,
defaultOrganizationProvider,
organizationCreation,
new DefaultGroupFinder(db.getDbClient()),
settings.asConfig());
+
private UserIdentityAuthenticator underTest = new UserIdentityAuthenticator(db.getDbClient(), userUpdater, defaultOrganizationProvider, organizationFlags,
new DefaultGroupFinder(db.getDbClient()));
@Test
- public void authenticate_new_user() throws Exception {
+ public void authenticate_new_user() {
organizationFlags.setEnabled(true);
underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, Source.realm(Method.BASIC, IDENTITY_PROVIDER.getName()));
@@ -111,7 +114,7 @@ public class UserIdentityAuthenticatorTest {
}
@Test
- public void authenticate_new_user_with_groups() throws Exception {
+ public void authenticate_new_user_with_groups() {
organizationFlags.setEnabled(true);
GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
@@ -123,7 +126,7 @@ public class UserIdentityAuthenticatorTest {
}
@Test
- public void authenticate_new_user_and_force_default_group_when_organizations_are_disabled() throws Exception {
+ public void authenticate_new_user_and_force_default_group_when_organizations_are_disabled() {
organizationFlags.setEnabled(false);
UserDto user = db.users().insertUser();
GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
@@ -137,7 +140,7 @@ public class UserIdentityAuthenticatorTest {
}
@Test
- public void does_not_force_default_group_when_authenticating_new_user_if_organizations_are_enabled() throws Exception {
+ public void does_not_force_default_group_when_authenticating_new_user_if_organizations_are_enabled() {
organizationFlags.setEnabled(true);
UserDto user = db.users().insertUser();
GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
@@ -171,7 +174,7 @@ public class UserIdentityAuthenticatorTest {
}
@Test
- public void authenticate_existing_user() throws Exception {
+ public void authenticate_existing_user() {
db.users().insertUser(newUserDto()
.setLogin(USER_LOGIN)
.setActive(true)
@@ -192,7 +195,7 @@ public class UserIdentityAuthenticatorTest {
}
@Test
- public void authenticate_existing_disabled_user() throws Exception {
+ public void authenticate_existing_disabled_user() {
organizationFlags.setEnabled(true);
db.users().insertUser(newUserDto()
.setLogin(USER_LOGIN)
@@ -214,7 +217,7 @@ public class UserIdentityAuthenticatorTest {
}
@Test
- public void authenticate_existing_user_and_add_new_groups() throws Exception {
+ public void authenticate_existing_user_and_add_new_groups() {
organizationFlags.setEnabled(true);
UserDto user = db.users().insertUser(newUserDto()
.setLogin(USER_LOGIN)
@@ -229,7 +232,7 @@ public class UserIdentityAuthenticatorTest {
}
@Test
- public void authenticate_existing_user_and_remove_groups() throws Exception {
+ public void authenticate_existing_user_and_remove_groups() {
organizationFlags.setEnabled(true);
UserDto user = db.users().insertUser(newUserDto()
.setLogin(USER_LOGIN)
@@ -246,7 +249,7 @@ public class UserIdentityAuthenticatorTest {
}
@Test
- public void authenticate_existing_user_and_remove_all_groups_expect_default_when_organizations_are_disabled() throws Exception {
+ public void authenticate_existing_user_and_remove_all_groups_expect_default_when_organizations_are_disabled() {
organizationFlags.setEnabled(false);
UserDto user = db.users().insertUser();
GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
@@ -262,7 +265,7 @@ public class UserIdentityAuthenticatorTest {
}
@Test
- public void does_not_force_default_group_when_authenticating_existing_user_when_organizations_are_enabled() throws Exception {
+ public void does_not_force_default_group_when_authenticating_existing_user_when_organizations_are_enabled() {
organizationFlags.setEnabled(true);
UserDto user = db.users().insertUser();
GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
@@ -276,7 +279,7 @@ public class UserIdentityAuthenticatorTest {
}
@Test
- public void ignore_groups_on_non_default_organizations() throws Exception {
+ public void ignore_groups_on_non_default_organizations() {
organizationFlags.setEnabled(true);
OrganizationDto org = db.organizations().insert();
UserDto user = db.users().insertUser(newUserDto()
@@ -299,7 +302,7 @@ public class UserIdentityAuthenticatorTest {
}
@Test
- public void fail_to_authenticate_new_user_when_allow_users_to_signup_is_false() throws Exception {
+ public void fail_to_authenticate_new_user_when_allow_users_to_signup_is_false() {
TestIdentityProvider identityProvider = new TestIdentityProvider()
.setKey("github")
.setName("Github")
@@ -313,7 +316,7 @@ public class UserIdentityAuthenticatorTest {
}
@Test
- public void fail_to_authenticate_new_user_when_email_already_exists() throws Exception {
+ public void fail_to_authenticate_new_user_when_email_already_exists() {
db.users().insertUser(newUserDto()
.setLogin("Existing user with same email")
.setActive(true)
diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/BulkIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/es/BulkIndexerTest.java
index 00a6028c46b..a1c5519384a 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/es/BulkIndexerTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/es/BulkIndexerTest.java
@@ -20,13 +20,21 @@
package org.sonar.server.es;
import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.stream.IntStream;
+import org.apache.commons.lang.math.RandomUtils;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.index.query.QueryBuilders;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
+import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.db.DbTester;
+import org.sonar.db.es.EsQueueDto;
import org.sonar.server.es.BulkIndexer.Size;
import static org.assertj.core.api.Assertions.assertThat;
@@ -35,8 +43,12 @@ import static org.sonar.server.es.FakeIndexDefinition.INDEX_TYPE_FAKE;
public class BulkIndexerTest {
+ private TestSystem2 testSystem2 = new TestSystem2().setNow(1_000L);
+
@Rule
public EsTester esTester = new EsTester(new FakeIndexDefinition().setReplicas(1));
+ @Rule
+ public DbTester dbTester = DbTester.create(testSystem2);
@Test
public void index_nothing() {
@@ -102,6 +114,42 @@ public class BulkIndexerTest {
assertThat(count()).isEqualTo(removeFrom);
}
+ @Test
+ @Ignore
+ public void when_index_is_done_EsQueues_must_be_deleted() {
+ BulkIndexer indexer = new BulkIndexer(esTester.client(), INDEX, Size.REGULAR);
+ int nbOfDelete = 10 + RandomUtils.nextInt(10);
+ int nbOfInsert = 10 + RandomUtils.nextInt(10);
+ int nbOfDocumentNotToBeDeleted = 10 + RandomUtils.nextInt(10);
+ Collection<EsQueueDto> esQueueDtos = new ArrayList<>();
+
+ // Those documents must be kept
+ FakeDoc[] docs = new FakeDoc[nbOfDocumentNotToBeDeleted];
+ for (int i = 1; i <= nbOfDocumentNotToBeDeleted; i++) {
+ docs[i] = FakeIndexDefinition.newDoc(-i);
+ }
+ esTester.putDocuments(INDEX_TYPE_FAKE, docs);
+
+ // Create nbOfDelete documents to be deleted
+ docs = new FakeDoc[nbOfDelete];
+ for (int i = 1; i <= nbOfDelete; i++) {
+ docs[i] = FakeIndexDefinition.newDoc(i);
+ }
+ esTester.putDocuments(INDEX_TYPE_FAKE, docs);
+ assertThat(count()).isEqualTo(nbOfDelete + nbOfDocumentNotToBeDeleted);
+
+ indexer.start(dbTester.getSession(), dbTester.getDbClient(), esQueueDtos);
+ // Create nbOfDelete for old Documents
+ IntStream.rangeClosed(1, nbOfDelete).forEach(
+ i -> indexer.addDeletion(INDEX_TYPE_FAKE, "" + i));
+ // Create nbOfInsert for new Documents
+ IntStream.rangeClosed(nbOfDelete + 1, nbOfInsert).forEach(
+ i -> indexer.add(newIndexRequest(i)));
+ indexer.stop();
+
+ assertThat(count()).isEqualTo(nbOfInsert + nbOfDocumentNotToBeDeleted);
+ }
+
private long count() {
return esTester.countDocuments("fakes", "fake");
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/RecoveryIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/es/RecoveryIndexerTest.java
new file mode 100644
index 00000000000..bb43319c9e4
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/es/RecoveryIndexerTest.java
@@ -0,0 +1,394 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.es;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.IntStream;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+import org.sonar.api.config.Settings;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.es.EsQueueDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.user.index.UserIndexDefinition;
+import org.sonar.server.user.index.UserIndexer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.sonar.api.utils.log.LoggerLevel.ERROR;
+import static org.sonar.api.utils.log.LoggerLevel.INFO;
+import static org.sonar.api.utils.log.LoggerLevel.TRACE;
+import static org.sonar.core.util.stream.MoreCollectors.toArrayList;
+
+public class RecoveryIndexerTest {
+
+ private static final long PAST = 1_000L;
+ private TestSystem2 system2 = new TestSystem2().setNow(PAST);
+
+ @Rule
+ public final EsTester es = new EsTester(new UserIndexDefinition(new MapSettings().asConfig()));
+ @Rule
+ public final DbTester db = DbTester.create(system2);
+ @Rule
+ public final LogTester logTester = new LogTester().setLevel(TRACE);
+ @Rule
+ public TestRule safeguard = new Timeout(60, TimeUnit.SECONDS);
+
+ private RecoveryIndexer underTest;
+
+ @After
+ public void tearDown() {
+ if (underTest != null) {
+ underTest.stop();
+ }
+ }
+
+ @Test
+ public void display_default_configuration_at_startup() {
+ UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
+ underTest = newRecoveryIndexer(userIndexer, new MapSettings());
+
+ underTest.start();
+
+ assertThat(logTester.logs(LoggerLevel.DEBUG)).contains(
+ "Elasticsearch recovery - sonar.search.recovery.delayInMs=300000",
+ "Elasticsearch recovery - sonar.search.recovery.minAgeInMs=300000");
+ }
+
+ @Test
+ public void start_triggers_recovery_run_at_fixed_rate() throws Exception {
+ Settings settings = new MapSettings()
+ .setProperty("sonar.search.recovery.initialDelayInMs", "0")
+ .setProperty("sonar.search.recovery.delayInMs", "1");
+ underTest = spy(new RecoveryIndexer(system2, settings, db.getDbClient(), mock(UserIndexer.class)));
+ AtomicInteger calls = new AtomicInteger(0);
+ doAnswer(invocation -> {
+ calls.incrementAndGet();
+ return null;
+ }).when(underTest).recover();
+
+ underTest.start();
+
+ // wait for 2 runs
+ while (calls.get() < 2) {
+ Thread.sleep(1L);
+ }
+ }
+
+ @Test
+ public void successfully_index_old_records() {
+ EsQueueDto item1 = createUnindexedUser();
+ EsQueueDto item2 = createUnindexedUser();
+
+ ProxyUserIndexer userIndexer = new ProxyUserIndexer();
+ advanceInTime();
+ underTest = newRecoveryIndexer(userIndexer);
+ underTest.recover();
+
+ assertThatQueueHasSize(0);
+ assertThat(userIndexer.called)
+ .extracting(EsQueueDto::getUuid)
+ .containsExactlyInAnyOrder(item1.getUuid(), item2.getUuid());
+
+ assertThatLogsContain(TRACE, "Elasticsearch recovery - processing 2 USER");
+ assertThatLogsContain(INFO, "Elasticsearch recovery - 2 documents processed [0 failures]");
+ }
+
+ @Test
+ public void recent_records_are_not_recovered() {
+ createUnindexedUser();
+ createUnindexedUser();
+
+ ProxyUserIndexer userIndexer = new ProxyUserIndexer();
+ // do not advance in time
+ underTest = newRecoveryIndexer(userIndexer);
+ underTest.recover();
+
+ assertThatQueueHasSize(2);
+ assertThat(userIndexer.called).isEmpty();
+
+ assertThatLogsDoNotContain(TRACE, "Elasticsearch recovery - processing 2 USER");
+ assertThatLogsDoNotContain(INFO, "documents processed");
+ }
+
+ @Test
+ public void do_nothing_if_queue_is_empty() {
+ underTest = newRecoveryIndexer();
+
+ underTest.recover();
+
+ assertThatNoLogsFromRecovery(INFO);
+ assertThatNoLogsFromRecovery(ERROR);
+ assertThatQueueHasSize(0);
+ }
+
+ @Test
+ public void log_exception_on_recovery_failure() {
+ createUnindexedUser();
+ FailingOnceUserIndexer failingOnceUserIndexer = new FailingOnceUserIndexer();
+ advanceInTime();
+
+ underTest = newRecoveryIndexer(failingOnceUserIndexer);
+ underTest.recover();
+
+ // No rows treated
+ assertThatQueueHasSize(1);
+ assertThatLogsContain(ERROR, "Elasticsearch recovery - fail to recover documents");
+ }
+
+ @Test
+ public void scheduler_is_not_stopped_on_failures() throws Exception {
+ createUnindexedUser();
+ advanceInTime();
+ FailingUserIndexer userIndexer = new FailingUserIndexer();
+
+ underTest = newRecoveryIndexer(userIndexer);
+ underTest.start();
+
+ // all runs fail, but they are still scheduled
+ // -> waiting for 2 runs
+ while (userIndexer.called.size() < 2) {
+ Thread.sleep(1L);
+ }
+ }
+
+ @Test
+ public void recovery_retries_on_next_run_if_failure() throws Exception {
+ createUnindexedUser();
+ advanceInTime();
+ FailingOnceUserIndexer userIndexer = new FailingOnceUserIndexer();
+
+ underTest = newRecoveryIndexer(userIndexer);
+ underTest.start();
+
+ // first run fails, second run succeeds
+ userIndexer.counter.await(30, TimeUnit.SECONDS);
+
+ // First we expecting an exception at first run
+ // Then the second run must have treated all records
+ assertThatLogsContain(ERROR, "Elasticsearch recovery - fail to recover documents");
+ assertThatQueueHasSize(0);
+ }
+
+ @Test
+ public void stop_run_if_too_many_failures() throws Exception {
+ IntStream.range(0, 10).forEach(i -> createUnindexedUser());
+ advanceInTime();
+
+ // 10 docs to process, by groups of 3.
+ // The first group successfully recovers only 1 docs --> above 30% of failures --> stop run
+ PartiallyFailingUserIndexer failingAboveRatioUserIndexer = new PartiallyFailingUserIndexer(1);
+ Settings settings = new MapSettings()
+ .setProperty("sonar.search.recovery.loopLimit", "3");
+ underTest = newRecoveryIndexer(failingAboveRatioUserIndexer, settings);
+ underTest.recover();
+
+ assertThatLogsContain(ERROR, "Elasticsearch recovery - too many failures [2/3 documents], waiting for next run");
+ assertThatQueueHasSize(9);
+
+ // The indexer must have been called once and only once.
+ assertThat(failingAboveRatioUserIndexer.called).hasSize(3);
+ }
+
+ @Test
+ public void do_not_stop_run_if_success_rate_is_greater_than_ratio() throws Exception {
+ IntStream.range(0, 10).forEach(i -> createUnindexedUser());
+ advanceInTime();
+
+ // 10 docs to process, by groups of 5.
+ // Each group successfully recovers 4 docs --> below 30% of failures --> continue run
+ PartiallyFailingUserIndexer failingAboveRatioUserIndexer = new PartiallyFailingUserIndexer(4, 4, 2);
+ Settings settings = new MapSettings()
+ .setProperty("sonar.search.recovery.loopLimit", "5");
+ underTest = newRecoveryIndexer(failingAboveRatioUserIndexer, settings);
+ underTest.recover();
+
+ assertThatLogsDoNotContain(ERROR, "too many failures");
+ assertThatQueueHasSize(0);
+ assertThat(failingAboveRatioUserIndexer.indexed).hasSize(10);
+ assertThat(failingAboveRatioUserIndexer.called).hasSize(10 + 2 /* retries */);
+ }
+
+ @Test
+ public void failing_always_on_same_document_does_not_generate_infinite_loop() {
+ EsQueueDto buggy = createUnindexedUser();
+ IntStream.range(0, 10).forEach(i -> createUnindexedUser());
+ advanceInTime();
+
+ FailingAlwaysOnSameElementIndexer indexer = new FailingAlwaysOnSameElementIndexer(buggy);
+ underTest = newRecoveryIndexer(indexer);
+ underTest.recover();
+
+ assertThatLogsContain(ERROR, "Elasticsearch recovery - too many failures [1/1 documents], waiting for next run");
+ assertThatQueueHasSize(1);
+ }
+
+ private class ProxyUserIndexer extends UserIndexer {
+ private final List<EsQueueDto> called = new ArrayList<>();
+
+ ProxyUserIndexer() {
+ super(db.getDbClient(), es.client());
+ }
+
+ @Override
+ public long index(DbSession dbSession, Collection<EsQueueDto> items) {
+ called.addAll(items);
+ return super.index(dbSession, items);
+ }
+ }
+
+ private class FailingUserIndexer extends UserIndexer {
+ private final List<EsQueueDto> called = new ArrayList<>();
+
+ FailingUserIndexer() {
+ super(db.getDbClient(), es.client());
+ }
+
+ @Override
+ public long index(DbSession dbSession, Collection<EsQueueDto> items) {
+ called.addAll(items);
+ throw new RuntimeException("boom");
+ }
+
+ }
+
+ private class FailingOnceUserIndexer extends UserIndexer {
+ private final CountDownLatch counter = new CountDownLatch(2);
+
+ FailingOnceUserIndexer() {
+ super(db.getDbClient(), es.client());
+ }
+
+ @Override
+ public long index(DbSession dbSession, Collection<EsQueueDto> items) {
+ try {
+ if (counter.getCount() == 2) {
+ throw new RuntimeException("boom");
+ }
+ return super.index(dbSession, items);
+ } finally {
+ counter.countDown();
+ }
+ }
+ }
+
+ private class FailingAlwaysOnSameElementIndexer extends UserIndexer {
+ private final EsQueueDto failing;
+
+ FailingAlwaysOnSameElementIndexer(EsQueueDto failing) {
+ super(db.getDbClient(), es.client());
+ this.failing = failing;
+ }
+
+ @Override
+ public long index(DbSession dbSession, Collection<EsQueueDto> items) {
+ List<EsQueueDto> filteredItems = items.stream().filter(
+ i -> !i.getUuid().equals(failing.getUuid())).collect(toArrayList());
+ return super.index(dbSession, filteredItems);
+ }
+ }
+
+ private class PartiallyFailingUserIndexer extends UserIndexer {
+ private final List<EsQueueDto> called = new ArrayList<>();
+ private final List<EsQueueDto> indexed = new ArrayList<>();
+ private final Iterator<Integer> successfulReturns;
+
+ PartiallyFailingUserIndexer(int... successfulReturns) {
+ super(db.getDbClient(), es.client());
+ this.successfulReturns = IntStream.of(successfulReturns).iterator();
+ }
+
+ @Override
+ public long index(DbSession dbSession, Collection<EsQueueDto> items) {
+ System.out.println("called with " + items.size());
+ called.addAll(items);
+ int success = successfulReturns.next();
+ items.stream().limit(success).forEach(i -> {
+ System.out.println(" + success");
+ db.getDbClient().esQueueDao().delete(dbSession, i);
+ indexed.add(i);
+ });
+ dbSession.commit();
+ return success;
+ }
+ }
+
+ private void advanceInTime() {
+ system2.setNow(system2.now() + 100_000_000L);
+ }
+
+ private void assertThatLogsContain(LoggerLevel loggerLevel, String message) {
+ assertThat(logTester.logs(loggerLevel)).filteredOn(m -> m.contains(message)).isNotEmpty();
+ }
+
+ private void assertThatLogsDoNotContain(LoggerLevel loggerLevel, String message) {
+ assertThat(logTester.logs(loggerLevel)).filteredOn(m -> m.contains(message)).isEmpty();
+ }
+
+ private void assertThatNoLogsFromRecovery(LoggerLevel loggerLevel) {
+ assertThat(logTester.logs(loggerLevel)).filteredOn(m -> m.contains("Elasticsearch recovery - ")).isEmpty();
+ }
+
+ private void assertThatQueueHasSize(int number) {
+ assertThat(db.countRowsOfTable(db.getSession(), "es_queue")).isEqualTo(number);
+ }
+
+ private RecoveryIndexer newRecoveryIndexer() {
+ UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
+ return newRecoveryIndexer(userIndexer);
+ }
+
+ private RecoveryIndexer newRecoveryIndexer(UserIndexer userIndexer) {
+ Settings settings = new MapSettings()
+ .setProperty("sonar.search.recovery.initialDelayInMs", "0")
+ .setProperty("sonar.search.recovery.delayInMs", "1")
+ .setProperty("sonar.search.recovery.minAgeInMs", "1");
+ return newRecoveryIndexer(userIndexer, settings);
+ }
+
+ private RecoveryIndexer newRecoveryIndexer(UserIndexer userIndexer, Settings settings) {
+ return new RecoveryIndexer(system2, settings, db.getDbClient(), userIndexer);
+ }
+
+ private EsQueueDto createUnindexedUser() {
+ UserDto user = db.users().insertUser();
+ EsQueueDto item = EsQueueDto.create(EsQueueDto.Type.USER, user.getLogin());
+ db.getDbClient().esQueueDao().insert(db.getSession(), item);
+ db.commit();
+
+ return item;
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/OrganizationCreationImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/OrganizationCreationImplTest.java
index 44e487ffc9f..6ba745d2b4d 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/organization/OrganizationCreationImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/organization/OrganizationCreationImplTest.java
@@ -20,6 +20,7 @@
package org.sonar.server.organization;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import org.apache.commons.lang.RandomStringUtils;
@@ -113,7 +114,7 @@ public class OrganizationCreationImplTest {
@Before
public void setUp() {
someUser = db.users().insertUser();
- userIndexer.index(someUser.getLogin());
+ userIndexer.indexOnStartup(new HashSet<>());
}
@Test
@@ -263,10 +264,8 @@ public class OrganizationCreationImplTest {
@Test
public void create_add_current_user_as_member_of_organization() throws OrganizationCreation.KeyConflictException {
UserDto user = db.users().insertUser();
- userIndexer.index(user.getLogin());
-
builtInQProfileRepositoryRule.initialize();
- userIndexer.index(someUser.getLogin());
+ userIndexer.commitAndIndex(db.getSession(), someUser);
OrganizationDto result = underTest.create(dbSession, someUser, FULL_POPULATED_NEW_ORGANIZATION);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java
index 7d7d33f10d9..e387274390f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java
@@ -21,6 +21,7 @@ package org.sonar.server.organization.ws;
import java.io.IOException;
import java.net.URISyntaxException;
+import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
@@ -112,7 +113,7 @@ public class CreateActionTest {
@Before
public void setUp() {
user = dbTester.users().insertUser();
- userIndexer.index(user.getLogin());
+ userIndexer.indexOnStartup(new HashSet<>());
userSession.logIn(user);
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java
index 903c7fcd16a..117e38fe396 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java
@@ -20,7 +20,6 @@
package org.sonar.server.organization.ws;
-import java.util.Arrays;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
@@ -59,6 +58,7 @@ import org.sonar.server.user.index.UserQuery;
import org.sonar.server.ws.WsActionTester;
import static com.google.common.collect.ImmutableList.of;
+import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
@@ -305,7 +305,7 @@ public class DeleteActionTest {
db.organizations().addMember(org, user1);
db.organizations().addMember(otherOrg, user1);
db.organizations().addMember(org, user2);
- userIndexer.index(Arrays.asList(user1.getLogin(), user2.getLogin()));
+ userIndexer.commitAndIndex(db.getSession(), asList(user1, user2));
logInAsAdministrator(org);
sendRequest(org);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/RemoveMemberActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/RemoveMemberActionTest.java
index c925621cab5..fe6a84f5132 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/RemoveMemberActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/RemoveMemberActionTest.java
@@ -20,6 +20,7 @@
package org.sonar.server.organization.ws;
+import java.util.HashSet;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
@@ -54,6 +55,7 @@ import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;
import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
+import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.groups.Tuple.tuple;
import static org.sonar.api.CoreProperties.DEFAULT_ISSUE_ASSIGNEE;
@@ -93,11 +95,11 @@ public class RemoveMemberActionTest {
user = db.users().insertUser();
db.organizations().addMember(organization, user);
- userIndexer.index(user.getLogin());
UserDto adminUser = db.users().insertAdminByUserPermission(organization);
db.organizations().addMember(organization, adminUser);
- userIndexer.index(adminUser.getLogin());
+
+ userIndexer.indexOnStartup(new HashSet<>());
}
@Test
@@ -317,10 +319,9 @@ public class RemoveMemberActionTest {
OrganizationDto anotherOrganization = db.organizations().insert();
UserDto admin1 = db.users().insertAdminByUserPermission(anotherOrganization);
db.organizations().addMember(anotherOrganization, admin1);
- userIndexer.index(admin1.getLogin());
UserDto admin2 = db.users().insertAdminByUserPermission(anotherOrganization);
db.organizations().addMember(anotherOrganization, admin2);
- userIndexer.index(admin2.getLogin());
+ userIndexer.commitAndIndex(db.getSession(), asList(admin1, admin2));
call(anotherOrganization.getKey(), admin1.getLogin());
diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchMembersActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchMembersActionTest.java
index db4e01c6fbb..ecf87cde61f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchMembersActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchMembersActionTest.java
@@ -127,7 +127,7 @@ public class SearchMembersActionTest {
public void return_avatar() {
UserDto user = db.users().insertUser(u -> u.setEmail("email@domain.com"));
db.organizations().addMember(db.getDefaultOrganization(), user);
- indexer.index(user.getLogin());
+ indexer.commitAndIndex(db.getSession(), user);
SearchMembersWsResponse result = call();
@@ -195,7 +195,6 @@ public class SearchMembersActionTest {
IntStream.range(0, 10).forEach(i -> {
UserDto userDto = db.users().insertUser(user -> user.setName("USER_" + i));
db.organizations().addMember(db.getDefaultOrganization(), userDto);
- indexer.index(userDto.getLogin());
});
indexAllUsers();
request.setPage(2).setPageSize(3);
@@ -214,7 +213,6 @@ public class SearchMembersActionTest {
IntStream.range(0, 10).forEach(i -> {
UserDto userDto = db.users().insertUser(user -> user.setName("USER_" + i));
db.organizations().addMember(db.getDefaultOrganization(), userDto);
- indexer.index(userDto.getLogin());
});
indexAllUsers();
request.setQuery("_9");
@@ -229,7 +227,6 @@ public class SearchMembersActionTest {
IntStream.range(0, 10).forEach(i -> {
UserDto userDto = db.users().insertUser(user -> user.setLogin("USER_" + i));
db.organizations().addMember(db.getDefaultOrganization(), userDto);
- indexer.index(userDto.getLogin());
});
indexAllUsers();
request.setQuery("_9");
@@ -246,7 +243,6 @@ public class SearchMembersActionTest {
.setLogin("L" + i)
.setEmail("USER_" + i + "@email.com"));
db.organizations().addMember(db.getDefaultOrganization(), userDto);
- indexer.index(userDto.getLogin());
});
indexAllUsers();
request.setQuery("_9");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java
index f8a2f50108b..ed73fca1371 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java
@@ -484,7 +484,7 @@ public class ServerUserSessionTest {
session.checkIsSystemAdministrator();
- db.getDbClient().userDao().deactivateUserById(db.getSession(), user.getId());
+ db.getDbClient().userDao().deactivateUser(db.getSession(), user);
db.commit();
// should fail but succeeds because flag is kept in cache
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java
index 0a3f827f0a9..18db33a72dc 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java
@@ -31,7 +31,7 @@ import org.mockito.ArgumentCaptor;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.platform.NewUserHandler;
import org.sonar.api.utils.System2;
-import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.api.utils.internal.AlwaysIncreasingSystem2;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
@@ -49,7 +49,6 @@ import org.sonar.server.user.index.UserIndexDefinition;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.usergroups.DefaultGroupFinder;
-import static com.google.common.collect.Lists.newArrayList;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
@@ -67,11 +66,9 @@ import static org.sonar.server.user.ExternalIdentity.SQ_AUTHORITY;
public class UserUpdaterTest {
- private static final long NOW = 1418215735482L;
- private static final long PAST = 1000000000000L;
private static final String DEFAULT_LOGIN = "marius";
- private System2 system2 = new TestSystem2().setNow(NOW);
+ private System2 system2 = new AlwaysIncreasingSystem2();
@Rule
public ExpectedException expectedException = ExpectedException.none();
@@ -91,20 +88,21 @@ public class UserUpdaterTest {
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
private MapSettings settings = new MapSettings();
- private UserUpdater underTest = new UserUpdater(newUserNotifier, dbClient, userIndexer, system2, organizationFlags, defaultOrganizationProvider, organizationCreation,
+ private UserUpdater underTest = new UserUpdater(newUserNotifier, dbClient, userIndexer, organizationFlags, defaultOrganizationProvider, organizationCreation,
new DefaultGroupFinder(dbClient), settings.asConfig());
@Test
public void create_user() {
createDefaultGroup();
- UserDto dto = underTest.create(db.getSession(), NewUser.builder()
+ UserDto dto = underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setEmail("user@mail.com")
.setPassword("PASSWORD")
.setScmAccounts(ImmutableList.of("u1", "u_1", "User 1"))
- .build());
+ .build(), u -> {
+ });
assertThat(dto.getId()).isNotNull();
assertThat(dto.getLogin()).isEqualTo("user");
@@ -116,8 +114,9 @@ public class UserUpdaterTest {
assertThat(dto.getSalt()).isNotNull();
assertThat(dto.getCryptedPassword()).isNotNull();
- assertThat(dto.getCreatedAt()).isEqualTo(1418215735482L);
- assertThat(dto.getUpdatedAt()).isEqualTo(1418215735482L);
+ assertThat(dto.getCreatedAt())
+ .isPositive()
+ .isEqualTo(dto.getUpdatedAt());
assertThat(dbClient.userDao().selectByLogin(session, "user").getId()).isEqualTo(dto.getId());
List<SearchHit> indexUsers = es.getDocuments(UserIndexDefinition.INDEX_TYPE_USER);
@@ -133,10 +132,11 @@ public class UserUpdaterTest {
public void create_user_with_minimum_fields() {
createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("us")
.setName("User")
- .build());
+ .build(), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, "us");
assertThat(dto.getId()).isNotNull();
@@ -151,11 +151,12 @@ public class UserUpdaterTest {
public void create_user_with_sq_authority_when_no_authority_set() throws Exception {
createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setPassword("password")
- .build());
+ .build(), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, "user");
assertThat(dto.getExternalIdentity()).isEqualTo("user");
@@ -167,11 +168,12 @@ public class UserUpdaterTest {
public void create_user_with_identity_provider() throws Exception {
createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setExternalIdentity(new ExternalIdentity("github", "github-user"))
- .build());
+ .build(), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, "user");
assertThat(dto.isLocal()).isFalse();
@@ -185,11 +187,12 @@ public class UserUpdaterTest {
public void create_user_with_sonarqube_external_identity() throws Exception {
createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setExternalIdentity(new ExternalIdentity(SQ_AUTHORITY, "user"))
- .build());
+ .build(), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, "user");
assertThat(dto.isLocal()).isFalse();
@@ -203,12 +206,13 @@ public class UserUpdaterTest {
public void create_user_with_scm_accounts_containing_blank_or_null_entries() {
createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setPassword("password")
- .setScmAccounts(newArrayList("u1", "", null))
- .build());
+ .setScmAccounts(asList("u1", "", null))
+ .build(), u -> {
+ });
assertThat(dbClient.userDao().selectByLogin(session, "user").getScmAccountsAsList()).containsOnly("u1");
}
@@ -217,12 +221,13 @@ public class UserUpdaterTest {
public void create_user_with_scm_accounts_containing_one_blank_entry() {
createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setPassword("password")
- .setScmAccounts(newArrayList(""))
- .build());
+ .setScmAccounts(asList(""))
+ .build(), u -> {
+ });
assertThat(dbClient.userDao().selectByLogin(session, "user").getScmAccounts()).isNull();
}
@@ -231,12 +236,13 @@ public class UserUpdaterTest {
public void create_user_with_scm_accounts_containing_duplications() {
createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setPassword("password")
- .setScmAccounts(newArrayList("u1", "u1"))
- .build());
+ .setScmAccounts(asList("u1", "u1"))
+ .build(), u -> {
+ });
assertThat(dbClient.userDao().selectByLogin(session, "user").getScmAccountsAsList()).containsOnly("u1");
}
@@ -246,10 +252,11 @@ public class UserUpdaterTest {
createDefaultGroup();
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, false);
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
- .build());
+ .build(), u -> {
+ });
assertThat(dbClient.userDao().selectByLogin(session, "user").isOnboarded()).isTrue();
}
@@ -259,10 +266,11 @@ public class UserUpdaterTest {
createDefaultGroup();
settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, true);
- UserDto user = underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
- .build());
+ .build(), u -> {
+ });
assertThat(dbClient.userDao().selectByLogin(session, "user").isOnboarded()).isFalse();
}
@@ -272,12 +280,13 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Login can't be empty");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(null)
.setName("Marius")
.setEmail("marius@mail.com")
.setPassword("password")
- .build());
+ .build(), u -> {
+ });
}
@Test
@@ -285,12 +294,13 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Use only letters, numbers, and .-_@ please.");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("/marius/")
.setName("Marius")
.setEmail("marius@mail.com")
.setPassword("password")
- .build());
+ .build(), u -> {
+ });
}
@Test
@@ -298,12 +308,13 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Use only letters, numbers, and .-_@ please.");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("mari us")
.setName("Marius")
.setEmail("marius@mail.com")
.setPassword("password")
- .build());
+ .build(), u -> {
+ });
}
@Test
@@ -311,12 +322,13 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Login is too short (minimum is 2 characters)");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("m")
.setName("Marius")
.setEmail("marius@mail.com")
.setPassword("password")
- .build());
+ .build(), u -> {
+ });
}
@Test
@@ -324,12 +336,13 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Login is too long (maximum is 255 characters)");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(Strings.repeat("m", 256))
.setName("Marius")
.setEmail("marius@mail.com")
.setPassword("password")
- .build());
+ .build(), u -> {
+ });
}
@Test
@@ -337,12 +350,13 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Name can't be empty");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName(null)
.setEmail("marius@mail.com")
.setPassword("password")
- .build());
+ .build(), u -> {
+ });
}
@Test
@@ -350,12 +364,13 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Name is too long (maximum is 200 characters)");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName(Strings.repeat("m", 201))
.setEmail("marius@mail.com")
.setPassword("password")
- .build());
+ .build(), u -> {
+ });
}
@Test
@@ -363,23 +378,25 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Email is too long (maximum is 100 characters)");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName("Marius")
.setEmail(Strings.repeat("m", 101))
.setPassword("password")
- .build());
+ .build(), u -> {
+ });
}
@Test
public void fail_to_create_user_with_many_errors() {
try {
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("")
.setName("")
.setEmail("marius@mail.com")
.setPassword("")
- .build());
+ .build(), u -> {
+ });
fail();
} catch (BadRequestException e) {
assertThat(e.errors()).hasSize(3);
@@ -393,13 +410,14 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("The scm account 'jo' is already used by user(s) : 'John (john)'");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName("Marius")
.setEmail("marius@mail.com")
.setPassword("password")
- .setScmAccounts(newArrayList("jo"))
- .build());
+ .setScmAccounts(asList("jo"))
+ .build(), u -> {
+ });
}
@Test
@@ -410,13 +428,14 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("The scm account 'john@email.com' is already used by user(s) : 'John (john), Technical account (technical-account)'");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName("Marius")
.setEmail("marius@mail.com")
.setPassword("password")
- .setScmAccounts(newArrayList("john@email.com"))
- .build());
+ .setScmAccounts(asList("john@email.com"))
+ .build(), u -> {
+ });
}
@Test
@@ -424,13 +443,14 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Login and email are automatically considered as SCM accounts");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
- .setScmAccounts(newArrayList(DEFAULT_LOGIN))
- .build());
+ .setScmAccounts(asList(DEFAULT_LOGIN))
+ .build(), u -> {
+ });
}
@Test
@@ -438,26 +458,28 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Login and email are automatically considered as SCM accounts");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
- .setScmAccounts(newArrayList("marius2@mail.com"))
- .build());
+ .setScmAccounts(asList("marius2@mail.com"))
+ .build(), u -> {
+ });
}
@Test
public void notify_new_user() {
createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setEmail("user@mail.com")
.setPassword("password")
- .setScmAccounts(newArrayList("u1", "u_1"))
- .build());
+ .setScmAccounts(asList("u1", "u_1"))
+ .build(), u -> {
+ });
verify(newUserNotifier).onNewUser(newUserHandler.capture());
assertThat(newUserHandler.getValue().getLogin()).isEqualTo("user");
@@ -470,12 +492,13 @@ public class UserUpdaterTest {
organizationFlags.setEnabled(false);
GroupDto defaultGroup = createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setEmail("user@mail.com")
.setPassword("password")
- .build());
+ .build(), u -> {
+ });
Multimap<String, String> groups = dbClient.groupMembershipDao().selectGroupsByLogins(session, asList("user"));
assertThat(groups.get("user")).containsOnly(defaultGroup.getName());
@@ -486,12 +509,13 @@ public class UserUpdaterTest {
organizationFlags.setEnabled(true);
createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setEmail("user@mail.com")
.setPassword("password")
- .build());
+ .build(), u -> {
+ });
Multimap<String, String> groups = dbClient.groupMembershipDao().selectGroupsByLogins(session, asList("user"));
assertThat(groups.get("user")).isEmpty();
@@ -502,25 +526,27 @@ public class UserUpdaterTest {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Default group cannot be found");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setEmail("user@mail.com")
.setPassword("password")
- .setScmAccounts(newArrayList("u1", "u_1"))
- .build());
+ .setScmAccounts(asList("u1", "u_1"))
+ .build(), u -> {
+ });
}
@Test
public void create_personal_organization_when_creating_user() {
createDefaultGroup();
- UserDto dto = underTest.create(db.getSession(), NewUser.builder()
+ UserDto dto = underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setEmail("user@mail.com")
.setPassword("PASSWORD")
- .build());
+ .build(), u -> {
+ });
verify(organizationCreation).createForUser(any(DbSession.class), eq(dto));
}
@@ -530,12 +556,13 @@ public class UserUpdaterTest {
organizationFlags.setEnabled(false);
createDefaultGroup();
- UserDto dto = underTest.create(db.getSession(), NewUser.builder()
+ UserDto dto = underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setEmail("user@mail.com")
.setPassword("PASSWORD")
- .build());
+ .build(), u -> {
+ });
assertThat(dbClient.organizationMemberDao().select(db.getSession(), defaultOrganizationProvider.get().getUuid(), dto.getId())).isPresent();
}
@@ -545,30 +572,30 @@ public class UserUpdaterTest {
organizationFlags.setEnabled(true);
createDefaultGroup();
- UserDto dto = underTest.create(db.getSession(), NewUser.builder()
+ UserDto dto = underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin("user")
.setName("User")
.setEmail("user@mail.com")
.setPassword("PASSWORD")
- .build());
+ .build(), u -> {
+ });
assertThat(dbClient.organizationMemberDao().select(db.getSession(), defaultOrganizationProvider.get().getUuid(), dto.getId())).isNotPresent();
}
@Test
public void reactivate_user_when_creating_user_with_existing_login() {
- db.users().insertUser(newDisabledUser(DEFAULT_LOGIN)
- .setLocal(false)
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ UserDto user = db.users().insertUser(newDisabledUser(DEFAULT_LOGIN)
+ .setLocal(false));
createDefaultGroup();
- UserDto dto = underTest.create(db.getSession(), NewUser.builder()
+ UserDto dto = underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
- .build());
+ .build(), u -> {
+ });
session.commit();
assertThat(dto.isActive()).isTrue();
@@ -579,26 +606,25 @@ public class UserUpdaterTest {
assertThat(dto.getSalt()).isNotNull().isNotEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365");
assertThat(dto.getCryptedPassword()).isNotNull().isNotEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg");
- assertThat(dto.getCreatedAt()).isEqualTo(PAST);
- assertThat(dto.getUpdatedAt()).isEqualTo(NOW);
+ assertThat(dto.getCreatedAt()).isEqualTo(user.getCreatedAt());
+ assertThat(dto.getUpdatedAt()).isGreaterThan(user.getCreatedAt());
assertThat(dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN).isActive()).isTrue();
}
@Test
public void reactivate_user_not_having_password() {
- db.users().insertUser(newDisabledUser("marius").setName("Marius").setEmail("marius@lesbronzes.fr")
+ UserDto user = db.users().insertUser(newDisabledUser("marius").setName("Marius").setEmail("marius@lesbronzes.fr")
.setSalt(null)
- .setCryptedPassword(null)
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setCryptedPassword(null));
createDefaultGroup();
- UserDto dto = underTest.create(db.getSession(), NewUser.builder()
+ UserDto dto = underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
- .build());
+ .build(), u -> {
+ });
session.commit();
assertThat(dto.isActive()).isTrue();
@@ -608,23 +634,22 @@ public class UserUpdaterTest {
assertThat(dto.getSalt()).isNull();
assertThat(dto.getCryptedPassword()).isNull();
- assertThat(dto.getCreatedAt()).isEqualTo(PAST);
- assertThat(dto.getUpdatedAt()).isEqualTo(NOW);
+ assertThat(dto.getCreatedAt()).isEqualTo(user.getCreatedAt());
+ assertThat(dto.getUpdatedAt()).isGreaterThan(user.getCreatedAt());
}
@Test
public void update_external_provider_when_reactivating_user() {
db.users().insertUser(newDisabledUser(DEFAULT_LOGIN)
- .setLocal(true)
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setLocal(true));
createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName("Marius2")
.setExternalIdentity(new ExternalIdentity("github", "john"))
- .build());
+ .build(), u -> {
+ });
session.commit();
UserDto dto = dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN);
@@ -635,40 +660,38 @@ public class UserUpdaterTest {
@Test
public void fail_to_reactivate_user_if_not_disabled() {
- db.users().insertUser(newLocalUser("marius", "Marius", "marius@lesbronzes.fr")
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ db.users().insertUser(newLocalUser("marius", "Marius", "marius@lesbronzes.fr"));
createDefaultGroup();
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("An active user with login 'marius' already exists");
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
- .build());
+ .build(), u -> {
+ });
}
@Test
public void associate_default_groups_when_reactivating_user_and_organizations_are_disabled() {
organizationFlags.setEnabled(false);
UserDto userDto = db.users().insertUser(newDisabledUser(DEFAULT_LOGIN)
- .setLocal(true)
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setLocal(true));
db.organizations().insertForUuid("org1");
GroupDto groupDto = db.users().insertGroup(GroupTesting.newGroupDto().setName("sonar-devs").setOrganizationUuid("org1"));
db.users().insertMember(groupDto, userDto);
GroupDto defaultGroup = createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
- .build());
+ .build(), u -> {
+ });
session.commit();
Multimap<String, String> groups = dbClient.groupMembershipDao().selectGroupsByLogins(session, asList(DEFAULT_LOGIN));
@@ -679,20 +702,19 @@ public class UserUpdaterTest {
public void does_not_associate_default_groups_when_reactivating_user_and_organizations_are_enabled() {
organizationFlags.setEnabled(true);
UserDto userDto = db.users().insertUser(newDisabledUser(DEFAULT_LOGIN)
- .setLocal(true)
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setLocal(true));
db.organizations().insertForUuid("org1");
GroupDto groupDto = db.users().insertGroup(GroupTesting.newGroupDto().setName("sonar-devs").setOrganizationUuid("org1"));
db.users().insertMember(groupDto, userDto);
GroupDto defaultGroup = createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
- .build());
+ .build(), u -> {
+ });
session.commit();
Multimap<String, String> groups = dbClient.groupMembershipDao().selectGroupsByLogins(session, asList(DEFAULT_LOGIN));
@@ -705,7 +727,8 @@ public class UserUpdaterTest {
db.users().insertUser(newDisabledUser(DEFAULT_LOGIN));
createDefaultGroup();
- UserDto dto = underTest.create(db.getSession(), NewUser.builder().setLogin(DEFAULT_LOGIN).setName("Name").build());
+ UserDto dto = underTest.createAndCommit(db.getSession(), NewUser.builder().setLogin(DEFAULT_LOGIN).setName("Name").build(), u -> {
+ });
session.commit();
assertThat(dbClient.organizationMemberDao().select(db.getSession(), defaultOrganizationProvider.get().getUuid(), dto.getId())).isPresent();
@@ -717,7 +740,8 @@ public class UserUpdaterTest {
db.users().insertUser(newDisabledUser(DEFAULT_LOGIN));
createDefaultGroup();
- UserDto dto = underTest.create(db.getSession(), NewUser.builder().setLogin(DEFAULT_LOGIN).setName("Name").build());
+ UserDto dto = underTest.createAndCommit(db.getSession(), NewUser.builder().setLogin(DEFAULT_LOGIN).setName("Name").build(), u -> {
+ });
session.commit();
assertThat(dbClient.organizationMemberDao().select(db.getSession(), defaultOrganizationProvider.get().getUuid(), dto.getId())).isNotPresent();
@@ -731,10 +755,11 @@ public class UserUpdaterTest {
.setOnboarded(false));
createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(user.getLogin())
.setName("name")
- .build());
+ .build(), u -> {
+ });
assertThat(dbClient.userDao().selectByLogin(session, user.getLogin()).isOnboarded()).isTrue();
}
@@ -747,10 +772,11 @@ public class UserUpdaterTest {
.setOnboarded(true));
createDefaultGroup();
- underTest.create(db.getSession(), NewUser.builder()
+ underTest.createAndCommit(db.getSession(), NewUser.builder()
.setLogin(user.getLogin())
.setName("name")
- .build());
+ .build(), u -> {
+ });
assertThat(dbClient.userDao().selectByLogin(session, user.getLogin()).isOnboarded()).isFalse();
}
@@ -760,17 +786,15 @@ public class UserUpdaterTest {
UserDto user = db.users().insertUser(newLocalUser(DEFAULT_LOGIN, "Marius", "marius@email.com")
.setScmAccounts(asList("ma", "marius33"))
.setSalt("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365")
- .setCryptedPassword("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg")
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setCryptedPassword("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
- .setScmAccounts(newArrayList("ma2")));
- session.commit();
+ .setScmAccounts(asList("ma2")), u -> {
+ });
UserDto updatedUser = dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN);
assertThat(updatedUser.isActive()).isTrue();
@@ -780,8 +804,8 @@ public class UserUpdaterTest {
assertThat(updatedUser.getSalt()).isNotEqualTo(user.getSalt());
assertThat(updatedUser.getCryptedPassword()).isNotEqualTo(user.getCryptedPassword());
- assertThat(updatedUser.getCreatedAt()).isEqualTo(PAST);
- assertThat(updatedUser.getUpdatedAt()).isEqualTo(NOW);
+ assertThat(updatedUser.getCreatedAt()).isEqualTo(user.getCreatedAt());
+ assertThat(updatedUser.getUpdatedAt()).isGreaterThan(user.getCreatedAt());
List<SearchHit> indexUsers = es.getDocuments(UserIndexDefinition.INDEX_TYPE_USER);
assertThat(indexUsers).hasSize(1);
@@ -794,37 +818,33 @@ public class UserUpdaterTest {
@Test
public void update_user_external_identity_when_user_was_not_local() {
- db.users().insertUser(UserTesting.newExternalUser(DEFAULT_LOGIN, "Marius", "marius@email.com")
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ UserDto user = db.users().insertUser(UserTesting.newExternalUser(DEFAULT_LOGIN, "Marius", "marius@email.com"));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@email.com")
.setPassword(null)
- .setExternalIdentity(new ExternalIdentity("github", "john")));
- session.commit();
+ .setExternalIdentity(new ExternalIdentity("github", "john")), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN);
assertThat(dto.getExternalIdentity()).isEqualTo("john");
assertThat(dto.getExternalIdentityProvider()).isEqualTo("github");
- assertThat(dto.getUpdatedAt()).isEqualTo(NOW);
+ assertThat(dto.getUpdatedAt()).isGreaterThan(user.getCreatedAt());
}
@Test
public void update_user_external_identity_when_user_was_local() {
- db.users().insertUser(newLocalUser(DEFAULT_LOGIN, "Marius", "marius@email.com")
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ UserDto user = db.users().insertUser(newLocalUser(DEFAULT_LOGIN, "Marius", "marius@email.com"));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@email.com")
.setPassword(null)
- .setExternalIdentity(new ExternalIdentity("github", "john")));
- session.commit();
+ .setExternalIdentity(new ExternalIdentity("github", "john")), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN);
assertThat(dto.getExternalIdentity()).isEqualTo("john");
@@ -832,7 +852,7 @@ public class UserUpdaterTest {
// Password must be removed
assertThat(dto.getCryptedPassword()).isNull();
assertThat(dto.getSalt()).isNull();
- assertThat(dto.getUpdatedAt()).isEqualTo(NOW);
+ assertThat(dto.getUpdatedAt()).isGreaterThan(user.getCreatedAt());
}
@Test
@@ -840,17 +860,15 @@ public class UserUpdaterTest {
UserDto user = db.users().insertUser(newLocalUser(DEFAULT_LOGIN, "Marius", "marius@lesbronzes.fr")
.setScmAccounts(asList("ma", "marius33"))
.setSalt("salt")
- .setCryptedPassword("crypted password")
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setCryptedPassword("crypted password"));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
- .setScmAccounts(newArrayList("ma2")));
- session.commit();
+ .setScmAccounts(asList("ma2")), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN);
assertThat(dto.isActive()).isTrue();
@@ -860,8 +878,8 @@ public class UserUpdaterTest {
assertThat(dto.getSalt()).isNotEqualTo(user.getSalt());
assertThat(dto.getCryptedPassword()).isNotEqualTo(user.getCryptedPassword());
- assertThat(dto.getCreatedAt()).isEqualTo(PAST);
- assertThat(dto.getUpdatedAt()).isEqualTo(NOW);
+ assertThat(dto.getCreatedAt()).isEqualTo(user.getCreatedAt());
+ assertThat(dto.getUpdatedAt()).isGreaterThan(user.getUpdatedAt());
List<SearchHit> indexUsers = es.getDocuments(UserIndexDefinition.INDEX_TYPE_USER);
assertThat(indexUsers).hasSize(1);
@@ -875,17 +893,15 @@ public class UserUpdaterTest {
@Test
public void update_user_with_scm_accounts_containing_blank_entry() {
db.users().insertUser(newLocalUser(DEFAULT_LOGIN, "Marius", "marius@lesbronzes.fr")
- .setScmAccounts(asList("ma", "marius33"))
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setScmAccounts(asList("ma", "marius33")));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
- .setScmAccounts(newArrayList("ma2", "", null)));
- session.commit();
+ .setScmAccounts(asList("ma2", "", null)), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN);
assertThat(dto.getScmAccountsAsList()).containsOnly("ma2");
@@ -896,14 +912,12 @@ public class UserUpdaterTest {
db.users().insertUser(newLocalUser(DEFAULT_LOGIN, "Marius", "marius@lesbronzes.fr")
.setScmAccounts(asList("ma", "marius33"))
.setSalt("salt")
- .setCryptedPassword("crypted password")
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setCryptedPassword("crypted password"));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
- .setName("Marius2"));
- session.commit();
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
+ .setName("Marius2"), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN);
assertThat(dto.getName()).isEqualTo("Marius2");
@@ -920,14 +934,12 @@ public class UserUpdaterTest {
db.users().insertUser(newLocalUser(DEFAULT_LOGIN, "Marius", "marius@lesbronzes.fr")
.setScmAccounts(asList("ma", "marius33"))
.setSalt("salt")
- .setCryptedPassword("crypted password")
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setCryptedPassword("crypted password"));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
- .setEmail("marius2@mail.com"));
- session.commit();
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
+ .setEmail("marius2@mail.com"), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN);
assertThat(dto.getEmail()).isEqualTo("marius2@mail.com");
@@ -944,14 +956,12 @@ public class UserUpdaterTest {
db.users().insertUser(newLocalUser(DEFAULT_LOGIN, "Marius", "marius@lesbronzes.fr")
.setScmAccounts(asList("ma", "marius33"))
.setSalt("salt")
- .setCryptedPassword("crypted password")
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setCryptedPassword("crypted password"));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
- .setScmAccounts(newArrayList("ma2")));
- session.commit();
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
+ .setScmAccounts(asList("ma2")), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN);
assertThat(dto.getScmAccountsAsList()).containsOnly("ma2");
@@ -966,14 +976,12 @@ public class UserUpdaterTest {
@Test
public void update_scm_accounts_with_same_values() {
db.users().insertUser(newLocalUser(DEFAULT_LOGIN, "Marius", "marius@lesbronzes.fr")
- .setScmAccounts(asList("ma", "marius33"))
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setScmAccounts(asList("ma", "marius33")));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
- .setScmAccounts(newArrayList("ma", "marius33")));
- session.commit();
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
+ .setScmAccounts(asList("ma", "marius33")), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN);
assertThat(dto.getScmAccountsAsList()).containsOnly("ma", "marius33");
@@ -982,14 +990,12 @@ public class UserUpdaterTest {
@Test
public void remove_scm_accounts() {
db.users().insertUser(newLocalUser(DEFAULT_LOGIN, "Marius", "marius@lesbronzes.fr")
- .setScmAccounts(asList("ma", "marius33"))
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setScmAccounts(asList("ma", "marius33")));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
- .setScmAccounts(null));
- session.commit();
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
+ .setScmAccounts(null), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN);
assertThat(dto.getScmAccounts()).isNull();
@@ -1000,14 +1006,12 @@ public class UserUpdaterTest {
db.users().insertUser(newLocalUser(DEFAULT_LOGIN, "Marius", "marius@lesbronzes.fr")
.setScmAccounts(asList("ma", "marius33"))
.setSalt("salt")
- .setCryptedPassword("crypted password")
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setCryptedPassword("crypted password"));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
- .setPassword("password2"));
- session.commit();
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
+ .setPassword("password2"), u -> {
+ });
UserDto dto = dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN);
assertThat(dto.getSalt()).isNotEqualTo("salt");
@@ -1023,34 +1027,30 @@ public class UserUpdaterTest {
public void update_only_external_identity_id() {
db.users().insertUser(UserTesting.newExternalUser(DEFAULT_LOGIN, "Marius", "marius@email.com")
.setExternalIdentity("john")
- .setExternalIdentityProvider("github")
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setExternalIdentityProvider("github"));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN).setExternalIdentity(new ExternalIdentity("github", "john.smith")));
- session.commit();
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN).setExternalIdentity(new ExternalIdentity("github", "john.smith")), u -> {
+ });
assertThat(dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN))
- .extracting(UserDto::getExternalIdentity, UserDto::getExternalIdentityProvider, UserDto::getUpdatedAt)
- .containsOnly("john.smith", "github", NOW);
+ .extracting(UserDto::getExternalIdentity, UserDto::getExternalIdentityProvider)
+ .containsOnly("john.smith", "github");
}
@Test
public void update_only_external_identity_provider() {
db.users().insertUser(UserTesting.newExternalUser(DEFAULT_LOGIN, "Marius", "marius@email.com")
.setExternalIdentity("john")
- .setExternalIdentityProvider("github")
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST));
+ .setExternalIdentityProvider("github"));
createDefaultGroup();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN).setExternalIdentity(new ExternalIdentity("bitbucket", "john")));
- session.commit();
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN).setExternalIdentity(new ExternalIdentity("bitbucket", "john")), u -> {
+ });
assertThat(dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN))
- .extracting(UserDto::getExternalIdentity, UserDto::getExternalIdentityProvider, UserDto::getUpdatedAt)
- .containsOnly("john", "bitbucket", NOW);
+ .extracting(UserDto::getExternalIdentity, UserDto::getExternalIdentityProvider)
+ .containsOnly("john", "bitbucket");
}
@Test
@@ -1058,20 +1058,18 @@ public class UserUpdaterTest {
UserDto user = UserTesting.newExternalUser(DEFAULT_LOGIN, "Marius", "marius@email.com")
.setExternalIdentity("john")
.setExternalIdentityProvider("github")
- .setScmAccounts(asList("ma1", "ma2"))
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST);
+ .setScmAccounts(asList("ma1", "ma2"));
db.users().insertUser(user);
createDefaultGroup();
- underTest.update(session, UpdateUser.create(user.getLogin())
+ underTest.updateAndCommit(session, UpdateUser.create(user.getLogin())
.setName(user.getName())
.setEmail(user.getEmail())
.setScmAccounts(user.getScmAccountsAsList())
- .setExternalIdentity(new ExternalIdentity(user.getExternalIdentityProvider(), user.getExternalIdentity())));
- session.commit();
+ .setExternalIdentity(new ExternalIdentity(user.getExternalIdentityProvider(), user.getExternalIdentity())), u -> {
+ });
- assertThat(dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN).getUpdatedAt()).isEqualTo(PAST);
+ assertThat(dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN).getUpdatedAt()).isEqualTo(user.getUpdatedAt());
}
@Test
@@ -1079,20 +1077,18 @@ public class UserUpdaterTest {
UserDto user = UserTesting.newExternalUser(DEFAULT_LOGIN, "Marius", "marius@email.com")
.setExternalIdentity("john")
.setExternalIdentityProvider("github")
- .setScmAccounts(asList("ma1", "ma2"))
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST);
+ .setScmAccounts(asList("ma1", "ma2"));
db.users().insertUser(user);
createDefaultGroup();
- underTest.update(session, UpdateUser.create(user.getLogin())
+ underTest.updateAndCommit(session, UpdateUser.create(user.getLogin())
.setName(user.getName())
.setEmail(user.getEmail())
.setScmAccounts(asList("ma2", "ma1"))
- .setExternalIdentity(new ExternalIdentity(user.getExternalIdentityProvider(), user.getExternalIdentity())));
- session.commit();
+ .setExternalIdentity(new ExternalIdentity(user.getExternalIdentityProvider(), user.getExternalIdentity())), u -> {
+ });
- assertThat(dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN).getUpdatedAt()).isEqualTo(PAST);
+ assertThat(dbClient.userDao().selectByLogin(session, DEFAULT_LOGIN).getUpdatedAt()).isEqualTo(user.getUpdatedAt());
}
@Test
@@ -1102,7 +1098,8 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Password can't be empty");
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN).setPassword(null));
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN).setPassword(null), u -> {
+ });
}
@Test
@@ -1114,7 +1111,8 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Password cannot be changed when external authentication is used");
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN).setPassword("password2"));
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN).setPassword("password2"), u -> {
+ });
}
@Test
@@ -1123,12 +1121,12 @@ public class UserUpdaterTest {
GroupDto defaultGroup = createDefaultGroup();
// Existing user, he has no group, and should not be associated to the default one
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
- .setScmAccounts(newArrayList("ma2")));
- session.commit();
+ .setScmAccounts(asList("ma2")), u -> {
+ });
Multimap<String, String> groups = dbClient.groupMembershipDao().selectGroupsByLogins(session, asList(DEFAULT_LOGIN));
assertThat(groups.get(DEFAULT_LOGIN).stream().anyMatch(g -> g.equals(defaultGroup.getName()))).isFalse();
@@ -1144,12 +1142,12 @@ public class UserUpdaterTest {
Multimap<String, String> groups = dbClient.groupMembershipDao().selectGroupsByLogins(session, asList(DEFAULT_LOGIN));
assertThat(groups.get(DEFAULT_LOGIN).stream().anyMatch(g -> g.equals(defaultGroup.getName()))).as("Current user groups : %s", groups.get(defaultGroup.getName())).isTrue();
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
- .setScmAccounts(newArrayList("ma2")));
- session.commit();
+ .setScmAccounts(asList("ma2")), u -> {
+ });
// Nothing as changed
groups = dbClient.groupMembershipDao().selectGroupsByLogins(session, asList(DEFAULT_LOGIN));
@@ -1165,11 +1163,12 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("The scm account 'jo' is already used by user(s) : 'John (john)'");
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
.setName("Marius2")
.setEmail("marius2@mail.com")
.setPassword("password2")
- .setScmAccounts(newArrayList("jo")));
+ .setScmAccounts(asList("jo")), u -> {
+ });
}
@Test
@@ -1179,7 +1178,8 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Login and email are automatically considered as SCM accounts");
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN).setScmAccounts(newArrayList(DEFAULT_LOGIN)));
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN).setScmAccounts(asList(DEFAULT_LOGIN)), u -> {
+ });
}
@Test
@@ -1189,7 +1189,8 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Login and email are automatically considered as SCM accounts");
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN).setScmAccounts(newArrayList("marius@lesbronzes.fr")));
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN).setScmAccounts(asList("marius@lesbronzes.fr")), u -> {
+ });
}
@Test
@@ -1199,9 +1200,10 @@ public class UserUpdaterTest {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Login and email are automatically considered as SCM accounts");
- underTest.update(session, UpdateUser.create(DEFAULT_LOGIN)
+ underTest.updateAndCommit(session, UpdateUser.create(DEFAULT_LOGIN)
.setEmail("marius@newmail.com")
- .setScmAccounts(newArrayList("marius@newmail.com")));
+ .setScmAccounts(asList("marius@newmail.com")), u -> {
+ });
}
private GroupDto createDefaultGroup() {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/index/UserIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/index/UserIndexTest.java
index 66ba400145a..f7026e2958f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/index/UserIndexTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/index/UserIndexTest.java
@@ -67,8 +67,6 @@ public class UserIndexTest {
assertThat(userDoc.email()).isEqualTo(user1.email());
assertThat(userDoc.active()).isTrue();
assertThat(userDoc.scmAccounts()).isEqualTo(user1.scmAccounts());
- assertThat(userDoc.createdAt()).isEqualTo(user1.createdAt());
- assertThat(userDoc.updatedAt()).isEqualTo(user1.updatedAt());
assertThat(underTest.getNullableByLogin("")).isNull();
assertThat(underTest.getNullableByLogin("unknown")).isNull();
@@ -202,9 +200,7 @@ public class UserIndexTest {
.setName(login.toUpperCase(Locale.ENGLISH))
.setEmail(login + "@mail.com")
.setActive(true)
- .setScmAccounts(scmAccounts)
- .setCreatedAt(DATE_1)
- .setUpdatedAt(DATE_2);
+ .setScmAccounts(scmAccounts);
}
private static UserDoc newUser(String login, String email, List<String> scmAccounts) {
@@ -213,8 +209,6 @@ public class UserIndexTest {
.setName(login.toUpperCase(Locale.ENGLISH))
.setEmail(email)
.setActive(true)
- .setScmAccounts(scmAccounts)
- .setCreatedAt(DATE_1)
- .setUpdatedAt(DATE_2);
+ .setScmAccounts(scmAccounts);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/index/UserIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/index/UserIndexerTest.java
index 66fb5c2ceae..3f4738b1aae 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/index/UserIndexerTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/index/UserIndexerTest.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.user.index;
+import java.util.HashSet;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
@@ -26,6 +27,7 @@ import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
import org.sonar.db.user.UserDto;
+import org.sonar.db.user.UserTesting;
import org.sonar.server.es.EsTester;
import static java.util.Arrays.asList;
@@ -45,53 +47,34 @@ public class UserIndexerTest {
@Test
public void index_nothing_on_startup() {
- underTest.indexOnStartup(null);
+ underTest.indexOnStartup(new HashSet<>());
assertThat(es.countDocuments(UserIndexDefinition.INDEX_TYPE_USER)).isEqualTo(0L);
}
@Test
- public void index_everything_on_startup() {
- db.users().insertUser(user -> user
- .setLogin("user1")
- .setName("User1")
- .setEmail("user1@mail.com")
- .setActive(true)
- .setScmAccounts(asList("user_1", "u1"))
- .setCreatedAt(1500000000000L)
- .setUpdatedAt(1500000000000L));
-
- underTest.indexOnStartup(null);
+ public void indexOnStartup_adds_all_users_to_index() {
+ UserDto user = db.users().insertUser(u -> u
+ .setScmAccounts(asList("user_1", "u1")));
- List<UserDoc> docs = es.getDocuments(UserIndexDefinition.INDEX_TYPE_USER, UserDoc.class);
- assertThat(docs).hasSize(1);
- UserDoc doc = docs.get(0);
- assertThat(doc.login()).isEqualTo("user1");
- assertThat(doc.name()).isEqualTo("User1");
- assertThat(doc.email()).isEqualTo("user1@mail.com");
- assertThat(doc.active()).isTrue();
- assertThat(doc.scmAccounts()).containsOnly("user_1", "u1");
- assertThat(doc.createdAt()).isEqualTo(1500000000000L);
- assertThat(doc.updatedAt()).isEqualTo(1500000000000L);
- }
-
- @Test
- public void index_single_user_on_startup() {
- UserDto user = db.users().insertUser();
-
- underTest.indexOnStartup(null);
+ underTest.indexOnStartup(new HashSet<>());
List<UserDoc> docs = es.getDocuments(UserIndexDefinition.INDEX_TYPE_USER, UserDoc.class);
assertThat(docs).hasSize(1);
- assertThat(docs).extracting(UserDoc::login).containsExactly(user.getLogin());
+ UserDoc doc = docs.get(0);
+ assertThat(doc.login()).isEqualTo(user.getLogin());
+ assertThat(doc.name()).isEqualTo(user.getName());
+ assertThat(doc.email()).isEqualTo(user.getEmail());
+ assertThat(doc.active()).isEqualTo(user.isActive());
+ assertThat(doc.scmAccounts()).isEqualTo(user.getScmAccountsAsList());
}
@Test
- public void index_single_user() {
+ public void commitAndIndex_single_user() {
UserDto user = db.users().insertUser();
UserDto anotherUser = db.users().insertUser();
- underTest.index(user.getLogin());
+ underTest.commitAndIndex(db.getSession(), user);
List<UserDoc> docs = es.getDocuments(UserIndexDefinition.INDEX_TYPE_USER, UserDoc.class);
assertThat(docs).hasSize(1);
@@ -101,14 +84,14 @@ public class UserIndexerTest {
}
@Test
- public void index_several_users() {
- UserDto user = db.users().insertUser();
- UserDto anotherUser = db.users().insertUser();
+ public void commitAndIndex_multiple_users() {
+ UserDto user1 = db.getDbClient().userDao().insert(db.getSession(), UserTesting.newUserDto());
+ UserDto user2 = db.getDbClient().userDao().insert(db.getSession(), UserTesting.newUserDto());
- underTest.index(asList(user.getLogin(), anotherUser.getLogin()));
+ underTest.commitAndIndex(db.getSession(), asList(user1, user2));
List<UserDoc> docs = es.getDocuments(UserIndexDefinition.INDEX_TYPE_USER, UserDoc.class);
- assertThat(docs).hasSize(2);
- assertThat(docs).extracting(UserDoc::login).containsOnly(user.getLogin(), anotherUser.getLogin());
+ assertThat(docs).extracting(UserDoc::login).containsExactlyInAnyOrder(user1.getLogin(), user2.getLogin());
+ assertThat(db.countRowsOfTable(db.getSession(), "users")).isEqualTo(2);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/index/UserResultSetIteratorTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/index/UserResultSetIteratorTest.java
deleted file mode 100644
index 852a8f75f45..00000000000
--- a/server/sonar-server/src/test/java/org/sonar/server/user/index/UserResultSetIteratorTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.user.index;
-
-import com.google.common.collect.Maps;
-import java.util.Arrays;
-import java.util.Map;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.db.DbTester;
-import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.user.UserDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class UserResultSetIteratorTest {
-
- @Rule
- public DbTester db = DbTester.create();
-
- @Test
- public void iterator_over_users() {
- UserDto userDto1 = db.users().insertUser(u -> u
- .setName("User1")
- .setLogin("user1")
- .setEmail("user1@mail.com")
- .setScmAccounts(Arrays.asList("user_1", "u1"))
- .setCreatedAt(1_500_000_000_000L)
- .setUpdatedAt(1_500_000_000_000L));
- UserDto userDto2 = db.users().insertUser(u -> u
- .setName("User2")
- .setLogin("user2")
- .setEmail("user2@mail.com")
- .setScmAccounts(Arrays.asList("user,2", "user_2"))
- .setCreatedAt(1_500_000_000_000L)
- .setUpdatedAt(1_500_000_000_000L));
- UserDto inactiveUser = db.users().insertUser(u -> u
- .setName("User3")
- .setLogin("user3")
- .setEmail(null)
- .setActive(false)
- .setScmAccounts((String) null)
- .setCreatedAt(1_500_000_000_000L)
- .setUpdatedAt(1_550_000_000_000L));
- OrganizationDto org1 = db.organizations().insertForUuid("ORG_1");
- OrganizationDto org2 = db.organizations().insertForUuid("ORG_2");
- db.organizations().addMember(org1, userDto1);
- db.organizations().addMember(org1, userDto2);
- db.organizations().addMember(org2, userDto1);
-
- UserResultSetIterator it = UserResultSetIterator.create(db.getDbClient(), db.getSession(), null);
- Map<String, UserDoc> usersByLogin = Maps.uniqueIndex(it, UserDoc::login);
- it.close();
-
- assertThat(usersByLogin).hasSize(3);
-
- UserDoc user1 = usersByLogin.get("user1");
- assertThat(user1.name()).isEqualTo("User1");
- assertThat(user1.email()).isEqualTo("user1@mail.com");
- assertThat(user1.active()).isTrue();
- assertThat(user1.scmAccounts()).containsOnly("user_1", "u1");
- assertThat(user1.createdAt()).isEqualTo(1_500_000_000_000L);
- assertThat(user1.updatedAt()).isEqualTo(1_500_000_000_000L);
- assertThat(user1.organizationUuids()).containsOnly("ORG_1", "ORG_2");
-
- UserDoc user2 = usersByLogin.get("user2");
- assertThat(user2.name()).isEqualTo("User2");
- assertThat(user2.email()).isEqualTo("user2@mail.com");
- assertThat(user2.active()).isTrue();
- assertThat(user2.scmAccounts()).containsOnly("user,2", "user_2");
- assertThat(user2.createdAt()).isEqualTo(1_500_000_000_000L);
- assertThat(user2.updatedAt()).isEqualTo(1_500_000_000_000L);
- assertThat(user2.organizationUuids()).containsOnly("ORG_1");
-
- UserDoc user3 = usersByLogin.get("user3");
- assertThat(user3.name()).isEqualTo("User3");
- assertThat(user3.email()).isNull();
- assertThat(user3.active()).isFalse();
- assertThat(user3.scmAccounts()).isEmpty();
- assertThat(user3.createdAt()).isEqualTo(1500000000000L);
- assertThat(user3.updatedAt()).isEqualTo(1550000000000L);
- assertThat(user3.organizationUuids()).isEmpty();
- }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java
index 6a09f91f7e8..3ff528cf1b6 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java
@@ -24,7 +24,6 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.BadRequestException;
@@ -60,7 +59,6 @@ public class ChangePasswordActionTest {
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
private UserUpdater userUpdater = new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), new UserIndexer(db.getDbClient(), esTester.client()),
- System2.INSTANCE,
organizationFlags,
TestDefaultOrganizationProvider.from(db),
mock(OrganizationCreation.class),
@@ -159,13 +157,15 @@ public class ChangePasswordActionTest {
public void fail_to_update_password_on_external_auth() throws Exception {
userSessionRule.logIn().setSystemAdministrator();
- userUpdater.create(db.getSession(), NewUser.builder()
+ NewUser newUser = NewUser.builder()
.setEmail("john@email.com")
.setLogin("john")
.setName("John")
.setScmAccounts(newArrayList("jn"))
.setExternalIdentity(new ExternalIdentity("gihhub", "john"))
- .build());
+ .build();
+ userUpdater.createAndCommit(db.getSession(), newUser, u -> {
+ });
expectedException.expect(BadRequestException.class);
tester.newPostRequest("api/users", "change_password")
@@ -175,12 +175,13 @@ public class ChangePasswordActionTest {
}
private void createUser() {
- userUpdater.create(db.getSession(), NewUser.builder()
+ userUpdater.createAndCommit(db.getSession(), NewUser.builder()
.setEmail("john@email.com")
.setLogin("john")
.setName("John")
.setScmAccounts(newArrayList("jn"))
.setPassword("Valar Dohaeris")
- .build());
+ .build(), u -> {
+ });
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ws/CreateActionTest.java
index d95b91cc0b2..704dc379de7 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/ws/CreateActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/ws/CreateActionTest.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.user.ws;
+import java.util.HashSet;
import java.util.Optional;
import org.junit.Before;
import org.junit.Rule;
@@ -81,11 +82,10 @@ public class CreateActionTest {
private GroupDto defaultGroupInDefaultOrg;
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
-
private OrganizationCreation organizationCreation = mock(OrganizationCreation.class);
private WsActionTester tester = new WsActionTester(new CreateAction(
db.getDbClient(),
- new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), userIndexer, system2, organizationFlags, defaultOrganizationProvider,
+ new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), userIndexer, organizationFlags, defaultOrganizationProvider,
organizationCreation, new DefaultGroupFinder(db.getDbClient()), settings.asConfig()),
userSessionRule));
@@ -234,7 +234,7 @@ public class CreateActionTest {
logInAsSystemAdministrator();
db.users().insertUser(newUserDto("john", "John", "john@email.com").setActive(false));
- userIndexer.index("john");
+ userIndexer.indexOnStartup(new HashSet<>());
call(CreateRequest.builder()
.setLogin("john")
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ws/DeactivateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ws/DeactivateActionTest.java
index a0c8afd3350..9b8346c9de9 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/ws/DeactivateActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/ws/DeactivateActionTest.java
@@ -334,14 +334,10 @@ public class DeactivateActionTest {
}
private UserDto insertUser(UserDto user) {
- user
- .setCreatedAt(system2.now())
- .setUpdatedAt(system2.now());
dbClient.userDao().insert(dbSession, user);
dbClient.userTokenDao().insert(dbSession, newUserToken().setLogin(user.getLogin()));
dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto().setUserId(user.getId()).setKey("foo").setValue("bar"));
- dbSession.commit();
- userIndexer.index(user.getLogin());
+ userIndexer.commitAndIndex(dbSession, user);
return user;
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ws/SearchActionTest.java
index 6c44ded965a..2001d838249 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/ws/SearchActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/ws/SearchActionTest.java
@@ -19,7 +19,7 @@
*/
package org.sonar.server.user.ws;
-import com.google.common.collect.Lists;
+import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Rule;
@@ -281,8 +281,7 @@ public class SearchActionTest {
}
private List<UserDto> injectUsers(int numberOfUsers) throws Exception {
- List<UserDto> userDtos = Lists.newArrayList();
- long createdAt = System.currentTimeMillis();
+ List<UserDto> userDtos = new ArrayList<>();
GroupDto group1 = db.users().insertGroup(newGroupDto().setName("sonar-users"));
GroupDto group2 = db.users().insertGroup(newGroupDto().setName("sonar-admins"));
for (int index = 0; index < numberOfUsers; index++) {
@@ -293,15 +292,13 @@ public class SearchActionTest {
UserDto userDto = dbClient.userDao().insert(dbSession, new UserDto()
.setActive(true)
- .setCreatedAt(createdAt)
.setEmail(email)
.setLogin(login)
.setName(name)
.setScmAccounts(scmAccounts)
.setLocal(true)
.setExternalIdentity(login)
- .setExternalIdentityProvider("sonarqube")
- .setUpdatedAt(createdAt));
+ .setExternalIdentityProvider("sonarqube"));
userDtos.add(userDto);
for (int tokenIndex = 0; tokenIndex < index; tokenIndex++) {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ws/SkipOnboardingTutorialActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ws/SkipOnboardingTutorialActionTest.java
index df97cd664de..620287f5534 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/ws/SkipOnboardingTutorialActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/ws/SkipOnboardingTutorialActionTest.java
@@ -23,7 +23,6 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.server.ws.WebService;
-import org.sonar.api.utils.internal.TestSystem2;
import org.sonar.db.DbTester;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.UnauthorizedException;
@@ -35,9 +34,6 @@ import static org.assertj.core.api.Assertions.assertThat;
public class SkipOnboardingTutorialActionTest {
- private final static long PAST = 100_000_000_000L;
- private final static long NOW = 500_000_000_000L;
-
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
@@ -47,36 +43,31 @@ public class SkipOnboardingTutorialActionTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
- private TestSystem2 system2 = new TestSystem2().setNow(NOW);
-
- private WsActionTester ws = new WsActionTester(new SkipOnboardingTutorialAction(userSession, db.getDbClient(), system2));
+ private WsActionTester ws = new WsActionTester(new SkipOnboardingTutorialAction(userSession, db.getDbClient()));
@Test
public void mark_user_as_onboarded() {
UserDto user = db.users().insertUser(u -> u
- .setOnboarded(false)
- .setUpdatedAt(PAST));
+ .setOnboarded(false));
userSession.logIn(user);
call();
UserDto userDto = selectUser(user.getLogin());
assertThat(userDto.isOnboarded()).isEqualTo(true);
- assertThat(userDto.getUpdatedAt()).isEqualTo(NOW);
}
@Test
public void does_nothing_if_user_already_onboarded() {
UserDto user = db.users().insertUser(u -> u
- .setOnboarded(true)
- .setUpdatedAt(PAST));
+ .setOnboarded(true));
userSession.logIn(user);
call();
UserDto userDto = selectUser(user.getLogin());
assertThat(userDto.isOnboarded()).isEqualTo(true);
- assertThat(userDto.getUpdatedAt()).isEqualTo(PAST);
+ assertThat(userDto.getUpdatedAt()).isEqualTo(user.getUpdatedAt());
}
@Test
@@ -112,7 +103,6 @@ public class SkipOnboardingTutorialActionTest {
@Test
public void test_definition() {
- WsActionTester ws = new WsActionTester(new SkipOnboardingTutorialAction(userSession, db.getDbClient(), system2));
WebService.Action def = ws.getDef();
assertThat(def.isPost()).isTrue();
assertThat(def.isInternal()).isTrue();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ws/UpdateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ws/UpdateActionTest.java
index 4747ce234a9..bce95d9b62c 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/ws/UpdateActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/ws/UpdateActionTest.java
@@ -75,7 +75,7 @@ public class UpdateActionTest {
dbTester.users().insertDefaultGroup(dbTester.getDefaultOrganization(), "sonar-users");
userIndexer = new UserIndexer(dbClient, esTester.client());
tester = new WsTester(new UsersWs(new UpdateAction(
- new UserUpdater(mock(NewUserNotifier.class), dbClient, userIndexer, system2, organizationFlags, defaultOrganizationProvider, ORGANIZATION_CREATION_NOT_USED_FOR_UPDATE,
+ new UserUpdater(mock(NewUserNotifier.class), dbClient, userIndexer, organizationFlags, defaultOrganizationProvider, ORGANIZATION_CREATION_NOT_USED_FOR_UPDATE,
new DefaultGroupFinder(dbTester.getDbClient()), settings.asConfig()),
userSessionRule,
new UserJsonWriter(userSessionRule), dbClient)));
@@ -226,7 +226,6 @@ public class UpdateActionTest {
.setExternalIdentity("jo")
.setExternalIdentityProvider("sonarqube");
dbClient.userDao().insert(session, userDto);
- session.commit();
- userIndexer.index(userDto.getLogin());
+ userIndexer.commitAndIndex(session, userDto);
}
}