*/
package org.sonar.server.computation.issue;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Collections2;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.sonar.server.user.index.UserDoc;
import org.sonar.server.user.index.UserIndex;
import org.sonar.server.util.cache.CacheLoader;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
/**
* Loads the association between a SCM account and a SQ user
*/
-public class ScmAccountCacheLoader implements CacheLoader<String,String> {
+public class ScmAccountCacheLoader implements CacheLoader<String, String> {
+ private final Logger log;
private final UserIndex index;
public ScmAccountCacheLoader(UserIndex index) {
+ this(index, LoggerFactory.getLogger(ScmAccountCacheLoader.class));
+ }
+
+ public ScmAccountCacheLoader(UserIndex index, Logger log) {
+ this.log = log;
this.index = index;
}
@Override
public String load(String scmAccount) {
- UserDoc user = index.getNullableByScmAccount(scmAccount);
- return user != null ? user.login() : null;
+ List<UserDoc> users = index.getAtMostThreeUsersForScmAccount(scmAccount);
+ if (users.isEmpty()) {
+ return null;
+ }
+ if (users.size() == 1) {
+ return users.get(0).login();
+ }
+ Collection<String> logins = Collections2.transform(users, new Function<UserDoc, String>() {
+ @Override
+ public String apply(UserDoc input) {
+ return input.login();
+ }
+ });
+ log.warn(String.format("Multiple users share the SCM account '%s': %s", scmAccount, Joiner.on(", ").join(logins)));
+ return null;
}
@Override
import javax.annotation.CheckForNull;
+import java.util.ArrayList;
+import java.util.List;
+
public class UserIndex implements ServerComponent {
private final EsClient esClient;
return userDoc;
}
+ /**
+ * Returns the users (at most 3) who are associated to the given SCM account. This method can be used
+ * to detect user conflicts. It never returns null.
+ */
+ public List<UserDoc> getAtMostThreeUsersForScmAccount(String scmAccount) {
+ List<UserDoc> result = new ArrayList<>();
+ if (!StringUtils.isEmpty(scmAccount)) {
+ SearchRequestBuilder request = esClient.prepareSearch(UserIndexDefinition.INDEX)
+ .setTypes(UserIndexDefinition.TYPE_USER)
+ .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(),
+ FilterBuilders.boolFilter()
+ .should(FilterBuilders.termFilter(UserIndexDefinition.FIELD_LOGIN, scmAccount))
+ .should(FilterBuilders.termFilter(UserIndexDefinition.FIELD_EMAIL, scmAccount))
+ .should(FilterBuilders.termFilter(UserIndexDefinition.FIELD_SCM_ACCOUNTS, scmAccount))))
+ .setSize(3);
+ for (SearchHit hit : request.get().getHits().getHits()) {
+ result.add(new UserDoc(hit.sourceAsMap()));
+ }
+ }
+ return result;
+ }
+
}
import org.junit.Rule;
import org.junit.Test;
+import org.slf4j.Logger;
import org.sonar.api.config.Settings;
import org.sonar.server.es.EsTester;
import org.sonar.server.user.index.UserIndex;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
public class ScmAccountCacheLoaderTest {
@Test
public void load_login_for_scm_account() throws Exception {
- esTester.putDocuments("users", "user", getClass(), "user1.json");
-
+ esTester.putDocuments("users", "user", getClass(), "charlie.json");
UserIndex index = new UserIndex(esTester.client());
ScmAccountCacheLoader loader = new ScmAccountCacheLoader(index);
assertThat(loader.load("jesuis@charlie.com")).isEqualTo("charlie");
}
+ @Test
+ public void warn_if_multiple_users_share_same_scm_account() throws Exception {
+ esTester.putDocuments("users", "user", getClass(), "charlie.json", "charlie_conflict.json");
+ UserIndex index = new UserIndex(esTester.client());
+ Logger log = mock(Logger.class);
+ ScmAccountCacheLoader loader = new ScmAccountCacheLoader(index, log);
+
+ assertThat(loader.load("charlie")).isNull();
+ verify(log).warn("Multiple users share the SCM account 'charlie': charlie, another.charlie");
+ }
+
@Test
public void load_by_multiple_scm_accounts_is_not_supported_yet() throws Exception {
UserIndex index = new UserIndex(esTester.client());
assertThat(index.getNullableByScmAccount("user1@mail.com")).isNull();
}
+ @Test
+ public void get_users_for_scm_account() throws Exception {
+ esTester.putDocuments(UserIndexDefinition.INDEX, UserIndexDefinition.TYPE_USER, this.getClass(), "user1.json", "user3-with-same-email-as-user1.json");
+
+ assertThat(index.getAtMostThreeUsersForScmAccount("user_1")).extractingResultOf("login").containsOnly("user1");
+ assertThat(index.getAtMostThreeUsersForScmAccount("user1")).extractingResultOf("login").containsOnly("user1");
+
+ // both users share the same email
+ assertThat(index.getAtMostThreeUsersForScmAccount("user1@mail.com")).extractingResultOf("login").containsOnly("user1", "user3");
+
+ assertThat(index.getAtMostThreeUsersForScmAccount("")).isEmpty();
+ assertThat(index.getAtMostThreeUsersForScmAccount("unknown")).isEmpty();
+ }
}
--- /dev/null
+{
+ "login": "charlie",
+ "name": "Charlie",
+ "email": "charlie@hebdo.com",
+ "active": true,
+ "scmAccounts": ["charlie", "jesuis@charlie.com"],
+ "createdAt": 1500000000000,
+ "updatedAt": 1500000000000
+}
--- /dev/null
+{
+ "login": "another.charlie",
+ "name": "Another Charlie",
+ "active": true,
+ "scmAccounts": ["charlie"],
+ "createdAt": 1500000000000,
+ "updatedAt": 1500000000000
+}
+++ /dev/null
-{
- "login": "charlie",
- "name": "Charlie",
- "email": "charlie@hebdo.com",
- "active": true,
- "scmAccounts": ["charlie", "jesuis@charlie.com"],
- "createdAt": 1500000000000,
- "updatedAt": 1500000000000
-}