Merge changes I62cb98f6,I1c5576b4,I61ae5780
* changes: Make ExternalIds and ExternalIdCache return immutable collections Implement cache for DefaultPreferences Serialize AccountCache
This commit is contained in:
@@ -840,6 +840,12 @@ for the client to use as potential delta bases. Push over smart HTTP
|
||||
requires two HTTP requests, and this cache tries to carry state from
|
||||
the first request into the second to ensure it can complete.
|
||||
|
||||
cache `"default_preferences"`::
|
||||
+
|
||||
Caches the server's default general, edit and diff preferences.
|
||||
+
|
||||
Default value is 1 to hold only the most current version in-memory.
|
||||
|
||||
cache `"changes"`::
|
||||
+
|
||||
The size of `memoryLimit` determines the number of projects for which
|
||||
|
||||
@@ -380,8 +380,7 @@ public abstract class AbstractDaemonTest {
|
||||
initSsh();
|
||||
}
|
||||
|
||||
protected void evictAndReindexAccount(Account.Id accountId) {
|
||||
accountCache.evict(accountId);
|
||||
protected void reindexAccount(Account.Id accountId) {
|
||||
accountIndexer.index(accountId);
|
||||
}
|
||||
|
||||
@@ -436,8 +435,8 @@ public abstract class AbstractDaemonTest {
|
||||
user = accountCreator.user();
|
||||
|
||||
// Evict and reindex accounts in case tests modify them.
|
||||
evictAndReindexAccount(admin.id());
|
||||
evictAndReindexAccount(user.id());
|
||||
reindexAccount(admin.id());
|
||||
reindexAccount(user.id());
|
||||
|
||||
adminRestSession = new RestSession(server, admin);
|
||||
userRestSession = new RestSession(server, user);
|
||||
|
||||
@@ -339,8 +339,8 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
|
||||
return description.getClassName();
|
||||
}
|
||||
|
||||
private TestAccount evictAndCopy(TestAccount account) {
|
||||
evictAndReindexAccount(account.id());
|
||||
private TestAccount reindexAndCopy(TestAccount account) {
|
||||
reindexAccount(account.id());
|
||||
return account;
|
||||
}
|
||||
|
||||
@@ -348,14 +348,14 @@ public abstract class AbstractNotificationTest extends AbstractDaemonTest {
|
||||
synchronized (stagedUsers) {
|
||||
if (stagedUsers.containsKey(usersCacheKey())) {
|
||||
StagedUsers existing = stagedUsers.get(usersCacheKey());
|
||||
owner = evictAndCopy(existing.owner);
|
||||
author = evictAndCopy(existing.author);
|
||||
uploader = evictAndCopy(existing.uploader);
|
||||
reviewer = evictAndCopy(existing.reviewer);
|
||||
ccer = evictAndCopy(existing.ccer);
|
||||
starrer = evictAndCopy(existing.starrer);
|
||||
assignee = evictAndCopy(existing.assignee);
|
||||
watchingProjectOwner = evictAndCopy(existing.watchingProjectOwner);
|
||||
owner = reindexAndCopy(existing.owner);
|
||||
author = reindexAndCopy(existing.author);
|
||||
uploader = reindexAndCopy(existing.uploader);
|
||||
reviewer = reindexAndCopy(existing.reviewer);
|
||||
ccer = reindexAndCopy(existing.ccer);
|
||||
starrer = reindexAndCopy(existing.starrer);
|
||||
assignee = reindexAndCopy(existing.assignee);
|
||||
watchingProjectOwner = reindexAndCopy(existing.watchingProjectOwner);
|
||||
watchers.putAll(existing.watchers);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -335,18 +335,18 @@ public class ProjectResetter implements AutoCloseable {
|
||||
// Make sure all accounts are evicted and reindexed.
|
||||
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||
for (Account.Id id : accountIds(repo)) {
|
||||
evictAndReindexAccount(id);
|
||||
reindexAccount(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove deleted accounts from the cache and index.
|
||||
for (Account.Id id : deletedAccounts) {
|
||||
evictAndReindexAccount(id);
|
||||
reindexAccount(id);
|
||||
}
|
||||
} else {
|
||||
// Evict and reindex all modified and deleted accounts.
|
||||
for (Account.Id id : Sets.union(modifiedAccounts, deletedAccounts)) {
|
||||
evictAndReindexAccount(id);
|
||||
reindexAccount(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -367,10 +367,7 @@ public class ProjectResetter implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
private void evictAndReindexAccount(Account.Id accountId) {
|
||||
if (accountCache != null) {
|
||||
accountCache.evict(accountId);
|
||||
}
|
||||
private void reindexAccount(Account.Id accountId) {
|
||||
if (groupIncludeCache != null) {
|
||||
groupIncludeCache.evictGroupsWithMember(accountId);
|
||||
}
|
||||
|
||||
@@ -19,15 +19,10 @@ import static com.google.gerrit.entities.RefNames.REFS_STARRED_CHANGES;
|
||||
import static com.google.gerrit.entities.RefNames.REFS_USERS;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
||||
import com.google.gerrit.proto.Protos;
|
||||
import com.google.gerrit.server.cache.proto.Cache.AccountProto;
|
||||
import com.google.gerrit.server.cache.serialize.CacheSerializer;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@@ -125,42 +120,6 @@ public abstract class Account {
|
||||
}
|
||||
}
|
||||
|
||||
enum Serializer implements CacheSerializer<Account> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public byte[] serialize(Account account) {
|
||||
// We don't care about the difference of empty strings and null in the Account entity.
|
||||
AccountProto.Builder proto =
|
||||
AccountProto.newBuilder()
|
||||
.setId(account.id().get())
|
||||
.setRegisteredOn(account.registeredOn().toInstant().toEpochMilli())
|
||||
.setInactive(account.inactive())
|
||||
.setFullName(Strings.nullToEmpty(account.fullName()))
|
||||
.setDisplayName(Strings.nullToEmpty(account.displayName()))
|
||||
.setPreferredEmail(Strings.nullToEmpty(account.preferredEmail()))
|
||||
.setStatus(Strings.nullToEmpty(account.status()))
|
||||
.setMetaId(Strings.nullToEmpty(account.metaId()));
|
||||
return Protos.toByteArray(proto.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account deserialize(byte[] in) {
|
||||
// We don't care about the difference of empty strings and null in the Account entity.
|
||||
AccountProto proto = Protos.parseUnchecked(AccountProto.parser(), in);
|
||||
return Account.builder(
|
||||
Account.id(proto.getId()),
|
||||
Timestamp.from(Instant.ofEpochMilli(proto.getRegisteredOn())))
|
||||
.setFullName(Strings.emptyToNull(proto.getFullName()))
|
||||
.setDisplayName(Strings.emptyToNull(proto.getDisplayName()))
|
||||
.setPreferredEmail(Strings.emptyToNull(proto.getPreferredEmail()))
|
||||
.setInactive(proto.getInactive())
|
||||
.setStatus(Strings.emptyToNull(proto.getStatus()))
|
||||
.setMetaId(Strings.emptyToNull(proto.getMetaId()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Id id();
|
||||
|
||||
/** Date and time the user registered with the review server. */
|
||||
|
||||
@@ -48,6 +48,7 @@ import com.google.gerrit.server.change.RebaseChangeOp;
|
||||
import com.google.gerrit.server.config.AdministrateServerGroups;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrlProvider;
|
||||
import com.google.gerrit.server.config.DefaultPreferencesCacheImpl;
|
||||
import com.google.gerrit.server.config.DefaultUrlFormatter;
|
||||
import com.google.gerrit.server.config.EnableReverseDnsLookup;
|
||||
import com.google.gerrit.server.config.EnableReverseDnsLookupProvider;
|
||||
@@ -155,6 +156,7 @@ public class BatchProgramModule extends FactoryModule {
|
||||
install(new GroupModule());
|
||||
install(new NoteDbModule());
|
||||
install(AccountCacheImpl.module());
|
||||
install(DefaultPreferencesCacheImpl.module());
|
||||
install(GroupCacheImpl.module());
|
||||
install(GroupIncludeCacheImpl.module());
|
||||
install(ProjectCacheImpl.module());
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package com.google.gerrit.server.account;
|
||||
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.entities.Account;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -73,14 +72,4 @@ public interface AccountCache {
|
||||
* exists or if loading the external ID fails {@link Optional#empty()} is returned
|
||||
*/
|
||||
Optional<AccountState> getByUsername(String username);
|
||||
|
||||
/**
|
||||
* Evicts the account from the cache.
|
||||
*
|
||||
* @param accountId account ID of the account that should be evicted
|
||||
*/
|
||||
void evict(@Nullable Account.Id accountId);
|
||||
|
||||
/** Evict all accounts from the cache. */
|
||||
void evictAll();
|
||||
}
|
||||
|
||||
@@ -20,12 +20,16 @@ import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.entities.Account;
|
||||
import com.google.gerrit.server.FanOutExecutor;
|
||||
import com.google.gerrit.entities.RefNames;
|
||||
import com.google.gerrit.exceptions.StorageException;
|
||||
import com.google.gerrit.server.account.externalids.ExternalId;
|
||||
import com.google.gerrit.server.account.externalids.ExternalIds;
|
||||
import com.google.gerrit.server.cache.CacheModule;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.config.CachedPreferences;
|
||||
import com.google.gerrit.server.config.DefaultPreferencesCache;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.logging.Metadata;
|
||||
import com.google.gerrit.server.logging.TraceContext;
|
||||
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
|
||||
@@ -33,34 +37,33 @@ import com.google.gerrit.server.util.time.TimeUtil;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Named;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
/** Caches important (but small) account state to avoid database hits. */
|
||||
@Singleton
|
||||
public class AccountCacheImpl implements AccountCache {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final String BYID_NAME = "accounts";
|
||||
private static final String BYID_AND_REV_NAME = "accounts";
|
||||
|
||||
public static Module module() {
|
||||
return new CacheModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
cache(BYID_NAME, Account.Id.class, new TypeLiteral<AccountState>() {})
|
||||
.loader(ByIdLoader.class);
|
||||
persist(BYID_AND_REV_NAME, CachedAccountDetails.Key.class, CachedAccountDetails.class)
|
||||
.version(1)
|
||||
.keySerializer(CachedAccountDetails.Key.Serializer.INSTANCE)
|
||||
.valueSerializer(CachedAccountDetails.Serializer.INSTANCE)
|
||||
.loader(Loader.class);
|
||||
|
||||
bind(AccountCacheImpl.class);
|
||||
bind(AccountCache.class).to(AccountCacheImpl.class);
|
||||
@@ -69,76 +72,67 @@ public class AccountCacheImpl implements AccountCache {
|
||||
}
|
||||
|
||||
private final ExternalIds externalIds;
|
||||
private final LoadingCache<Account.Id, AccountState> byId;
|
||||
private final ExecutorService executor;
|
||||
private final LoadingCache<CachedAccountDetails.Key, CachedAccountDetails> accountDetailsCache;
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final AllUsersName allUsersName;
|
||||
private final DefaultPreferencesCache defaultPreferenceCache;
|
||||
|
||||
@Inject
|
||||
AccountCacheImpl(
|
||||
ExternalIds externalIds,
|
||||
@Named(BYID_NAME) LoadingCache<Account.Id, AccountState> byId,
|
||||
@FanOutExecutor ExecutorService executor) {
|
||||
@Named(BYID_AND_REV_NAME)
|
||||
LoadingCache<CachedAccountDetails.Key, CachedAccountDetails> accountDetailsCache,
|
||||
GitRepositoryManager repoManager,
|
||||
AllUsersName allUsersName,
|
||||
DefaultPreferencesCache defaultPreferenceCache) {
|
||||
this.externalIds = externalIds;
|
||||
this.byId = byId;
|
||||
this.executor = executor;
|
||||
this.accountDetailsCache = accountDetailsCache;
|
||||
this.repoManager = repoManager;
|
||||
this.allUsersName = allUsersName;
|
||||
this.defaultPreferenceCache = defaultPreferenceCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccountState getEvenIfMissing(Account.Id accountId) {
|
||||
try {
|
||||
return byId.get(accountId);
|
||||
} catch (ExecutionException e) {
|
||||
if (!(e.getCause() instanceof AccountNotFoundException)) {
|
||||
logger.atWarning().withCause(e).log("Cannot load AccountState for %s", accountId);
|
||||
}
|
||||
return missing(accountId);
|
||||
}
|
||||
return get(accountId).orElse(missing(accountId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<AccountState> get(Account.Id accountId) {
|
||||
try {
|
||||
return Optional.ofNullable(byId.get(accountId));
|
||||
} catch (ExecutionException e) {
|
||||
if (!(e.getCause() instanceof AccountNotFoundException)) {
|
||||
logger.atWarning().withCause(e).log("Cannot load AccountState for %s", accountId);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.ofNullable(get(Collections.singleton(accountId)).getOrDefault(accountId, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Account.Id, AccountState> get(Set<Account.Id> accountIds) {
|
||||
Map<Account.Id, AccountState> accountStates = new HashMap<>(accountIds.size());
|
||||
List<Callable<Optional<AccountState>>> callables = new ArrayList<>();
|
||||
for (Account.Id accountId : accountIds) {
|
||||
AccountState state = byId.getIfPresent(accountId);
|
||||
if (state != null) {
|
||||
// The value is in-memory, so we just get the state
|
||||
accountStates.put(accountId, state);
|
||||
} else {
|
||||
// Queue up a callable so that we can load accounts in parallel
|
||||
callables.add(() -> get(accountId));
|
||||
}
|
||||
}
|
||||
if (callables.isEmpty()) {
|
||||
return accountStates;
|
||||
try {
|
||||
try (Repository allUsers = repoManager.openRepository(allUsersName)) {
|
||||
// Get the default preferences for this Gerrit host
|
||||
Ref ref = allUsers.exactRef(RefNames.REFS_USERS_DEFAULT);
|
||||
CachedPreferences defaultPreferences =
|
||||
ref != null
|
||||
? defaultPreferenceCache.get(ref.getObjectId())
|
||||
: DefaultPreferencesCache.EMPTY;
|
||||
|
||||
ImmutableMap.Builder<Account.Id, AccountState> result = ImmutableMap.builder();
|
||||
for (Account.Id id : accountIds) {
|
||||
Ref userRef = allUsers.exactRef(RefNames.refsUsers(id));
|
||||
if (userRef == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Future<Optional<AccountState>>> futures;
|
||||
try {
|
||||
futures = executor.invokeAll(callables);
|
||||
} catch (InterruptedException e) {
|
||||
logger.atSevere().withCause(e).log("Cannot load AccountStates");
|
||||
return ImmutableMap.of();
|
||||
result.put(
|
||||
id,
|
||||
AccountState.forCachedAccount(
|
||||
accountDetailsCache.get(
|
||||
CachedAccountDetails.Key.create(id, userRef.getObjectId())),
|
||||
defaultPreferences,
|
||||
externalIds));
|
||||
}
|
||||
for (Future<Optional<AccountState>> f : futures) {
|
||||
try {
|
||||
f.get().ifPresent(s -> accountStates.put(s.account().id(), s));
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
logger.atSevere().withCause(e).log("Cannot load AccountState");
|
||||
return result.build();
|
||||
}
|
||||
} catch (IOException | ExecutionException e) {
|
||||
throw new StorageException(e);
|
||||
}
|
||||
return accountStates;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -154,42 +148,35 @@ public class AccountCacheImpl implements AccountCache {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evict(@Nullable Account.Id accountId) {
|
||||
if (accountId != null) {
|
||||
logger.atFine().log("Evict account %d", accountId.get());
|
||||
byId.invalidate(accountId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictAll() {
|
||||
logger.atFine().log("Evict all accounts");
|
||||
byId.invalidateAll();
|
||||
}
|
||||
|
||||
private AccountState missing(Account.Id accountId) {
|
||||
Account.Builder account = Account.builder(accountId, TimeUtil.nowTs());
|
||||
account.setActive(false);
|
||||
return AccountState.forAccount(account.build());
|
||||
}
|
||||
|
||||
static class ByIdLoader extends CacheLoader<Account.Id, AccountState> {
|
||||
private final Accounts accounts;
|
||||
@Singleton
|
||||
static class Loader extends CacheLoader<CachedAccountDetails.Key, CachedAccountDetails> {
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final AllUsersName allUsersName;
|
||||
|
||||
@Inject
|
||||
ByIdLoader(Accounts accounts) {
|
||||
this.accounts = accounts;
|
||||
Loader(GitRepositoryManager repoManager, AllUsersName allUsersName) {
|
||||
this.repoManager = repoManager;
|
||||
this.allUsersName = allUsersName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccountState load(Account.Id who) throws Exception {
|
||||
try (TraceTimer timer =
|
||||
public CachedAccountDetails load(CachedAccountDetails.Key key) throws Exception {
|
||||
try (TraceTimer ignored =
|
||||
TraceContext.newTimer(
|
||||
"Loading account", Metadata.builder().accountId(who.get()).build())) {
|
||||
return accounts
|
||||
.get(who)
|
||||
.orElseThrow(() -> new AccountNotFoundException(who + " not found"));
|
||||
"Loading account", Metadata.builder().accountId(key.accountId().get()).build());
|
||||
Repository repo = repoManager.openRepository(allUsersName)) {
|
||||
AccountConfig cfg = new AccountConfig(key.accountId(), allUsersName, repo).load(key.id());
|
||||
Account account =
|
||||
cfg.getLoadedAccount()
|
||||
.orElseThrow(() -> new AccountNotFoundException(key.accountId() + " not found"));
|
||||
return CachedAccountDetails.create(
|
||||
account, cfg.getProjectWatches(), cfg.asCachedPreferences());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,11 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gerrit.entities.Account;
|
||||
import com.google.gerrit.entities.RefNames;
|
||||
import com.google.gerrit.exceptions.DuplicateKeyException;
|
||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.EditPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
|
||||
import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
|
||||
import com.google.gerrit.server.account.externalids.ExternalIds;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.config.CachedPreferences;
|
||||
import com.google.gerrit.server.git.ValidationError;
|
||||
import com.google.gerrit.server.git.meta.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.meta.VersionedMetaData;
|
||||
@@ -106,6 +104,11 @@ public class AccountConfig extends VersionedMetaData implements ValidationError.
|
||||
return this;
|
||||
}
|
||||
|
||||
public AccountConfig load(ObjectId rev) throws IOException, ConfigInvalidException {
|
||||
load(allUsersName, repo, rev);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the loaded account.
|
||||
*
|
||||
@@ -142,36 +145,6 @@ public class AccountConfig extends VersionedMetaData implements ValidationError.
|
||||
return projectWatches.getProjectWatches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the general preferences of the loaded account.
|
||||
*
|
||||
* @return the general preferences of the loaded account
|
||||
*/
|
||||
public GeneralPreferencesInfo getGeneralPreferences() {
|
||||
checkLoaded();
|
||||
return preferences.getGeneralPreferences();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the diff preferences of the loaded account.
|
||||
*
|
||||
* @return the diff preferences of the loaded account
|
||||
*/
|
||||
public DiffPreferencesInfo getDiffPreferences() {
|
||||
checkLoaded();
|
||||
return preferences.getDiffPreferences();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the edit preferences of the loaded account.
|
||||
*
|
||||
* @return the edit preferences of the loaded account
|
||||
*/
|
||||
public EditPreferencesInfo getEditPreferences() {
|
||||
checkLoaded();
|
||||
return preferences.getEditPreferences();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the account. This means the loaded account will be overwritten with the given account.
|
||||
*
|
||||
@@ -228,6 +201,15 @@ public class AccountConfig extends VersionedMetaData implements ValidationError.
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content of the {@code preferences.config} file wrapped as {@link
|
||||
* CachedPreferences}.
|
||||
*/
|
||||
CachedPreferences asCachedPreferences() {
|
||||
checkLoaded();
|
||||
return CachedPreferences.fromConfig(preferences.getRaw());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLoad() throws IOException, ConfigInvalidException {
|
||||
if (revision != null) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
|
||||
import com.google.gerrit.server.account.externalids.ExternalId;
|
||||
import com.google.gerrit.server.account.externalids.ExternalIdNotes;
|
||||
import com.google.gerrit.server.account.externalids.ExternalIds;
|
||||
import com.google.gerrit.server.config.CachedPreferences;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
@@ -47,12 +48,14 @@ public abstract class AccountState {
|
||||
*
|
||||
* @param externalIds class to access external IDs
|
||||
* @param accountConfig the account config, must already be loaded
|
||||
* @param defaultPreferences the default preferences for this Gerrit installation
|
||||
* @return the account state, {@link Optional#empty()} if the account doesn't exist
|
||||
* @throws IOException if accessing the external IDs fails
|
||||
*/
|
||||
public static Optional<AccountState> fromAccountConfig(
|
||||
ExternalIds externalIds, AccountConfig accountConfig) throws IOException {
|
||||
return fromAccountConfig(externalIds, accountConfig, null);
|
||||
ExternalIds externalIds, AccountConfig accountConfig, CachedPreferences defaultPreferences)
|
||||
throws IOException {
|
||||
return fromAccountConfig(externalIds, accountConfig, null, defaultPreferences);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,11 +71,15 @@ public abstract class AccountState {
|
||||
* @param externalIds class to access external IDs
|
||||
* @param accountConfig the account config, must already be loaded
|
||||
* @param extIdNotes external ID notes, must already be loaded, may be {@code null}
|
||||
* @param defaultPreferences the default preferences for this Gerrit installation
|
||||
* @return the account state, {@link Optional#empty()} if the account doesn't exist
|
||||
* @throws IOException if accessing the external IDs fails
|
||||
*/
|
||||
public static Optional<AccountState> fromAccountConfig(
|
||||
ExternalIds externalIds, AccountConfig accountConfig, @Nullable ExternalIdNotes extIdNotes)
|
||||
ExternalIds externalIds,
|
||||
AccountConfig accountConfig,
|
||||
@Nullable ExternalIdNotes extIdNotes,
|
||||
CachedPreferences defaultPreferences)
|
||||
throws IOException {
|
||||
if (!accountConfig.getLoadedAccount().isPresent()) {
|
||||
return Optional.empty();
|
||||
@@ -85,19 +92,13 @@ public abstract class AccountState {
|
||||
: accountConfig.getExternalIdsRev();
|
||||
ImmutableSet<ExternalId> extIds =
|
||||
extIdsRev.isPresent()
|
||||
? ImmutableSet.copyOf(externalIds.byAccount(account.id(), extIdsRev.get()))
|
||||
? externalIds.byAccount(account.id(), extIdsRev.get())
|
||||
: ImmutableSet.of();
|
||||
|
||||
// Don't leak references to AccountConfig into the AccountState, since it holds a reference to
|
||||
// an open Repository instance.
|
||||
ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches =
|
||||
accountConfig.getProjectWatches();
|
||||
Preferences.General generalPreferences =
|
||||
Preferences.General.fromInfo(accountConfig.getGeneralPreferences());
|
||||
Preferences.Diff diffPreferences =
|
||||
Preferences.Diff.fromInfo(accountConfig.getDiffPreferences());
|
||||
Preferences.Edit editPreferences =
|
||||
Preferences.Edit.fromInfo(accountConfig.getEditPreferences());
|
||||
|
||||
return Optional.of(
|
||||
new AutoValue_AccountState(
|
||||
@@ -105,9 +106,8 @@ public abstract class AccountState {
|
||||
extIds,
|
||||
ExternalId.getUserName(extIds),
|
||||
projectWatches,
|
||||
generalPreferences,
|
||||
diffPreferences,
|
||||
editPreferences));
|
||||
Optional.of(defaultPreferences),
|
||||
Optional.of(accountConfig.asCachedPreferences())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,6 +121,25 @@ public abstract class AccountState {
|
||||
return forAccount(account, ImmutableSet.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AccountState for a given account and external IDs.
|
||||
*
|
||||
* @param account the account
|
||||
* @return the account state
|
||||
*/
|
||||
public static AccountState forCachedAccount(
|
||||
CachedAccountDetails account, CachedPreferences defaultConfig, ExternalIds externalIds)
|
||||
throws IOException {
|
||||
ImmutableSet<ExternalId> extIds = externalIds.byAccount(account.account().id());
|
||||
return new AutoValue_AccountState(
|
||||
account.account(),
|
||||
extIds,
|
||||
ExternalId.getUserName(extIds),
|
||||
account.projectWatches(),
|
||||
Optional.of(defaultConfig),
|
||||
Optional.of(account.preferences()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AccountState for a given account with no project watches and default preferences.
|
||||
*
|
||||
@@ -134,9 +153,8 @@ public abstract class AccountState {
|
||||
ImmutableSet.copyOf(extIds),
|
||||
ExternalId.getUserName(extIds),
|
||||
ImmutableMap.of(),
|
||||
Preferences.General.fromInfo(GeneralPreferencesInfo.defaults()),
|
||||
Preferences.Diff.fromInfo(DiffPreferencesInfo.defaults()),
|
||||
Preferences.Edit.fromInfo(EditPreferencesInfo.defaults()));
|
||||
Optional.empty(),
|
||||
Optional.empty());
|
||||
}
|
||||
|
||||
/** Get the cached account metadata. */
|
||||
@@ -158,17 +176,20 @@ public abstract class AccountState {
|
||||
|
||||
/** The general preferences of the account. */
|
||||
public GeneralPreferencesInfo generalPreferences() {
|
||||
return immutableGeneralPreferences().toInfo();
|
||||
return CachedPreferences.general(
|
||||
defaultPreferences(), userPreferences().orElse(CachedPreferences.EMPTY));
|
||||
}
|
||||
|
||||
/** The diff preferences of the account. */
|
||||
public DiffPreferencesInfo diffPreferences() {
|
||||
return immutableDiffPreferences().toInfo();
|
||||
return CachedPreferences.diff(
|
||||
defaultPreferences(), userPreferences().orElse(CachedPreferences.EMPTY));
|
||||
}
|
||||
|
||||
/** The edit preferences of the account. */
|
||||
public EditPreferencesInfo editPreferences() {
|
||||
return immutableEditPreferences().toInfo();
|
||||
return CachedPreferences.edit(
|
||||
defaultPreferences(), userPreferences().orElse(CachedPreferences.EMPTY));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -178,9 +199,9 @@ public abstract class AccountState {
|
||||
return h.toString();
|
||||
}
|
||||
|
||||
protected abstract Preferences.General immutableGeneralPreferences();
|
||||
/** Gerrit's default preferences as stored in {@code preferences.config}. */
|
||||
protected abstract Optional<CachedPreferences> defaultPreferences();
|
||||
|
||||
protected abstract Preferences.Diff immutableDiffPreferences();
|
||||
|
||||
protected abstract Preferences.Edit immutableEditPreferences();
|
||||
/** User preferences as stored in {@code preferences.config}. */
|
||||
protected abstract Optional<CachedPreferences> userPreferences();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import com.google.gerrit.metrics.MetricMaker;
|
||||
import com.google.gerrit.metrics.Timer0;
|
||||
import com.google.gerrit.server.account.externalids.ExternalIds;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.config.CachedPreferences;
|
||||
import com.google.gerrit.server.config.VersionedDefaultPreferences;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
@@ -148,10 +150,15 @@ public class Accounts {
|
||||
private Optional<AccountState> read(Repository allUsersRepository, Account.Id accountId)
|
||||
throws IOException, ConfigInvalidException {
|
||||
AccountConfig cfg;
|
||||
CachedPreferences defaultPreferences;
|
||||
try (Timer0.Context ignored = readSingleLatency.start()) {
|
||||
cfg = new AccountConfig(accountId, allUsersName, allUsersRepository).load();
|
||||
defaultPreferences =
|
||||
CachedPreferences.fromConfig(
|
||||
VersionedDefaultPreferences.get(allUsersRepository, allUsersName));
|
||||
}
|
||||
return AccountState.fromAccountConfig(externalIds, cfg);
|
||||
|
||||
return AccountState.fromAccountConfig(externalIds, cfg, defaultPreferences);
|
||||
}
|
||||
|
||||
public static Stream<Account.Id> readUserRefs(Repository repo) throws IOException {
|
||||
|
||||
@@ -35,6 +35,8 @@ import com.google.gerrit.server.account.externalids.ExternalIdNotes;
|
||||
import com.google.gerrit.server.account.externalids.ExternalIdNotes.ExternalIdNotesLoader;
|
||||
import com.google.gerrit.server.account.externalids.ExternalIds;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.config.CachedPreferences;
|
||||
import com.google.gerrit.server.config.VersionedDefaultPreferences;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.meta.MetaDataUpdate;
|
||||
@@ -328,8 +330,12 @@ public class AccountsUpdate {
|
||||
accountConfig.setAccountUpdate(update);
|
||||
ExternalIdNotes extIdNotes =
|
||||
createExternalIdNotes(r, accountConfig.getExternalIdsRev(), accountId, update);
|
||||
CachedPreferences defaultPreferences =
|
||||
CachedPreferences.fromConfig(VersionedDefaultPreferences.get(r, allUsersName));
|
||||
|
||||
UpdatedAccount updatedAccounts =
|
||||
new UpdatedAccount(externalIds, message, accountConfig, extIdNotes);
|
||||
new UpdatedAccount(
|
||||
externalIds, message, accountConfig, extIdNotes, defaultPreferences);
|
||||
updatedAccounts.setCreated(true);
|
||||
return updatedAccounts;
|
||||
})
|
||||
@@ -375,8 +381,10 @@ public class AccountsUpdate {
|
||||
return updateAccount(
|
||||
r -> {
|
||||
AccountConfig accountConfig = read(r, accountId);
|
||||
CachedPreferences defaultPreferences =
|
||||
CachedPreferences.fromConfig(VersionedDefaultPreferences.get(r, allUsersName));
|
||||
Optional<AccountState> account =
|
||||
AccountState.fromAccountConfig(externalIds, accountConfig);
|
||||
AccountState.fromAccountConfig(externalIds, accountConfig, defaultPreferences);
|
||||
if (!account.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
@@ -388,8 +396,12 @@ public class AccountsUpdate {
|
||||
accountConfig.setAccountUpdate(update);
|
||||
ExternalIdNotes extIdNotes =
|
||||
createExternalIdNotes(r, accountConfig.getExternalIdsRev(), accountId, update);
|
||||
CachedPreferences cachedDefaultPreferences =
|
||||
CachedPreferences.fromConfig(VersionedDefaultPreferences.get(r, allUsersName));
|
||||
|
||||
UpdatedAccount updatedAccounts =
|
||||
new UpdatedAccount(externalIds, message, accountConfig, extIdNotes);
|
||||
new UpdatedAccount(
|
||||
externalIds, message, accountConfig, extIdNotes, cachedDefaultPreferences);
|
||||
return updatedAccounts;
|
||||
});
|
||||
}
|
||||
@@ -563,6 +575,7 @@ public class AccountsUpdate {
|
||||
private final String message;
|
||||
private final AccountConfig accountConfig;
|
||||
private final ExternalIdNotes extIdNotes;
|
||||
private final CachedPreferences defaultPreferences;
|
||||
|
||||
private boolean created;
|
||||
|
||||
@@ -570,12 +583,14 @@ public class AccountsUpdate {
|
||||
ExternalIds externalIds,
|
||||
String message,
|
||||
AccountConfig accountConfig,
|
||||
ExternalIdNotes extIdNotes) {
|
||||
ExternalIdNotes extIdNotes,
|
||||
CachedPreferences defaultPreferences) {
|
||||
checkState(!Strings.isNullOrEmpty(message), "message for account update must be set");
|
||||
this.externalIds = requireNonNull(externalIds);
|
||||
this.message = requireNonNull(message);
|
||||
this.accountConfig = requireNonNull(accountConfig);
|
||||
this.extIdNotes = requireNonNull(extIdNotes);
|
||||
this.defaultPreferences = defaultPreferences;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
@@ -587,7 +602,9 @@ public class AccountsUpdate {
|
||||
}
|
||||
|
||||
public AccountState getAccount() throws IOException {
|
||||
return AccountState.fromAccountConfig(externalIds, accountConfig, extIdNotes).get();
|
||||
return AccountState.fromAccountConfig(
|
||||
externalIds, accountConfig, extIdNotes, defaultPreferences)
|
||||
.get();
|
||||
}
|
||||
|
||||
public ExternalIdNotes getExternalIdNotes() {
|
||||
|
||||
174
java/com/google/gerrit/server/account/CachedAccountDetails.java
Normal file
174
java/com/google/gerrit/server/account/CachedAccountDetails.java
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.account;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.base.Enums;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gerrit.entities.Account;
|
||||
import com.google.gerrit.entities.Project;
|
||||
import com.google.gerrit.proto.Protos;
|
||||
import com.google.gerrit.server.cache.proto.Cache;
|
||||
import com.google.gerrit.server.cache.serialize.CacheSerializer;
|
||||
import com.google.gerrit.server.cache.serialize.ObjectIdConverter;
|
||||
import com.google.gerrit.server.config.CachedPreferences;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
|
||||
/** Details of an account that are cached persistently in {@link AccountCache}. */
|
||||
@AutoValue
|
||||
abstract class CachedAccountDetails {
|
||||
@AutoValue
|
||||
abstract static class Key {
|
||||
static Key create(Account.Id accountId, ObjectId id) {
|
||||
return new AutoValue_CachedAccountDetails_Key(accountId, id.copy());
|
||||
}
|
||||
|
||||
/** Identifier of the account. */
|
||||
abstract Account.Id accountId();
|
||||
|
||||
/**
|
||||
* Git revision at which the account was loaded. Corresponds to a revision on the account ref
|
||||
* ({@code refs/users/<sharded-id>}).
|
||||
*/
|
||||
abstract ObjectId id();
|
||||
|
||||
/** Serializer used to read this entity from and write it to a persistent storage. */
|
||||
enum Serializer implements CacheSerializer<Key> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public byte[] serialize(Key object) {
|
||||
return Protos.toByteArray(
|
||||
Cache.AccountKeyProto.newBuilder()
|
||||
.setAccountId(object.accountId().get())
|
||||
.setId(ObjectIdConverter.create().toByteString(object.id()))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key deserialize(byte[] in) {
|
||||
Cache.AccountKeyProto proto = Protos.parseUnchecked(Cache.AccountKeyProto.parser(), in);
|
||||
return Key.create(
|
||||
Account.id(proto.getAccountId()),
|
||||
ObjectIdConverter.create().fromByteString(proto.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Essential attributes of the account, such as name or registration time. */
|
||||
abstract Account account();
|
||||
|
||||
/** Projects that the user has configured to watch. */
|
||||
abstract ImmutableMap<ProjectWatches.ProjectWatchKey, ImmutableSet<ProjectWatches.NotifyType>>
|
||||
projectWatches();
|
||||
|
||||
/** Preferences that this user has. Serialized as Git-config style string. */
|
||||
abstract CachedPreferences preferences();
|
||||
|
||||
static CachedAccountDetails create(
|
||||
Account account,
|
||||
ImmutableMap<ProjectWatches.ProjectWatchKey, ImmutableSet<ProjectWatches.NotifyType>>
|
||||
projectWatches,
|
||||
CachedPreferences preferences) {
|
||||
return new AutoValue_CachedAccountDetails(account, projectWatches, preferences);
|
||||
}
|
||||
|
||||
/** Serializer used to read this entity from and write it to a persistent storage. */
|
||||
enum Serializer implements CacheSerializer<CachedAccountDetails> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public byte[] serialize(CachedAccountDetails cachedAccountDetails) {
|
||||
Cache.AccountDetailsProto.Builder serialized = Cache.AccountDetailsProto.newBuilder();
|
||||
// We don't care about the difference of empty strings and null in the Account entity.
|
||||
Account account = cachedAccountDetails.account();
|
||||
Cache.AccountProto.Builder accountProto =
|
||||
Cache.AccountProto.newBuilder()
|
||||
.setId(account.id().get())
|
||||
.setRegisteredOn(account.registeredOn().toInstant().toEpochMilli())
|
||||
.setInactive(account.inactive())
|
||||
.setFullName(Strings.nullToEmpty(account.fullName()))
|
||||
.setDisplayName(Strings.nullToEmpty(account.displayName()))
|
||||
.setPreferredEmail(Strings.nullToEmpty(account.preferredEmail()))
|
||||
.setStatus(Strings.nullToEmpty(account.status()))
|
||||
.setMetaId(Strings.nullToEmpty(account.metaId()));
|
||||
serialized.setAccount(accountProto);
|
||||
|
||||
for (Map.Entry<ProjectWatches.ProjectWatchKey, ImmutableSet<ProjectWatches.NotifyType>>
|
||||
watch : cachedAccountDetails.projectWatches().entrySet()) {
|
||||
Cache.ProjectWatchProto.Builder proto =
|
||||
Cache.ProjectWatchProto.newBuilder().setProject(watch.getKey().project().get());
|
||||
if (watch.getKey().filter() != null) {
|
||||
proto.setFilter(watch.getKey().filter());
|
||||
}
|
||||
watch
|
||||
.getValue()
|
||||
.forEach(
|
||||
n ->
|
||||
proto.addNotifyType(
|
||||
Enums.stringConverter(ProjectWatches.NotifyType.class)
|
||||
.reverse()
|
||||
.convert(n)));
|
||||
serialized.addProjectWatchProto(proto);
|
||||
}
|
||||
|
||||
serialized.setUserPreferences(cachedAccountDetails.preferences().config());
|
||||
return Protos.toByteArray(serialized.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedAccountDetails deserialize(byte[] in) {
|
||||
Cache.AccountDetailsProto proto =
|
||||
Protos.parseUnchecked(Cache.AccountDetailsProto.parser(), in);
|
||||
Account account =
|
||||
Account.builder(
|
||||
Account.id(proto.getAccount().getId()),
|
||||
Timestamp.from(Instant.ofEpochMilli(proto.getAccount().getRegisteredOn())))
|
||||
.setFullName(Strings.emptyToNull(proto.getAccount().getFullName()))
|
||||
.setDisplayName(Strings.emptyToNull(proto.getAccount().getDisplayName()))
|
||||
.setPreferredEmail(Strings.emptyToNull(proto.getAccount().getPreferredEmail()))
|
||||
.setInactive(proto.getAccount().getInactive())
|
||||
.setStatus(Strings.emptyToNull(proto.getAccount().getStatus()))
|
||||
.setMetaId(Strings.emptyToNull(proto.getAccount().getMetaId()))
|
||||
.build();
|
||||
|
||||
ImmutableMap.Builder<ProjectWatches.ProjectWatchKey, ImmutableSet<ProjectWatches.NotifyType>>
|
||||
projectWatches = ImmutableMap.builder();
|
||||
proto.getProjectWatchProtoList().stream()
|
||||
.forEach(
|
||||
p ->
|
||||
projectWatches.put(
|
||||
ProjectWatches.ProjectWatchKey.create(
|
||||
Project.nameKey(p.getProject()), p.getFilter()),
|
||||
p.getNotifyTypeList().stream()
|
||||
.map(
|
||||
e ->
|
||||
Enums.stringConverter(ProjectWatches.NotifyType.class).convert(e))
|
||||
.collect(toImmutableSet())));
|
||||
|
||||
return CachedAccountDetails.create(
|
||||
account,
|
||||
projectWatches.build(),
|
||||
CachedPreferences.fromString(proto.getUserPreferences()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,429 +0,0 @@
|
||||
// Copyright (C) 2019 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package com.google.gerrit.server.account;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
|
||||
import com.google.gerrit.extensions.client.EditPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DateFormat;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DefaultBase;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailFormat;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.TimeFormat;
|
||||
import com.google.gerrit.extensions.client.MenuItem;
|
||||
import java.util.Optional;
|
||||
|
||||
@AutoValue
|
||||
public abstract class Preferences {
|
||||
@AutoValue
|
||||
public abstract static class General {
|
||||
public abstract Optional<Integer> changesPerPage();
|
||||
|
||||
public abstract Optional<String> downloadScheme();
|
||||
|
||||
public abstract Optional<DateFormat> dateFormat();
|
||||
|
||||
public abstract Optional<TimeFormat> timeFormat();
|
||||
|
||||
public abstract Optional<Boolean> expandInlineDiffs();
|
||||
|
||||
public abstract Optional<Boolean> highlightAssigneeInChangeTable();
|
||||
|
||||
public abstract Optional<Boolean> relativeDateInChangeTable();
|
||||
|
||||
public abstract Optional<DiffView> diffView();
|
||||
|
||||
public abstract Optional<Boolean> sizeBarInChangeTable();
|
||||
|
||||
public abstract Optional<Boolean> legacycidInChangeTable();
|
||||
|
||||
public abstract Optional<Boolean> muteCommonPathPrefixes();
|
||||
|
||||
public abstract Optional<Boolean> signedOffBy();
|
||||
|
||||
public abstract Optional<EmailStrategy> emailStrategy();
|
||||
|
||||
public abstract Optional<EmailFormat> emailFormat();
|
||||
|
||||
public abstract Optional<DefaultBase> defaultBaseForMerges();
|
||||
|
||||
public abstract Optional<Boolean> publishCommentsOnPush();
|
||||
|
||||
public abstract Optional<Boolean> workInProgressByDefault();
|
||||
|
||||
public abstract Optional<ImmutableList<MenuItem>> my();
|
||||
|
||||
public abstract Optional<ImmutableList<String>> changeTable();
|
||||
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
abstract Builder changesPerPage(@Nullable Integer val);
|
||||
|
||||
abstract Builder downloadScheme(@Nullable String val);
|
||||
|
||||
abstract Builder dateFormat(@Nullable DateFormat val);
|
||||
|
||||
abstract Builder timeFormat(@Nullable TimeFormat val);
|
||||
|
||||
abstract Builder expandInlineDiffs(@Nullable Boolean val);
|
||||
|
||||
abstract Builder highlightAssigneeInChangeTable(@Nullable Boolean val);
|
||||
|
||||
abstract Builder relativeDateInChangeTable(@Nullable Boolean val);
|
||||
|
||||
abstract Builder diffView(@Nullable DiffView val);
|
||||
|
||||
abstract Builder sizeBarInChangeTable(@Nullable Boolean val);
|
||||
|
||||
abstract Builder legacycidInChangeTable(@Nullable Boolean val);
|
||||
|
||||
abstract Builder muteCommonPathPrefixes(@Nullable Boolean val);
|
||||
|
||||
abstract Builder signedOffBy(@Nullable Boolean val);
|
||||
|
||||
abstract Builder emailStrategy(@Nullable EmailStrategy val);
|
||||
|
||||
abstract Builder emailFormat(@Nullable EmailFormat val);
|
||||
|
||||
abstract Builder defaultBaseForMerges(@Nullable DefaultBase val);
|
||||
|
||||
abstract Builder publishCommentsOnPush(@Nullable Boolean val);
|
||||
|
||||
abstract Builder workInProgressByDefault(@Nullable Boolean val);
|
||||
|
||||
abstract Builder my(@Nullable ImmutableList<MenuItem> val);
|
||||
|
||||
abstract Builder changeTable(@Nullable ImmutableList<String> val);
|
||||
|
||||
abstract General build();
|
||||
}
|
||||
|
||||
public static General fromInfo(GeneralPreferencesInfo info) {
|
||||
return (new AutoValue_Preferences_General.Builder())
|
||||
.changesPerPage(info.changesPerPage)
|
||||
.downloadScheme(info.downloadScheme)
|
||||
.dateFormat(info.dateFormat)
|
||||
.timeFormat(info.timeFormat)
|
||||
.expandInlineDiffs(info.expandInlineDiffs)
|
||||
.highlightAssigneeInChangeTable(info.highlightAssigneeInChangeTable)
|
||||
.relativeDateInChangeTable(info.relativeDateInChangeTable)
|
||||
.diffView(info.diffView)
|
||||
.sizeBarInChangeTable(info.sizeBarInChangeTable)
|
||||
.legacycidInChangeTable(info.legacycidInChangeTable)
|
||||
.muteCommonPathPrefixes(info.muteCommonPathPrefixes)
|
||||
.signedOffBy(info.signedOffBy)
|
||||
.emailStrategy(info.emailStrategy)
|
||||
.emailFormat(info.emailFormat)
|
||||
.defaultBaseForMerges(info.defaultBaseForMerges)
|
||||
.publishCommentsOnPush(info.publishCommentsOnPush)
|
||||
.workInProgressByDefault(info.workInProgressByDefault)
|
||||
.my(info.my == null ? null : ImmutableList.copyOf(info.my))
|
||||
.changeTable(info.changeTable == null ? null : ImmutableList.copyOf(info.changeTable))
|
||||
.build();
|
||||
}
|
||||
|
||||
public GeneralPreferencesInfo toInfo() {
|
||||
GeneralPreferencesInfo info = new GeneralPreferencesInfo();
|
||||
info.changesPerPage = changesPerPage().orElse(null);
|
||||
info.downloadScheme = downloadScheme().orElse(null);
|
||||
info.dateFormat = dateFormat().orElse(null);
|
||||
info.timeFormat = timeFormat().orElse(null);
|
||||
info.expandInlineDiffs = expandInlineDiffs().orElse(null);
|
||||
info.highlightAssigneeInChangeTable = highlightAssigneeInChangeTable().orElse(null);
|
||||
info.relativeDateInChangeTable = relativeDateInChangeTable().orElse(null);
|
||||
info.diffView = diffView().orElse(null);
|
||||
info.sizeBarInChangeTable = sizeBarInChangeTable().orElse(null);
|
||||
info.legacycidInChangeTable = legacycidInChangeTable().orElse(null);
|
||||
info.muteCommonPathPrefixes = muteCommonPathPrefixes().orElse(null);
|
||||
info.signedOffBy = signedOffBy().orElse(null);
|
||||
info.emailStrategy = emailStrategy().orElse(null);
|
||||
info.emailFormat = emailFormat().orElse(null);
|
||||
info.defaultBaseForMerges = defaultBaseForMerges().orElse(null);
|
||||
info.publishCommentsOnPush = publishCommentsOnPush().orElse(null);
|
||||
info.workInProgressByDefault = workInProgressByDefault().orElse(null);
|
||||
info.my = my().orElse(null);
|
||||
info.changeTable = changeTable().orElse(null);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
public abstract static class Edit {
|
||||
public abstract Optional<Integer> tabSize();
|
||||
|
||||
public abstract Optional<Integer> lineLength();
|
||||
|
||||
public abstract Optional<Integer> indentUnit();
|
||||
|
||||
public abstract Optional<Integer> cursorBlinkRate();
|
||||
|
||||
public abstract Optional<Boolean> hideTopMenu();
|
||||
|
||||
public abstract Optional<Boolean> showTabs();
|
||||
|
||||
public abstract Optional<Boolean> showWhitespaceErrors();
|
||||
|
||||
public abstract Optional<Boolean> syntaxHighlighting();
|
||||
|
||||
public abstract Optional<Boolean> hideLineNumbers();
|
||||
|
||||
public abstract Optional<Boolean> matchBrackets();
|
||||
|
||||
public abstract Optional<Boolean> lineWrapping();
|
||||
|
||||
public abstract Optional<Boolean> indentWithTabs();
|
||||
|
||||
public abstract Optional<Boolean> autoCloseBrackets();
|
||||
|
||||
public abstract Optional<Boolean> showBase();
|
||||
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
abstract Builder tabSize(@Nullable Integer val);
|
||||
|
||||
abstract Builder lineLength(@Nullable Integer val);
|
||||
|
||||
abstract Builder indentUnit(@Nullable Integer val);
|
||||
|
||||
abstract Builder cursorBlinkRate(@Nullable Integer val);
|
||||
|
||||
abstract Builder hideTopMenu(@Nullable Boolean val);
|
||||
|
||||
abstract Builder showTabs(@Nullable Boolean val);
|
||||
|
||||
abstract Builder showWhitespaceErrors(@Nullable Boolean val);
|
||||
|
||||
abstract Builder syntaxHighlighting(@Nullable Boolean val);
|
||||
|
||||
abstract Builder hideLineNumbers(@Nullable Boolean val);
|
||||
|
||||
abstract Builder matchBrackets(@Nullable Boolean val);
|
||||
|
||||
abstract Builder lineWrapping(@Nullable Boolean val);
|
||||
|
||||
abstract Builder indentWithTabs(@Nullable Boolean val);
|
||||
|
||||
abstract Builder autoCloseBrackets(@Nullable Boolean val);
|
||||
|
||||
abstract Builder showBase(@Nullable Boolean val);
|
||||
|
||||
abstract Edit build();
|
||||
}
|
||||
|
||||
public static Edit fromInfo(EditPreferencesInfo info) {
|
||||
return (new AutoValue_Preferences_Edit.Builder())
|
||||
.tabSize(info.tabSize)
|
||||
.lineLength(info.lineLength)
|
||||
.indentUnit(info.indentUnit)
|
||||
.cursorBlinkRate(info.cursorBlinkRate)
|
||||
.hideTopMenu(info.hideTopMenu)
|
||||
.showTabs(info.showTabs)
|
||||
.showWhitespaceErrors(info.showWhitespaceErrors)
|
||||
.syntaxHighlighting(info.syntaxHighlighting)
|
||||
.hideLineNumbers(info.hideLineNumbers)
|
||||
.matchBrackets(info.matchBrackets)
|
||||
.lineWrapping(info.lineWrapping)
|
||||
.indentWithTabs(info.indentWithTabs)
|
||||
.autoCloseBrackets(info.autoCloseBrackets)
|
||||
.showBase(info.showBase)
|
||||
.build();
|
||||
}
|
||||
|
||||
public EditPreferencesInfo toInfo() {
|
||||
EditPreferencesInfo info = new EditPreferencesInfo();
|
||||
info.tabSize = tabSize().orElse(null);
|
||||
info.lineLength = lineLength().orElse(null);
|
||||
info.indentUnit = indentUnit().orElse(null);
|
||||
info.cursorBlinkRate = cursorBlinkRate().orElse(null);
|
||||
info.hideTopMenu = hideTopMenu().orElse(null);
|
||||
info.showTabs = showTabs().orElse(null);
|
||||
info.showWhitespaceErrors = showWhitespaceErrors().orElse(null);
|
||||
info.syntaxHighlighting = syntaxHighlighting().orElse(null);
|
||||
info.hideLineNumbers = hideLineNumbers().orElse(null);
|
||||
info.matchBrackets = matchBrackets().orElse(null);
|
||||
info.lineWrapping = lineWrapping().orElse(null);
|
||||
info.indentWithTabs = indentWithTabs().orElse(null);
|
||||
info.autoCloseBrackets = autoCloseBrackets().orElse(null);
|
||||
info.showBase = showBase().orElse(null);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
public abstract static class Diff {
|
||||
public abstract Optional<Integer> context();
|
||||
|
||||
public abstract Optional<Integer> tabSize();
|
||||
|
||||
public abstract Optional<Integer> fontSize();
|
||||
|
||||
public abstract Optional<Integer> lineLength();
|
||||
|
||||
public abstract Optional<Integer> cursorBlinkRate();
|
||||
|
||||
public abstract Optional<Boolean> expandAllComments();
|
||||
|
||||
public abstract Optional<Boolean> intralineDifference();
|
||||
|
||||
public abstract Optional<Boolean> manualReview();
|
||||
|
||||
public abstract Optional<Boolean> showLineEndings();
|
||||
|
||||
public abstract Optional<Boolean> showTabs();
|
||||
|
||||
public abstract Optional<Boolean> showWhitespaceErrors();
|
||||
|
||||
public abstract Optional<Boolean> syntaxHighlighting();
|
||||
|
||||
public abstract Optional<Boolean> hideTopMenu();
|
||||
|
||||
public abstract Optional<Boolean> autoHideDiffTableHeader();
|
||||
|
||||
public abstract Optional<Boolean> hideLineNumbers();
|
||||
|
||||
public abstract Optional<Boolean> renderEntireFile();
|
||||
|
||||
public abstract Optional<Boolean> hideEmptyPane();
|
||||
|
||||
public abstract Optional<Boolean> matchBrackets();
|
||||
|
||||
public abstract Optional<Boolean> lineWrapping();
|
||||
|
||||
public abstract Optional<Whitespace> ignoreWhitespace();
|
||||
|
||||
public abstract Optional<Boolean> retainHeader();
|
||||
|
||||
public abstract Optional<Boolean> skipDeleted();
|
||||
|
||||
public abstract Optional<Boolean> skipUnchanged();
|
||||
|
||||
public abstract Optional<Boolean> skipUncommented();
|
||||
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
abstract Builder context(@Nullable Integer val);
|
||||
|
||||
abstract Builder tabSize(@Nullable Integer val);
|
||||
|
||||
abstract Builder fontSize(@Nullable Integer val);
|
||||
|
||||
abstract Builder lineLength(@Nullable Integer val);
|
||||
|
||||
abstract Builder cursorBlinkRate(@Nullable Integer val);
|
||||
|
||||
abstract Builder expandAllComments(@Nullable Boolean val);
|
||||
|
||||
abstract Builder intralineDifference(@Nullable Boolean val);
|
||||
|
||||
abstract Builder manualReview(@Nullable Boolean val);
|
||||
|
||||
abstract Builder showLineEndings(@Nullable Boolean val);
|
||||
|
||||
abstract Builder showTabs(@Nullable Boolean val);
|
||||
|
||||
abstract Builder showWhitespaceErrors(@Nullable Boolean val);
|
||||
|
||||
abstract Builder syntaxHighlighting(@Nullable Boolean val);
|
||||
|
||||
abstract Builder hideTopMenu(@Nullable Boolean val);
|
||||
|
||||
abstract Builder autoHideDiffTableHeader(@Nullable Boolean val);
|
||||
|
||||
abstract Builder hideLineNumbers(@Nullable Boolean val);
|
||||
|
||||
abstract Builder renderEntireFile(@Nullable Boolean val);
|
||||
|
||||
abstract Builder hideEmptyPane(@Nullable Boolean val);
|
||||
|
||||
abstract Builder matchBrackets(@Nullable Boolean val);
|
||||
|
||||
abstract Builder lineWrapping(@Nullable Boolean val);
|
||||
|
||||
abstract Builder ignoreWhitespace(@Nullable Whitespace val);
|
||||
|
||||
abstract Builder retainHeader(@Nullable Boolean val);
|
||||
|
||||
abstract Builder skipDeleted(@Nullable Boolean val);
|
||||
|
||||
abstract Builder skipUnchanged(@Nullable Boolean val);
|
||||
|
||||
abstract Builder skipUncommented(@Nullable Boolean val);
|
||||
|
||||
abstract Diff build();
|
||||
}
|
||||
|
||||
public static Diff fromInfo(DiffPreferencesInfo info) {
|
||||
return (new AutoValue_Preferences_Diff.Builder())
|
||||
.context(info.context)
|
||||
.tabSize(info.tabSize)
|
||||
.fontSize(info.fontSize)
|
||||
.lineLength(info.lineLength)
|
||||
.cursorBlinkRate(info.cursorBlinkRate)
|
||||
.expandAllComments(info.expandAllComments)
|
||||
.intralineDifference(info.intralineDifference)
|
||||
.manualReview(info.manualReview)
|
||||
.showLineEndings(info.showLineEndings)
|
||||
.showTabs(info.showTabs)
|
||||
.showWhitespaceErrors(info.showWhitespaceErrors)
|
||||
.syntaxHighlighting(info.syntaxHighlighting)
|
||||
.hideTopMenu(info.hideTopMenu)
|
||||
.autoHideDiffTableHeader(info.autoHideDiffTableHeader)
|
||||
.hideLineNumbers(info.hideLineNumbers)
|
||||
.renderEntireFile(info.renderEntireFile)
|
||||
.hideEmptyPane(info.hideEmptyPane)
|
||||
.matchBrackets(info.matchBrackets)
|
||||
.lineWrapping(info.lineWrapping)
|
||||
.ignoreWhitespace(info.ignoreWhitespace)
|
||||
.retainHeader(info.retainHeader)
|
||||
.skipDeleted(info.skipDeleted)
|
||||
.skipUnchanged(info.skipUnchanged)
|
||||
.skipUncommented(info.skipUncommented)
|
||||
.build();
|
||||
}
|
||||
|
||||
public DiffPreferencesInfo toInfo() {
|
||||
DiffPreferencesInfo info = new DiffPreferencesInfo();
|
||||
info.context = context().orElse(null);
|
||||
info.tabSize = tabSize().orElse(null);
|
||||
info.fontSize = fontSize().orElse(null);
|
||||
info.lineLength = lineLength().orElse(null);
|
||||
info.cursorBlinkRate = cursorBlinkRate().orElse(null);
|
||||
info.expandAllComments = expandAllComments().orElse(null);
|
||||
info.intralineDifference = intralineDifference().orElse(null);
|
||||
info.manualReview = manualReview().orElse(null);
|
||||
info.showLineEndings = showLineEndings().orElse(null);
|
||||
info.showTabs = showTabs().orElse(null);
|
||||
info.showWhitespaceErrors = showWhitespaceErrors().orElse(null);
|
||||
info.syntaxHighlighting = syntaxHighlighting().orElse(null);
|
||||
info.hideTopMenu = hideTopMenu().orElse(null);
|
||||
info.autoHideDiffTableHeader = autoHideDiffTableHeader().orElse(null);
|
||||
info.hideLineNumbers = hideLineNumbers().orElse(null);
|
||||
info.renderEntireFile = renderEntireFile().orElse(null);
|
||||
info.hideEmptyPane = hideEmptyPane().orElse(null);
|
||||
info.matchBrackets = matchBrackets().orElse(null);
|
||||
info.lineWrapping = lineWrapping().orElse(null);
|
||||
info.ignoreWhitespace = ignoreWhitespace().orElse(null);
|
||||
info.retainHeader = retainHeader().orElse(null);
|
||||
info.skipDeleted = skipDeleted().orElse(null);
|
||||
info.skipUnchanged = skipUnchanged().orElse(null);
|
||||
info.skipUncommented = skipUncommented().orElse(null);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,6 @@ import com.google.common.collect.Sets;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.entities.Account;
|
||||
import com.google.gerrit.entities.Project;
|
||||
import com.google.gerrit.proto.Protos;
|
||||
import com.google.gerrit.server.cache.proto.Cache.ProjectWatchKeyProto;
|
||||
import com.google.gerrit.server.cache.serialize.CacheSerializer;
|
||||
import com.google.gerrit.server.git.ValidationError;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -90,26 +87,6 @@ public class ProjectWatches {
|
||||
public abstract Project.NameKey project();
|
||||
|
||||
public abstract @Nullable String filter();
|
||||
|
||||
enum Serializer implements CacheSerializer<ProjectWatchKey> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public byte[] serialize(ProjectWatchKey key) {
|
||||
ProjectWatchKeyProto.Builder proto =
|
||||
ProjectWatchKeyProto.newBuilder().setProject(key.project().get());
|
||||
if (key.filter() != null) {
|
||||
proto.setFilter(key.filter());
|
||||
}
|
||||
return Protos.toByteArray(proto.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectWatchKey deserialize(byte[] in) {
|
||||
ProjectWatchKeyProto proto = Protos.parseUnchecked(ProjectWatchKeyProto.parser(), in);
|
||||
return ProjectWatchKey.create(Project.nameKey(proto.getProject()), proto.getFilter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum NotifyType {
|
||||
|
||||
@@ -30,24 +30,22 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.entities.Account;
|
||||
import com.google.gerrit.entities.RefNames;
|
||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.EditPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.MenuItem;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.config.VersionedDefaultPreferences;
|
||||
import com.google.gerrit.server.git.UserConfigSections;
|
||||
import com.google.gerrit.server.git.ValidationError;
|
||||
import com.google.gerrit.server.git.meta.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.meta.VersionedMetaData;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
@@ -184,6 +182,11 @@ public class StoredPreferences {
|
||||
return cfg;
|
||||
}
|
||||
|
||||
/** Returns the content of the {@code preferences.config} file as {@link Config}. */
|
||||
Config getRaw() {
|
||||
return cfg;
|
||||
}
|
||||
|
||||
private GeneralPreferencesInfo parseGeneralPreferences(@Nullable GeneralPreferencesInfo input) {
|
||||
try {
|
||||
return parseGeneralPreferences(cfg, defaultCfg, input);
|
||||
@@ -224,7 +227,12 @@ public class StoredPreferences {
|
||||
}
|
||||
}
|
||||
|
||||
private static GeneralPreferencesInfo parseGeneralPreferences(
|
||||
/**
|
||||
* Returns a {@link GeneralPreferencesInfo} that is the result of parsing {@code defaultCfg} for
|
||||
* the server's default configs and {@code cfg} for the user's config. These configs are then
|
||||
* overlaid to inherit values (default -> user -> input (if provided).
|
||||
*/
|
||||
public static GeneralPreferencesInfo parseGeneralPreferences(
|
||||
Config cfg, @Nullable Config defaultCfg, @Nullable GeneralPreferencesInfo input)
|
||||
throws ConfigInvalidException {
|
||||
GeneralPreferencesInfo r =
|
||||
@@ -247,7 +255,12 @@ public class StoredPreferences {
|
||||
return r;
|
||||
}
|
||||
|
||||
private static DiffPreferencesInfo parseDiffPreferences(
|
||||
/**
|
||||
* Returns a {@link DiffPreferencesInfo} that is the result of parsing {@code defaultCfg} for the
|
||||
* server's default configs and {@code cfg} for the user's config. These configs are then overlaid
|
||||
* to inherit values (default -> user -> input (if provided).
|
||||
*/
|
||||
public static DiffPreferencesInfo parseDiffPreferences(
|
||||
Config cfg, @Nullable Config defaultCfg, @Nullable DiffPreferencesInfo input)
|
||||
throws ConfigInvalidException {
|
||||
return loadSection(
|
||||
@@ -261,7 +274,12 @@ public class StoredPreferences {
|
||||
input);
|
||||
}
|
||||
|
||||
private static EditPreferencesInfo parseEditPreferences(
|
||||
/**
|
||||
* Returns a {@link EditPreferencesInfo} that is the result of parsing {@code defaultCfg} for the
|
||||
* server's default configs and {@code cfg} for the user's config. These configs are then overlaid
|
||||
* to inherit values (default -> user -> input (if provided).
|
||||
*/
|
||||
public static EditPreferencesInfo parseEditPreferences(
|
||||
Config cfg, @Nullable Config defaultCfg, @Nullable EditPreferencesInfo input)
|
||||
throws ConfigInvalidException {
|
||||
return loadSection(
|
||||
@@ -543,32 +561,4 @@ public class StoredPreferences {
|
||||
cfg.unsetSection(section, subsection);
|
||||
}
|
||||
}
|
||||
|
||||
private static class VersionedDefaultPreferences extends VersionedMetaData {
|
||||
private Config cfg;
|
||||
|
||||
@Override
|
||||
protected String getRefName() {
|
||||
return RefNames.REFS_USERS_DEFAULT;
|
||||
}
|
||||
|
||||
private Config getConfig() {
|
||||
checkState(cfg != null, "Default preferences not loaded yet.");
|
||||
return cfg;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLoad() throws IOException, ConfigInvalidException {
|
||||
cfg = readConfig(PREFERENCES_CONFIG);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
|
||||
if (Strings.isNullOrEmpty(commit.getMessage())) {
|
||||
commit.setMessage("Update default preferences\n");
|
||||
}
|
||||
saveConfig(PREFERENCES_CONFIG, cfg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
|
||||
package com.google.gerrit.server.account.externalids;
|
||||
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.gerrit.entities.Account;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
|
||||
/**
|
||||
@@ -36,17 +36,17 @@ interface ExternalIdCache {
|
||||
Collection<ExternalId> toRemove,
|
||||
Collection<ExternalId> toAdd);
|
||||
|
||||
Set<ExternalId> byAccount(Account.Id accountId) throws IOException;
|
||||
ImmutableSet<ExternalId> byAccount(Account.Id accountId) throws IOException;
|
||||
|
||||
Set<ExternalId> byAccount(Account.Id accountId, ObjectId rev) throws IOException;
|
||||
ImmutableSet<ExternalId> byAccount(Account.Id accountId, ObjectId rev) throws IOException;
|
||||
|
||||
SetMultimap<Account.Id, ExternalId> allByAccount() throws IOException;
|
||||
ImmutableSetMultimap<Account.Id, ExternalId> allByAccount() throws IOException;
|
||||
|
||||
SetMultimap<String, ExternalId> byEmails(String... emails) throws IOException;
|
||||
ImmutableSetMultimap<String, ExternalId> byEmails(String... emails) throws IOException;
|
||||
|
||||
SetMultimap<String, ExternalId> allByEmail() throws IOException;
|
||||
ImmutableSetMultimap<String, ExternalId> allByEmail() throws IOException;
|
||||
|
||||
default Set<ExternalId> byEmail(String email) throws IOException {
|
||||
default ImmutableSet<ExternalId> byEmail(String email) throws IOException {
|
||||
return byEmails(email).get(email);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.server.account.externalids;
|
||||
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
@@ -25,7 +26,6 @@ import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Named;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -73,22 +73,22 @@ class ExternalIdCacheImpl implements ExternalIdCache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ExternalId> byAccount(Account.Id accountId) throws IOException {
|
||||
public ImmutableSet<ExternalId> byAccount(Account.Id accountId) throws IOException {
|
||||
return get().byAccount().get(accountId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ExternalId> byAccount(Account.Id accountId, ObjectId rev) throws IOException {
|
||||
public ImmutableSet<ExternalId> byAccount(Account.Id accountId, ObjectId rev) throws IOException {
|
||||
return get(rev).byAccount().get(accountId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SetMultimap<Account.Id, ExternalId> allByAccount() throws IOException {
|
||||
public ImmutableSetMultimap<Account.Id, ExternalId> allByAccount() throws IOException {
|
||||
return get().byAccount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SetMultimap<String, ExternalId> byEmails(String... emails) throws IOException {
|
||||
public ImmutableSetMultimap<String, ExternalId> byEmails(String... emails) throws IOException {
|
||||
AllExternalIds allExternalIds = get();
|
||||
ImmutableSetMultimap.Builder<String, ExternalId> byEmails = ImmutableSetMultimap.builder();
|
||||
for (String email : emails) {
|
||||
@@ -98,7 +98,7 @@ class ExternalIdCacheImpl implements ExternalIdCache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SetMultimap<String, ExternalId> allByEmail() throws IOException {
|
||||
public ImmutableSetMultimap<String, ExternalId> allByEmail() throws IOException {
|
||||
return get().byEmail();
|
||||
}
|
||||
|
||||
|
||||
@@ -745,9 +745,6 @@ public class ExternalIdNotes extends VersionedMetaData {
|
||||
.map(ExternalId::accountId)
|
||||
.filter(i -> !accountsToSkip.contains(i))
|
||||
.collect(toSet())) {
|
||||
if (accountCache != null) {
|
||||
accountCache.evict(id);
|
||||
}
|
||||
if (accountIndexer != null) {
|
||||
accountIndexer.get().index(id);
|
||||
}
|
||||
|
||||
@@ -17,13 +17,12 @@ package com.google.gerrit.server.account.externalids;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.gerrit.entities.Account;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
|
||||
@@ -65,24 +64,25 @@ public class ExternalIds {
|
||||
}
|
||||
|
||||
/** Returns the external IDs of the specified account. */
|
||||
public Set<ExternalId> byAccount(Account.Id accountId) throws IOException {
|
||||
public ImmutableSet<ExternalId> byAccount(Account.Id accountId) throws IOException {
|
||||
return externalIdCache.byAccount(accountId);
|
||||
}
|
||||
|
||||
/** Returns the external IDs of the specified account that have the given scheme. */
|
||||
public Set<ExternalId> byAccount(Account.Id accountId, String scheme) throws IOException {
|
||||
public ImmutableSet<ExternalId> byAccount(Account.Id accountId, String scheme)
|
||||
throws IOException {
|
||||
return byAccount(accountId).stream()
|
||||
.filter(e -> e.key().isScheme(scheme))
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
/** Returns the external IDs of the specified account. */
|
||||
public Set<ExternalId> byAccount(Account.Id accountId, ObjectId rev) throws IOException {
|
||||
public ImmutableSet<ExternalId> byAccount(Account.Id accountId, ObjectId rev) throws IOException {
|
||||
return externalIdCache.byAccount(accountId, rev);
|
||||
}
|
||||
|
||||
/** Returns the external IDs of the specified account that have the given scheme. */
|
||||
public Set<ExternalId> byAccount(Account.Id accountId, String scheme, ObjectId rev)
|
||||
public ImmutableSet<ExternalId> byAccount(Account.Id accountId, String scheme, ObjectId rev)
|
||||
throws IOException {
|
||||
return byAccount(accountId, rev).stream()
|
||||
.filter(e -> e.key().isScheme(scheme))
|
||||
@@ -90,7 +90,7 @@ public class ExternalIds {
|
||||
}
|
||||
|
||||
/** Returns all external IDs by account. */
|
||||
public SetMultimap<Account.Id, ExternalId> allByAccount() throws IOException {
|
||||
public ImmutableSetMultimap<Account.Id, ExternalId> allByAccount() throws IOException {
|
||||
return externalIdCache.allByAccount();
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ public class ExternalIds {
|
||||
*
|
||||
* @see #byEmails(String...)
|
||||
*/
|
||||
public Set<ExternalId> byEmail(String email) throws IOException {
|
||||
public ImmutableSet<ExternalId> byEmail(String email) throws IOException {
|
||||
return externalIdCache.byEmail(email);
|
||||
}
|
||||
|
||||
@@ -125,12 +125,12 @@ public class ExternalIds {
|
||||
*
|
||||
* @see #byEmail(String)
|
||||
*/
|
||||
public SetMultimap<String, ExternalId> byEmails(String... emails) throws IOException {
|
||||
public ImmutableSetMultimap<String, ExternalId> byEmails(String... emails) throws IOException {
|
||||
return externalIdCache.byEmails(emails);
|
||||
}
|
||||
|
||||
/** Returns all external IDs by email. */
|
||||
public SetMultimap<String, ExternalId> allByEmail() throws IOException {
|
||||
public ImmutableSetMultimap<String, ExternalId> allByEmail() throws IOException {
|
||||
return externalIdCache.allByEmail();
|
||||
}
|
||||
}
|
||||
|
||||
97
java/com/google/gerrit/server/config/CachedPreferences.java
Normal file
97
java/com/google/gerrit/server/config/CachedPreferences.java
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.config;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.exceptions.StorageException;
|
||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.EditPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.server.account.StoredPreferences;
|
||||
import java.util.Optional;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
|
||||
/**
|
||||
* Container class for preferences serialized as Git-style config files. Keeps the values as {@link
|
||||
* String}s as they are immutable and thread-safe.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class CachedPreferences {
|
||||
|
||||
public static CachedPreferences EMPTY = fromString("");
|
||||
|
||||
public abstract String config();
|
||||
|
||||
/** Returns a cache-able representation of the config. */
|
||||
public static CachedPreferences fromConfig(Config cfg) {
|
||||
return new AutoValue_CachedPreferences(cfg.toText());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cache-able representation of the config. To be used only when constructing a {@link
|
||||
* CachedPreferences} from a serialized, cached value.
|
||||
*/
|
||||
public static CachedPreferences fromString(String cfg) {
|
||||
return new AutoValue_CachedPreferences(cfg);
|
||||
}
|
||||
|
||||
public static GeneralPreferencesInfo general(
|
||||
Optional<CachedPreferences> defaultPreferences, CachedPreferences userPreferences) {
|
||||
try {
|
||||
return StoredPreferences.parseGeneralPreferences(
|
||||
userPreferences.asConfig(), configOrNull(defaultPreferences), null);
|
||||
} catch (ConfigInvalidException e) {
|
||||
return GeneralPreferencesInfo.defaults();
|
||||
}
|
||||
}
|
||||
|
||||
public static EditPreferencesInfo edit(
|
||||
Optional<CachedPreferences> defaultPreferences, CachedPreferences userPreferences) {
|
||||
try {
|
||||
return StoredPreferences.parseEditPreferences(
|
||||
userPreferences.asConfig(), configOrNull(defaultPreferences), null);
|
||||
} catch (ConfigInvalidException e) {
|
||||
return EditPreferencesInfo.defaults();
|
||||
}
|
||||
}
|
||||
|
||||
public static DiffPreferencesInfo diff(
|
||||
Optional<CachedPreferences> defaultPreferences, CachedPreferences userPreferences) {
|
||||
try {
|
||||
return StoredPreferences.parseDiffPreferences(
|
||||
userPreferences.asConfig(), configOrNull(defaultPreferences), null);
|
||||
} catch (ConfigInvalidException e) {
|
||||
return DiffPreferencesInfo.defaults();
|
||||
}
|
||||
}
|
||||
|
||||
public Config asConfig() {
|
||||
Config cfg = new Config();
|
||||
try {
|
||||
cfg.fromText(config());
|
||||
} catch (ConfigInvalidException e) {
|
||||
// Programmer error: We have parsed this config before and are unable to parse it now.
|
||||
throw new StorageException(e);
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Config configOrNull(Optional<CachedPreferences> cachedPreferences) {
|
||||
return cachedPreferences.map(CachedPreferences::asConfig).orElse(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.config;
|
||||
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
|
||||
/** Cache for Gerrit's default preferences (general, diff edit). */
|
||||
public interface DefaultPreferencesCache {
|
||||
/**
|
||||
* Static member to be returned when there is no default config. This prevents re-instantiating
|
||||
* many {@link CachedPreferences} in this case.
|
||||
*/
|
||||
CachedPreferences EMPTY = CachedPreferences.fromString("");
|
||||
|
||||
/** Returns a cached instance of {@link CachedPreferences}. */
|
||||
CachedPreferences get();
|
||||
|
||||
/** Returns a cached instance of {@link CachedPreferences} at the specified revision. */
|
||||
CachedPreferences get(ObjectId rev);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.config;
|
||||
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.gerrit.entities.RefNames;
|
||||
import com.google.gerrit.exceptions.StorageException;
|
||||
import com.google.gerrit.server.cache.CacheModule;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Named;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
@Singleton
|
||||
public class DefaultPreferencesCacheImpl implements DefaultPreferencesCache {
|
||||
private static final String NAME = "default_preferences";
|
||||
|
||||
public static Module module() {
|
||||
return new CacheModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
// Bind an in-memory cache that allows exactly 1 value to be cached.
|
||||
cache(NAME, ObjectId.class, CachedPreferences.class).loader(Loader.class).maximumWeight(1);
|
||||
bind(DefaultPreferencesCacheImpl.class);
|
||||
bind(DefaultPreferencesCache.class).to(DefaultPreferencesCacheImpl.class);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private final GitRepositoryManager repositoryManager;
|
||||
private final AllUsersName allUsersName;
|
||||
private final LoadingCache<ObjectId, CachedPreferences> cache;
|
||||
|
||||
@Inject
|
||||
DefaultPreferencesCacheImpl(
|
||||
GitRepositoryManager repositoryManager,
|
||||
AllUsersName allUsersName,
|
||||
@Named(NAME) LoadingCache<ObjectId, CachedPreferences> cache) {
|
||||
this.repositoryManager = repositoryManager;
|
||||
this.allUsersName = allUsersName;
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedPreferences get() {
|
||||
try (Repository allUsersRepo = repositoryManager.openRepository(allUsersName)) {
|
||||
Ref ref = allUsersRepo.exactRef(RefNames.REFS_USERS_DEFAULT);
|
||||
if (ref == null) {
|
||||
return EMPTY;
|
||||
}
|
||||
return get(ref.getObjectId());
|
||||
} catch (IOException e) {
|
||||
throw new StorageException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedPreferences get(ObjectId rev) {
|
||||
try {
|
||||
return cache.get(rev);
|
||||
} catch (ExecutionException e) {
|
||||
throw new StorageException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
private static class Loader extends CacheLoader<ObjectId, CachedPreferences> {
|
||||
private final GitRepositoryManager repositoryManager;
|
||||
private final AllUsersName allUsersName;
|
||||
|
||||
@Inject
|
||||
Loader(GitRepositoryManager repositoryManager, AllUsersName allUsersName) {
|
||||
this.repositoryManager = repositoryManager;
|
||||
this.allUsersName = allUsersName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedPreferences load(ObjectId key) throws IOException, ConfigInvalidException {
|
||||
try (Repository allUsersRepo = repositoryManager.openRepository(allUsersName)) {
|
||||
VersionedDefaultPreferences versionedDefaultPreferences = new VersionedDefaultPreferences();
|
||||
versionedDefaultPreferences.load(allUsersName, allUsersRepo, key);
|
||||
return CachedPreferences.fromConfig(versionedDefaultPreferences.getConfig());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,6 +232,7 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
install(ChangeKindCacheImpl.module());
|
||||
install(ChangeFinder.module());
|
||||
install(ConflictsCacheImpl.module());
|
||||
install(DefaultPreferencesCacheImpl.module());
|
||||
install(GroupCacheImpl.module());
|
||||
install(GroupIncludeCacheImpl.module());
|
||||
install(MergeabilityCacheImpl.module());
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.config;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.entities.RefNames;
|
||||
import com.google.gerrit.exceptions.StorageException;
|
||||
import com.google.gerrit.server.git.meta.VersionedMetaData;
|
||||
import java.io.IOException;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
/**
|
||||
* Low-level storage API to load Gerrit's default config from {@code All-Users}. Should not be used
|
||||
* directly.
|
||||
*/
|
||||
public class VersionedDefaultPreferences extends VersionedMetaData {
|
||||
private static final String PREFERENCES_CONFIG = "preferences.config";
|
||||
|
||||
private Config cfg;
|
||||
|
||||
public static Config get(Repository allUsersRepo, AllUsersName allUsersName)
|
||||
throws StorageException, ConfigInvalidException {
|
||||
VersionedDefaultPreferences versionedDefaultPreferences = new VersionedDefaultPreferences();
|
||||
try {
|
||||
versionedDefaultPreferences.load(allUsersName, allUsersRepo);
|
||||
} catch (IOException e) {
|
||||
throw new StorageException(e);
|
||||
}
|
||||
return versionedDefaultPreferences.getConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRefName() {
|
||||
return RefNames.REFS_USERS_DEFAULT;
|
||||
}
|
||||
|
||||
public Config getConfig() {
|
||||
checkState(cfg != null, "Default preferences not loaded yet.");
|
||||
return cfg;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLoad() throws IOException, ConfigInvalidException {
|
||||
cfg = readConfig(PREFERENCES_CONFIG);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onSave(CommitBuilder commit) throws IOException {
|
||||
if (Strings.isNullOrEmpty(commit.getMessage())) {
|
||||
commit.setMessage("Update default preferences\n");
|
||||
}
|
||||
saveConfig(PREFERENCES_CONFIG, cfg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,6 @@ public class AccountIndexerImpl implements AccountIndexer {
|
||||
|
||||
@Override
|
||||
public void index(Account.Id id) {
|
||||
byIdCache.evict(id);
|
||||
Optional<AccountState> accountState = byIdCache.get(id);
|
||||
|
||||
if (accountState.isPresent()) {
|
||||
|
||||
@@ -90,7 +90,6 @@ public class AllAccountsIndexer extends SiteIndexer<Account.Id, AccountState, Ac
|
||||
executor.submit(
|
||||
() -> {
|
||||
try {
|
||||
accountCache.evict(id);
|
||||
Optional<AccountState> a = accountCache.get(id);
|
||||
if (a.isPresent()) {
|
||||
index.replace(a.get());
|
||||
|
||||
@@ -94,7 +94,6 @@ public class ReindexAfterRefUpdate implements GitReferenceUpdatedListener {
|
||||
if (allUsersName.get().equals(event.getProjectName())) {
|
||||
Account.Id accountId = Account.Id.fromRef(event.getRefName());
|
||||
if (accountId != null && !event.getRefName().startsWith(RefNames.REFS_STARRED_CHANGES)) {
|
||||
accountCache.evict(accountId);
|
||||
indexer.get().index(accountId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,32 +20,28 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.server.account.StoredPreferences;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.config.ConfigResource;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.config.DefaultPreferencesCache;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
@Singleton
|
||||
public class GetDiffPreferences implements RestReadView<ConfigResource> {
|
||||
|
||||
private final AllUsersName allUsersName;
|
||||
private final GitRepositoryManager gitManager;
|
||||
private final DefaultPreferencesCache defaultPreferenceCache;
|
||||
|
||||
@Inject
|
||||
GetDiffPreferences(GitRepositoryManager gitManager, AllUsersName allUsersName) {
|
||||
this.allUsersName = allUsersName;
|
||||
this.gitManager = gitManager;
|
||||
GetDiffPreferences(DefaultPreferencesCache defaultPreferenceCache) {
|
||||
this.defaultPreferenceCache = defaultPreferenceCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<DiffPreferencesInfo> apply(ConfigResource configResource)
|
||||
throws BadRequestException, ResourceConflictException, IOException, ConfigInvalidException {
|
||||
try (Repository git = gitManager.openRepository(allUsersName)) {
|
||||
return Response.ok(StoredPreferences.readDefaultDiffPreferences(allUsersName, git));
|
||||
}
|
||||
return Response.ok(
|
||||
StoredPreferences.parseDiffPreferences(
|
||||
defaultPreferenceCache.get().asConfig(), null, null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,31 +20,27 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.server.account.StoredPreferences;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.config.ConfigResource;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.config.DefaultPreferencesCache;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
@Singleton
|
||||
public class GetEditPreferences implements RestReadView<ConfigResource> {
|
||||
private final AllUsersName allUsersName;
|
||||
private final GitRepositoryManager gitManager;
|
||||
private final DefaultPreferencesCache defaultPreferenceCache;
|
||||
|
||||
@Inject
|
||||
GetEditPreferences(GitRepositoryManager gitManager, AllUsersName allUsersName) {
|
||||
this.allUsersName = allUsersName;
|
||||
this.gitManager = gitManager;
|
||||
GetEditPreferences(DefaultPreferencesCache defaultPreferenceCache) {
|
||||
this.defaultPreferenceCache = defaultPreferenceCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<EditPreferencesInfo> apply(ConfigResource configResource)
|
||||
throws BadRequestException, ResourceConflictException, IOException, ConfigInvalidException {
|
||||
try (Repository git = gitManager.openRepository(allUsersName)) {
|
||||
return Response.ok(StoredPreferences.readDefaultEditPreferences(allUsersName, git));
|
||||
}
|
||||
return Response.ok(
|
||||
StoredPreferences.parseEditPreferences(
|
||||
defaultPreferenceCache.get().asConfig(), null, null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,31 +18,27 @@ import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.server.account.StoredPreferences;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.config.ConfigResource;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.config.DefaultPreferencesCache;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
@Singleton
|
||||
public class GetPreferences implements RestReadView<ConfigResource> {
|
||||
private final GitRepositoryManager gitMgr;
|
||||
private final AllUsersName allUsersName;
|
||||
private final DefaultPreferencesCache defaultPreferenceCache;
|
||||
|
||||
@Inject
|
||||
public GetPreferences(GitRepositoryManager gitMgr, AllUsersName allUsersName) {
|
||||
this.gitMgr = gitMgr;
|
||||
this.allUsersName = allUsersName;
|
||||
public GetPreferences(DefaultPreferencesCache defaultPreferenceCache) {
|
||||
this.defaultPreferenceCache = defaultPreferenceCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<GeneralPreferencesInfo> apply(ConfigResource rsrc)
|
||||
throws IOException, ConfigInvalidException {
|
||||
try (Repository git = gitMgr.openRepository(allUsersName)) {
|
||||
return Response.ok(StoredPreferences.readDefaultGeneralPreferences(allUsersName, git));
|
||||
}
|
||||
return Response.ok(
|
||||
StoredPreferences.parseGeneralPreferences(
|
||||
defaultPreferenceCache.get().asConfig(), null, null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,6 @@ public class SetDiffPreferences implements RestModifyView<ConfigResource, DiffPr
|
||||
|
||||
try (MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName)) {
|
||||
DiffPreferencesInfo updatedPrefs = StoredPreferences.updateDefaultDiffPreferences(md, input);
|
||||
accountCache.evictAll();
|
||||
return Response.ok(updatedPrefs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,6 @@ public class SetEditPreferences implements RestModifyView<ConfigResource, EditPr
|
||||
|
||||
try (MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName)) {
|
||||
EditPreferencesInfo updatedPrefs = StoredPreferences.updateDefaultEditPreferences(md, input);
|
||||
accountCache.evictAll();
|
||||
return Response.ok(updatedPrefs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,6 @@ public class SetPreferences implements RestModifyView<ConfigResource, GeneralPre
|
||||
try (MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName)) {
|
||||
GeneralPreferencesInfo updatedPrefs =
|
||||
StoredPreferences.updateDefaultGeneralPreferences(md, input);
|
||||
accountCache.evictAll();
|
||||
return Response.ok(updatedPrefs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ package com.google.gerrit.testing;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.entities.Account;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
@@ -61,18 +60,6 @@ public class FakeAccountCache implements AccountCache {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void evict(@Nullable Account.Id accountId) {
|
||||
if (byId != null) {
|
||||
byId.remove(accountId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void evictAll() {
|
||||
byId.clear();
|
||||
}
|
||||
|
||||
public synchronized void put(Account account) {
|
||||
AccountState state = newState(account);
|
||||
byId.put(account.id(), state);
|
||||
|
||||
@@ -20,9 +20,7 @@ import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.entities.Account;
|
||||
import com.google.gerrit.entities.AccountGroup;
|
||||
import com.google.gerrit.entities.Project;
|
||||
import com.google.gerrit.entities.RefNames;
|
||||
@@ -253,130 +251,6 @@ public class ProjectResetterTest {
|
||||
verify(projectCache, only()).evict(project2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accountEvictionIfUserBranchIsReset() throws Exception {
|
||||
Account.Id accountId = Account.id(1);
|
||||
Project.NameKey allUsers = Project.nameKey(AllUsersNameProvider.DEFAULT);
|
||||
Repository allUsersRepo = repoManager.createRepository(allUsers);
|
||||
Ref userBranch = createRef(allUsersRepo, RefNames.refsUsers(accountId));
|
||||
|
||||
AccountCache accountCache = mock(AccountCache.class);
|
||||
AccountIndexer accountIndexer = mock(AccountIndexer.class);
|
||||
|
||||
// Non-user branch because it's not in All-Users.
|
||||
Ref nonUserBranch = createRef(RefNames.refsUsers(Account.id(2)));
|
||||
|
||||
try (ProjectResetter resetProject =
|
||||
builder(null, accountCache, accountIndexer, null, null, null, null)
|
||||
.build(new ProjectResetter.Config().reset(project).reset(allUsers))) {
|
||||
updateRef(nonUserBranch);
|
||||
updateRef(allUsersRepo, userBranch);
|
||||
}
|
||||
|
||||
verify(accountCache, only()).evict(accountId);
|
||||
verify(accountIndexer, only()).index(accountId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accountEvictionIfUserBranchIsDeleted() throws Exception {
|
||||
Account.Id accountId = Account.id(1);
|
||||
Project.NameKey allUsers = Project.nameKey(AllUsersNameProvider.DEFAULT);
|
||||
Repository allUsersRepo = repoManager.createRepository(allUsers);
|
||||
|
||||
AccountCache accountCache = mock(AccountCache.class);
|
||||
AccountIndexer accountIndexer = mock(AccountIndexer.class);
|
||||
|
||||
try (ProjectResetter resetProject =
|
||||
builder(null, accountCache, accountIndexer, null, null, null, null)
|
||||
.build(new ProjectResetter.Config().reset(project).reset(allUsers))) {
|
||||
// Non-user branch because it's not in All-Users.
|
||||
createRef(RefNames.refsUsers(Account.id(2)));
|
||||
|
||||
createRef(allUsersRepo, RefNames.refsUsers(accountId));
|
||||
}
|
||||
|
||||
verify(accountCache, only()).evict(accountId);
|
||||
verify(accountIndexer, only()).index(accountId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accountEvictionIfExternalIdsBranchIsReset() throws Exception {
|
||||
Account.Id accountId = Account.id(1);
|
||||
Project.NameKey allUsers = Project.nameKey(AllUsersNameProvider.DEFAULT);
|
||||
Repository allUsersRepo = repoManager.createRepository(allUsers);
|
||||
Ref externalIds = createRef(allUsersRepo, RefNames.REFS_EXTERNAL_IDS);
|
||||
createRef(allUsersRepo, RefNames.refsUsers(accountId));
|
||||
|
||||
Account.Id accountId2 = Account.id(2);
|
||||
|
||||
AccountCache accountCache = mock(AccountCache.class);
|
||||
AccountIndexer accountIndexer = mock(AccountIndexer.class);
|
||||
|
||||
// Non-user branch because it's not in All-Users.
|
||||
Ref nonUserBranch = createRef(RefNames.refsUsers(Account.id(3)));
|
||||
|
||||
try (ProjectResetter resetProject =
|
||||
builder(null, accountCache, accountIndexer, null, null, null, null)
|
||||
.build(new ProjectResetter.Config().reset(project).reset(allUsers))) {
|
||||
updateRef(nonUserBranch);
|
||||
updateRef(allUsersRepo, externalIds);
|
||||
createRef(allUsersRepo, RefNames.refsUsers(accountId2));
|
||||
}
|
||||
|
||||
verify(accountCache).evict(accountId);
|
||||
verify(accountCache).evict(accountId2);
|
||||
verify(accountIndexer).index(accountId);
|
||||
verify(accountIndexer).index(accountId2);
|
||||
verifyNoMoreInteractions(accountCache, accountIndexer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accountEvictionIfExternalIdsBranchIsDeleted() throws Exception {
|
||||
Account.Id accountId = Account.id(1);
|
||||
Project.NameKey allUsers = Project.nameKey(AllUsersNameProvider.DEFAULT);
|
||||
Repository allUsersRepo = repoManager.createRepository(allUsers);
|
||||
createRef(allUsersRepo, RefNames.refsUsers(accountId));
|
||||
|
||||
Account.Id accountId2 = Account.id(2);
|
||||
|
||||
AccountCache accountCache = mock(AccountCache.class);
|
||||
AccountIndexer accountIndexer = mock(AccountIndexer.class);
|
||||
|
||||
// Non-user branch because it's not in All-Users.
|
||||
Ref nonUserBranch = createRef(RefNames.refsUsers(Account.id(3)));
|
||||
|
||||
try (ProjectResetter resetProject =
|
||||
builder(null, accountCache, accountIndexer, null, null, null, null)
|
||||
.build(new ProjectResetter.Config().reset(project).reset(allUsers))) {
|
||||
updateRef(nonUserBranch);
|
||||
createRef(allUsersRepo, RefNames.REFS_EXTERNAL_IDS);
|
||||
createRef(allUsersRepo, RefNames.refsUsers(accountId2));
|
||||
}
|
||||
|
||||
verify(accountCache).evict(accountId);
|
||||
verify(accountCache).evict(accountId2);
|
||||
verify(accountIndexer).index(accountId);
|
||||
verify(accountIndexer).index(accountId2);
|
||||
verifyNoMoreInteractions(accountCache, accountIndexer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accountEvictionFromAccountCreatorIfUserBranchIsDeleted() throws Exception {
|
||||
Account.Id accountId = Account.id(1);
|
||||
Project.NameKey allUsers = Project.nameKey(AllUsersNameProvider.DEFAULT);
|
||||
Repository allUsersRepo = repoManager.createRepository(allUsers);
|
||||
|
||||
AccountCreator accountCreator = mock(AccountCreator.class);
|
||||
|
||||
try (ProjectResetter resetProject =
|
||||
builder(accountCreator, null, null, null, null, null, null)
|
||||
.build(new ProjectResetter.Config().reset(project).reset(allUsers))) {
|
||||
createRef(allUsersRepo, RefNames.refsUsers(accountId));
|
||||
}
|
||||
|
||||
verify(accountCreator, only()).evict(ImmutableSet.of(accountId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupEviction() throws Exception {
|
||||
AccountGroup.UUID uuid1 = AccountGroup.uuid("abcd1");
|
||||
|
||||
@@ -47,7 +47,6 @@ import static java.util.stream.Collectors.toSet;
|
||||
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
|
||||
|
||||
import com.github.rholder.retry.StopStrategies;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -143,7 +142,6 @@ import com.google.gerrit.testing.ConfigSuite;
|
||||
import com.google.gerrit.testing.FakeEmailSender.Message;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.name.Named;
|
||||
import com.jcraft.jsch.KeyPair;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -218,10 +216,6 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
|
||||
@Inject protected Emails emails;
|
||||
|
||||
@Inject
|
||||
@Named("accounts")
|
||||
private LoadingCache<Account.Id, AccountState> accountsCache;
|
||||
|
||||
@Inject private AccountOperations accountOperations;
|
||||
|
||||
@Inject protected GroupOperations groupOperations;
|
||||
@@ -2408,10 +2402,6 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
}
|
||||
|
||||
private void assertStaleAccountAndReindex(Account.Id accountId) throws IOException {
|
||||
// Evict account from cache to be sure that we use the index state for staleness checks. This
|
||||
// has to happen directly on the accounts cache because AccountCacheImpl triggers a reindex for
|
||||
// the account.
|
||||
accountsCache.invalidate(accountId);
|
||||
assertThat(stalenessChecker.check(accountId).isStale()).isTrue();
|
||||
|
||||
// Reindex fixes staleness
|
||||
|
||||
@@ -85,18 +85,6 @@ public class AccountIndexerIT {
|
||||
assertThat(matchedAccountStates.get(0).account().id()).isEqualTo(accountId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexingUpdatesStaleCache() throws Exception {
|
||||
Account.Id accountId = createAccount("foo");
|
||||
loadAccountToCache(accountId);
|
||||
String status = "ooo";
|
||||
updateAccountWithoutCacheOrIndex(accountId, newAccountUpdate().setStatus(status).build());
|
||||
assertThat(accountCache.get(accountId).get().account().status()).isNull();
|
||||
|
||||
accountIndexer.index(accountId);
|
||||
assertThat(accountCache.get(accountId).get().account().status()).isEqualTo(status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reindexingStaleAccountUpdatesTheIndex() throws Exception {
|
||||
Account.Id accountId = createAccount("foo");
|
||||
@@ -140,7 +128,6 @@ public class AccountIndexerIT {
|
||||
}
|
||||
|
||||
private void reloadAccountToCache(Account.Id accountId) {
|
||||
accountCache.evict(accountId);
|
||||
loadAccountToCache(accountId);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.acceptance.api.config;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
import com.google.gerrit.entities.RefNames;
|
||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
||||
import com.google.gerrit.server.config.CachedPreferences;
|
||||
import com.google.gerrit.server.config.DefaultPreferencesCache;
|
||||
import com.google.inject.Inject;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DefaultConfigCacheIT extends AbstractDaemonTest {
|
||||
@Inject DefaultPreferencesCache defaultPreferencesCache;
|
||||
|
||||
@Test
|
||||
public void invalidatesOldValue() throws Exception {
|
||||
CachedPreferences before = defaultPreferencesCache.get();
|
||||
DiffPreferencesInfo update = new DiffPreferencesInfo();
|
||||
update.lineLength = 123;
|
||||
gApi.config().server().setDefaultDiffPreferences(update);
|
||||
assertThat(before).isNotEqualTo(defaultPreferencesCache.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subsequentCallsReturnSameInstance() {
|
||||
assertThat(defaultPreferencesCache.get()).isSameInstanceAs(defaultPreferencesCache.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canLoadAtSpecificRev() throws Exception {
|
||||
// Set a value to make sure we have custom preferences set
|
||||
DiffPreferencesInfo update = new DiffPreferencesInfo();
|
||||
update.lineLength = 1337;
|
||||
gApi.config().server().setDefaultDiffPreferences(update);
|
||||
|
||||
ObjectId oldRev = currentRev();
|
||||
CachedPreferences before = defaultPreferencesCache.get();
|
||||
|
||||
// Mutate the preferences
|
||||
DiffPreferencesInfo update2 = new DiffPreferencesInfo();
|
||||
update2.lineLength = 815;
|
||||
gApi.config().server().setDefaultDiffPreferences(update2);
|
||||
|
||||
assertThat(oldRev).isNotEqualTo(currentRev());
|
||||
assertThat(defaultPreferencesCache.get()).isNotEqualTo(before);
|
||||
assertThat(defaultPreferencesCache.get(oldRev)).isEqualTo(before);
|
||||
}
|
||||
|
||||
private ObjectId currentRev() throws Exception {
|
||||
try (Repository repo = repoManager.openRepository(allUsers)) {
|
||||
return repo.exactRef(RefNames.REFS_USERS_DEFAULT).getObjectId();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.entities;
|
||||
|
||||
import com.google.common.truth.Truth;
|
||||
import com.google.common.truth.extensions.proto.ProtoTruth;
|
||||
import com.google.gerrit.server.cache.proto.Cache.AccountProto;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test to ensure that we are serializing and deserializing {@link Account} correctly. This is part
|
||||
* of the {@code AccountCache}.
|
||||
*/
|
||||
public class AccountCacheTest {
|
||||
@Test
|
||||
public void roundTrip() throws Exception {
|
||||
Account account =
|
||||
Account.builder(Account.id(1), Timestamp.from(Instant.EPOCH))
|
||||
.setFullName("foo bar")
|
||||
.setDisplayName("foo")
|
||||
.setActive(false)
|
||||
.setMetaId("dead..beef")
|
||||
.setStatus("OOO")
|
||||
.setPreferredEmail("foo@bar.tld")
|
||||
.build();
|
||||
byte[] serialized = Account.Serializer.INSTANCE.serialize(account);
|
||||
ProtoTruth.assertThat(AccountProto.parseFrom(serialized))
|
||||
.isEqualTo(
|
||||
AccountProto.newBuilder()
|
||||
.setId(1)
|
||||
.setRegisteredOn(0)
|
||||
.setFullName("foo bar")
|
||||
.setDisplayName("foo")
|
||||
.setInactive(true)
|
||||
.setMetaId("dead..beef")
|
||||
.setStatus("OOO")
|
||||
.setPreferredEmail("foo@bar.tld")
|
||||
.build());
|
||||
Truth.assertThat(Account.Serializer.INSTANCE.deserialize(serialized)).isEqualTo(account);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripNullFields() throws Exception {
|
||||
Account account = Account.builder(Account.id(1), Timestamp.from(Instant.EPOCH)).build();
|
||||
byte[] serialized = Account.Serializer.INSTANCE.serialize(account);
|
||||
ProtoTruth.assertThat(AccountProto.parseFrom(serialized))
|
||||
.isEqualTo(AccountProto.newBuilder().setId(1).setRegisteredOn(0).build());
|
||||
Truth.assertThat(Account.Serializer.INSTANCE.deserialize(serialized)).isEqualTo(account);
|
||||
}
|
||||
}
|
||||
145
javatests/com/google/gerrit/server/account/AccountCacheTest.java
Normal file
145
javatests/com/google/gerrit/server/account/AccountCacheTest.java
Normal file
@@ -0,0 +1,145 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.account;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.truth.Truth;
|
||||
import com.google.common.truth.extensions.proto.ProtoTruth;
|
||||
import com.google.gerrit.entities.Account;
|
||||
import com.google.gerrit.entities.Project;
|
||||
import com.google.gerrit.server.cache.proto.Cache;
|
||||
import com.google.gerrit.server.config.CachedPreferences;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test to ensure that we are serializing and deserializing {@link Account} correctly. This is part
|
||||
* of the {@code AccountCache}.
|
||||
*/
|
||||
public class AccountCacheTest {
|
||||
private static final Account ACCOUNT =
|
||||
Account.builder(Account.id(1), Timestamp.from(Instant.EPOCH)).build();
|
||||
private static final Cache.AccountProto ACCOUNT_PROTO =
|
||||
Cache.AccountProto.newBuilder().setId(1).setRegisteredOn(0).build();
|
||||
private static final CachedAccountDetails.Serializer SERIALIZER =
|
||||
CachedAccountDetails.Serializer.INSTANCE;
|
||||
|
||||
@Test
|
||||
public void account_roundTrip() throws Exception {
|
||||
Account account =
|
||||
Account.builder(Account.id(1), Timestamp.from(Instant.EPOCH))
|
||||
.setFullName("foo bar")
|
||||
.setDisplayName("foo")
|
||||
.setActive(false)
|
||||
.setMetaId("dead..beef")
|
||||
.setStatus("OOO")
|
||||
.setPreferredEmail("foo@bar.tld")
|
||||
.build();
|
||||
CachedAccountDetails original =
|
||||
CachedAccountDetails.create(account, ImmutableMap.of(), CachedPreferences.fromString(""));
|
||||
byte[] serialized = SERIALIZER.serialize(original);
|
||||
Cache.AccountDetailsProto expected =
|
||||
Cache.AccountDetailsProto.newBuilder()
|
||||
.setAccount(
|
||||
Cache.AccountProto.newBuilder()
|
||||
.setId(1)
|
||||
.setRegisteredOn(0)
|
||||
.setFullName("foo bar")
|
||||
.setDisplayName("foo")
|
||||
.setInactive(true)
|
||||
.setMetaId("dead..beef")
|
||||
.setStatus("OOO")
|
||||
.setPreferredEmail("foo@bar.tld"))
|
||||
.build();
|
||||
ProtoTruth.assertThat(Cache.AccountDetailsProto.parseFrom(serialized)).isEqualTo(expected);
|
||||
Truth.assertThat(SERIALIZER.deserialize(serialized)).isEqualTo(original);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void account_roundTripNullFields() throws Exception {
|
||||
CachedAccountDetails original =
|
||||
CachedAccountDetails.create(ACCOUNT, ImmutableMap.of(), CachedPreferences.fromString(""));
|
||||
byte[] serialized = SERIALIZER.serialize(original);
|
||||
Cache.AccountDetailsProto expected =
|
||||
Cache.AccountDetailsProto.newBuilder().setAccount(ACCOUNT_PROTO).build();
|
||||
ProtoTruth.assertThat(Cache.AccountDetailsProto.parseFrom(serialized)).isEqualTo(expected);
|
||||
Truth.assertThat(SERIALIZER.deserialize(serialized)).isEqualTo(original);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void config_roundTrip() throws Exception {
|
||||
CachedAccountDetails original =
|
||||
CachedAccountDetails.create(
|
||||
ACCOUNT, ImmutableMap.of(), CachedPreferences.fromString("[general]\n\tfoo = bar"));
|
||||
|
||||
byte[] serialized = SERIALIZER.serialize(original);
|
||||
Cache.AccountDetailsProto expected =
|
||||
Cache.AccountDetailsProto.newBuilder()
|
||||
.setAccount(ACCOUNT_PROTO)
|
||||
.setUserPreferences("[general]\n\tfoo = bar")
|
||||
.build();
|
||||
ProtoTruth.assertThat(Cache.AccountDetailsProto.parseFrom(serialized)).isEqualTo(expected);
|
||||
Truth.assertThat(SERIALIZER.deserialize(serialized)).isEqualTo(original);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectWatch_roundTrip() throws Exception {
|
||||
ProjectWatches.ProjectWatchKey key =
|
||||
ProjectWatches.ProjectWatchKey.create(Project.nameKey("pro/ject"), "*");
|
||||
CachedAccountDetails original =
|
||||
CachedAccountDetails.create(
|
||||
ACCOUNT,
|
||||
ImmutableMap.of(key, ImmutableSet.of(ProjectWatches.NotifyType.ALL_COMMENTS)),
|
||||
CachedPreferences.fromString(""));
|
||||
|
||||
byte[] serialized = SERIALIZER.serialize(original);
|
||||
Cache.AccountDetailsProto expected =
|
||||
Cache.AccountDetailsProto.newBuilder()
|
||||
.setAccount(ACCOUNT_PROTO)
|
||||
.addProjectWatchProto(
|
||||
Cache.ProjectWatchProto.newBuilder()
|
||||
.setProject("pro/ject")
|
||||
.setFilter("*")
|
||||
.addNotifyType("ALL_COMMENTS"))
|
||||
.build();
|
||||
ProtoTruth.assertThat(Cache.AccountDetailsProto.parseFrom(serialized)).isEqualTo(expected);
|
||||
Truth.assertThat(SERIALIZER.deserialize(serialized)).isEqualTo(original);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectWatch_roundTripNullFilter() throws Exception {
|
||||
ProjectWatches.ProjectWatchKey key =
|
||||
ProjectWatches.ProjectWatchKey.create(Project.nameKey("pro/ject"), null);
|
||||
CachedAccountDetails original =
|
||||
CachedAccountDetails.create(
|
||||
ACCOUNT,
|
||||
ImmutableMap.of(key, ImmutableSet.of(ProjectWatches.NotifyType.ALL_COMMENTS)),
|
||||
CachedPreferences.fromString(""));
|
||||
|
||||
byte[] serialized = SERIALIZER.serialize(original);
|
||||
Cache.AccountDetailsProto expected =
|
||||
Cache.AccountDetailsProto.newBuilder()
|
||||
.setAccount(ACCOUNT_PROTO)
|
||||
.addProjectWatchProto(
|
||||
Cache.ProjectWatchProto.newBuilder()
|
||||
.setProject("pro/ject")
|
||||
.addNotifyType("ALL_COMMENTS"))
|
||||
.build();
|
||||
ProtoTruth.assertThat(Cache.AccountDetailsProto.parseFrom(serialized)).isEqualTo(expected);
|
||||
Truth.assertThat(SERIALIZER.deserialize(serialized)).isEqualTo(original);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright (C) 2019 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.account;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.EditPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.json.OutputFormat;
|
||||
import com.google.gson.Gson;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PreferencesTest {
|
||||
|
||||
private static final Gson GSON = OutputFormat.JSON_COMPACT.newGson();
|
||||
|
||||
@Test
|
||||
public void generalPreferencesRoundTrip() {
|
||||
GeneralPreferencesInfo original = GeneralPreferencesInfo.defaults();
|
||||
assertThat(GSON.toJson(original))
|
||||
.isEqualTo(GSON.toJson(Preferences.General.fromInfo(original).toInfo()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void diffPreferencesRoundTrip() {
|
||||
DiffPreferencesInfo original = DiffPreferencesInfo.defaults();
|
||||
assertThat(GSON.toJson(original))
|
||||
.isEqualTo(GSON.toJson(Preferences.Diff.fromInfo(original).toInfo()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void editPreferencesRoundTrip() {
|
||||
EditPreferencesInfo original = EditPreferencesInfo.defaults();
|
||||
assertThat(GSON.toJson(original))
|
||||
.isEqualTo(GSON.toJson(Preferences.Edit.fromInfo(original).toInfo()));
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.account;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.common.truth.extensions.proto.ProtoTruth;
|
||||
import com.google.gerrit.entities.Project;
|
||||
import com.google.gerrit.server.cache.proto.Cache.ProjectWatchKeyProto;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test to ensure that we are serializing and deserializing {@link ProjectWatches.ProjectWatchKey}
|
||||
* correctly. This is part of the {@code AccountCache}.
|
||||
*/
|
||||
public class ProjectWatchCacheTest {
|
||||
@Test
|
||||
public void keyRoundTrip() throws Exception {
|
||||
ProjectWatches.ProjectWatchKey key =
|
||||
ProjectWatches.ProjectWatchKey.create(Project.nameKey("pro/ject"), "*");
|
||||
byte[] serialized = ProjectWatches.ProjectWatchKey.Serializer.INSTANCE.serialize(key);
|
||||
ProtoTruth.assertThat(ProjectWatchKeyProto.parseFrom(serialized))
|
||||
.isEqualTo(ProjectWatchKeyProto.newBuilder().setProject("pro/ject").setFilter("*").build());
|
||||
assertThat(ProjectWatches.ProjectWatchKey.Serializer.INSTANCE.deserialize(serialized))
|
||||
.isEqualTo(key);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keyRoundTripNullFilter() throws Exception {
|
||||
ProjectWatches.ProjectWatchKey key =
|
||||
ProjectWatches.ProjectWatchKey.create(Project.nameKey("pro/ject"), null);
|
||||
byte[] serialized = ProjectWatches.ProjectWatchKey.Serializer.INSTANCE.serialize(key);
|
||||
ProtoTruth.assertThat(ProjectWatchKeyProto.parseFrom(serialized))
|
||||
.isEqualTo(ProjectWatchKeyProto.newBuilder().setProject("pro/ject").build());
|
||||
assertThat(ProjectWatches.ProjectWatchKey.Serializer.INSTANCE.deserialize(serialized))
|
||||
.isEqualTo(key);
|
||||
}
|
||||
}
|
||||
@@ -790,7 +790,6 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
|
||||
for (String email : emails) {
|
||||
accountManager.link(id, AuthRequest.forEmail(email));
|
||||
}
|
||||
accountCache.evict(id);
|
||||
accountIndexer.index(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -287,11 +287,12 @@ message PureRevertKeyProto {
|
||||
bytes claimed_revert = 3;
|
||||
}
|
||||
|
||||
// Key for com.google.gerrit.server.account.ProjectWatches.ProjectWatcheKey.
|
||||
// Next ID: 3
|
||||
message ProjectWatchKeyProto {
|
||||
// Key for com.google.gerrit.server.account.ProjectWatches.
|
||||
// Next ID: 4
|
||||
message ProjectWatchProto {
|
||||
string project = 1;
|
||||
string filter = 2;
|
||||
repeated string notify_type = 3;
|
||||
}
|
||||
|
||||
// Serialized form of
|
||||
@@ -307,3 +308,18 @@ message AccountProto {
|
||||
string status = 7;
|
||||
string meta_id = 8;
|
||||
}
|
||||
|
||||
// Serialized form of com.google.gerrit.server.account.CachedAccountDetails.Key.
|
||||
// Next ID: 3
|
||||
message AccountKeyProto {
|
||||
int32 account_id = 1;
|
||||
bytes id = 2;
|
||||
}
|
||||
|
||||
// Serialized form of com.google.gerrit.server.account.CachedAccountDetails.
|
||||
// Next ID: 4
|
||||
message AccountDetailsProto {
|
||||
AccountProto account = 1;
|
||||
repeated ProjectWatchProto project_watch_proto = 2;
|
||||
string user_preferences = 3;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user