Merge changes Ic063facd,Ie4feae69,I090bb11e,Iedb4bf4d,Ic0f2adfe, ...
* changes: Move general preferences from Account to AccountState Accounts: Load AccountState instead of Account ExternalIds: Allow to specify revision for getting ext IDs by account AccountConfig: Read revision of the external IDs branch Allow to update accounts and general preferences atomically ExternalIdNotes: Make constructor and load() method private Allow to update accounts and project watches atomically
This commit is contained in:
@@ -183,9 +183,9 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
|
||||
return HtmlDomUtil.toUTF8(doc);
|
||||
}
|
||||
|
||||
private AuthResult auth(Account account) {
|
||||
private AuthResult auth(AccountState account) {
|
||||
if (account != null) {
|
||||
return new AuthResult(account.getId(), null, false);
|
||||
return new AuthResult(account.getAccount().getId(), null, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -218,13 +218,8 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
|
||||
|
||||
private AuthResult byPreferredEmail(String email) {
|
||||
try (ReviewDb db = schema.open()) {
|
||||
Optional<Account> match =
|
||||
queryProvider
|
||||
.get()
|
||||
.byPreferredEmail(email)
|
||||
.stream()
|
||||
.map(AccountState::getAccount)
|
||||
.findFirst();
|
||||
Optional<AccountState> match =
|
||||
queryProvider.get().byPreferredEmail(email).stream().findFirst();
|
||||
return match.isPresent() ? auth(match.get()) : null;
|
||||
} catch (OrmException e) {
|
||||
getServletContext().log("cannot query database", e);
|
||||
|
@@ -70,7 +70,7 @@ public class AccountsOnInit {
|
||||
new GerritPersonIdentProvider(flags.cfg).get(), account.getRegisteredOn());
|
||||
|
||||
Config accountConfig = new Config();
|
||||
AccountConfig.writeToConfig(
|
||||
AccountConfig.writeToAccountConfig(
|
||||
InternalAccountUpdate.builder()
|
||||
.setActive(account.isActive())
|
||||
.setFullName(account.getFullName())
|
||||
|
@@ -21,6 +21,7 @@ import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.common.data.GroupReference;
|
||||
import com.google.gerrit.common.errors.NoSuchGroupException;
|
||||
import com.google.gerrit.extensions.client.AuthType;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
|
||||
import com.google.gerrit.pgm.init.api.ConsoleUI;
|
||||
import com.google.gerrit.pgm.init.api.InitFlags;
|
||||
@@ -151,7 +152,12 @@ public class InitAdminUser implements InitStep {
|
||||
}
|
||||
|
||||
AccountState as =
|
||||
new AccountState(new AllUsersName(allUsers.get()), a, extIds, new HashMap<>());
|
||||
new AccountState(
|
||||
new AllUsersName(allUsers.get()),
|
||||
a,
|
||||
extIds,
|
||||
new HashMap<>(),
|
||||
GeneralPreferencesInfo.defaults());
|
||||
for (AccountIndex accountIndex : accountIndexCollection.getWriteIndexes()) {
|
||||
accountIndex.replace(as);
|
||||
}
|
||||
|
@@ -19,7 +19,6 @@ import static com.google.gerrit.reviewdb.client.RefNames.REFS_STARRED_CHANGES;
|
||||
import static com.google.gerrit.reviewdb.client.RefNames.REFS_USERS;
|
||||
|
||||
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gwtorm.client.Column;
|
||||
import com.google.gwtorm.client.IntKey;
|
||||
import java.sql.Timestamp;
|
||||
@@ -180,9 +179,6 @@ public final class Account {
|
||||
/** <i>computed</i> the username selected from the identities. */
|
||||
protected String userName;
|
||||
|
||||
/** <i>stored in git, used for caching</i> the user's preferences. */
|
||||
private GeneralPreferencesInfo generalPreferences;
|
||||
|
||||
/**
|
||||
* ID of the user branch from which the account was read, {@code null} if the account was read
|
||||
* from ReviewDb.
|
||||
@@ -286,14 +282,6 @@ public final class Account {
|
||||
return registeredOn;
|
||||
}
|
||||
|
||||
public GeneralPreferencesInfo getGeneralPreferencesInfo() {
|
||||
return generalPreferences;
|
||||
}
|
||||
|
||||
public void setGeneralPreferences(GeneralPreferencesInfo p) {
|
||||
generalPreferences = p;
|
||||
}
|
||||
|
||||
public String getMetaId() {
|
||||
return metaId;
|
||||
}
|
||||
|
@@ -130,50 +130,25 @@ public class AccountCacheImpl implements AccountCache {
|
||||
private AccountState missing(Account.Id accountId) {
|
||||
Account account = new Account(accountId, TimeUtil.nowTs());
|
||||
account.setActive(false);
|
||||
return new AccountState(allUsersName, account, Collections.emptySet(), new HashMap<>());
|
||||
return new AccountState(
|
||||
allUsersName,
|
||||
account,
|
||||
Collections.emptySet(),
|
||||
new HashMap<>(),
|
||||
GeneralPreferencesInfo.defaults());
|
||||
}
|
||||
|
||||
static class ByIdLoader extends CacheLoader<Account.Id, Optional<AccountState>> {
|
||||
private final AllUsersName allUsersName;
|
||||
private final Accounts accounts;
|
||||
private final GeneralPreferencesLoader loader;
|
||||
private final Provider<WatchConfig.Accessor> watchConfig;
|
||||
private final ExternalIds externalIds;
|
||||
|
||||
@Inject
|
||||
ByIdLoader(
|
||||
AllUsersName allUsersName,
|
||||
Accounts accounts,
|
||||
GeneralPreferencesLoader loader,
|
||||
Provider<WatchConfig.Accessor> watchConfig,
|
||||
ExternalIds externalIds) {
|
||||
this.allUsersName = allUsersName;
|
||||
ByIdLoader(Accounts accounts) {
|
||||
this.accounts = accounts;
|
||||
this.loader = loader;
|
||||
this.watchConfig = watchConfig;
|
||||
this.externalIds = externalIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<AccountState> load(Account.Id who) throws Exception {
|
||||
Account account = accounts.get(who);
|
||||
if (account == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
try {
|
||||
account.setGeneralPreferences(loader.load(who));
|
||||
} catch (IOException | ConfigInvalidException e) {
|
||||
log.warn("Cannot load GeneralPreferences for " + who + " (using default)", e);
|
||||
account.setGeneralPreferences(GeneralPreferencesInfo.defaults());
|
||||
}
|
||||
|
||||
return Optional.of(
|
||||
new AccountState(
|
||||
allUsersName,
|
||||
account,
|
||||
externalIds.byAccount(who),
|
||||
watchConfig.get().getProjectWatches(who)));
|
||||
return Optional.ofNullable(accounts.get(who));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,19 +18,32 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.account.WatchConfig.NotifyType;
|
||||
import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
|
||||
import com.google.gerrit.server.account.externalids.ExternalIds;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.ValidationError;
|
||||
import com.google.gerrit.server.git.VersionedMetaData;
|
||||
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevSort;
|
||||
|
||||
@@ -61,7 +74,7 @@ import org.eclipse.jgit.revwalk.RevSort;
|
||||
* account. The first commit may be an empty commit (if no properties were set and 'account.config'
|
||||
* doesn't exist).
|
||||
*/
|
||||
public class AccountConfig extends VersionedMetaData {
|
||||
public class AccountConfig extends VersionedMetaData implements ValidationError.Sink {
|
||||
public static final String ACCOUNT_CONFIG = "account.config";
|
||||
public static final String ACCOUNT = "account";
|
||||
public static final String KEY_ACTIVE = "active";
|
||||
@@ -70,22 +83,49 @@ public class AccountConfig extends VersionedMetaData {
|
||||
public static final String KEY_STATUS = "status";
|
||||
|
||||
private final Account.Id accountId;
|
||||
private final Repository repo;
|
||||
private final String ref;
|
||||
|
||||
private Optional<Account> loadedAccount;
|
||||
private Optional<ObjectId> externalIdsRev;
|
||||
private WatchConfig watchConfig;
|
||||
private PreferencesConfig prefConfig;
|
||||
private Optional<InternalAccountUpdate> accountUpdate = Optional.empty();
|
||||
private Timestamp registeredOn;
|
||||
private boolean eagerParsing;
|
||||
private List<ValidationError> validationErrors;
|
||||
|
||||
public AccountConfig(Account.Id accountId) {
|
||||
this.accountId = checkNotNull(accountId);
|
||||
public AccountConfig(Account.Id accountId, Repository allUsersRepo) {
|
||||
this.accountId = checkNotNull(accountId, "accountId");
|
||||
this.repo = checkNotNull(allUsersRepo, "allUsersRepo");
|
||||
this.ref = RefNames.refsUsers(accountId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether all account data should be eagerly parsed.
|
||||
*
|
||||
* <p>Eager parsing should only be used if the caller is interested in validation errors for all
|
||||
* account data (see {@link #getValidationErrors()}.
|
||||
*
|
||||
* @param eagerParsing whether all account data should be eagerly parsed
|
||||
* @return this AccountConfig instance for chaining
|
||||
*/
|
||||
public AccountConfig setEagerParsing(boolean eagerParsing) {
|
||||
checkState(loadedAccount == null, "Account %s already loaded", accountId.get());
|
||||
this.eagerParsing = eagerParsing;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRefName() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public AccountConfig load() throws IOException, ConfigInvalidException {
|
||||
load(repo);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the loaded account.
|
||||
*
|
||||
@@ -98,6 +138,40 @@ public class AccountConfig extends VersionedMetaData {
|
||||
return loadedAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the revision of the {@code refs/meta/external-ids} branch.
|
||||
*
|
||||
* <p>This revision can be used to load the external IDs of the loaded account lazily via {@link
|
||||
* ExternalIds#byAccount(com.google.gerrit.reviewdb.client.Account.Id, ObjectId)}.
|
||||
*
|
||||
* @return revision of the {@code refs/meta/external-ids} branch, {@link Optional#empty()} if no
|
||||
* {@code refs/meta/external-ids} branch exists
|
||||
*/
|
||||
public Optional<ObjectId> getExternalIdsRev() {
|
||||
checkLoaded();
|
||||
return externalIdsRev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the project watches of the loaded account.
|
||||
*
|
||||
* @return the project watches of the loaded account
|
||||
*/
|
||||
public Map<ProjectWatchKey, Set<NotifyType>> getProjectWatches() {
|
||||
checkLoaded();
|
||||
return watchConfig.getProjectWatches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the general preferences of the loaded account.
|
||||
*
|
||||
* @return the general preferences of the loaded account
|
||||
*/
|
||||
public GeneralPreferencesInfo getGeneralPreferences() {
|
||||
checkLoaded();
|
||||
return prefConfig.getGeneralPreferences();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the account. This means the loaded account will be overwritten with the given account.
|
||||
*
|
||||
@@ -106,7 +180,7 @@ public class AccountConfig extends VersionedMetaData {
|
||||
* @param account account that should be set
|
||||
* @throws IllegalStateException if the account was not loaded yet
|
||||
*/
|
||||
public void setAccount(Account account) {
|
||||
public AccountConfig setAccount(Account account) {
|
||||
checkLoaded();
|
||||
this.loadedAccount = Optional.of(account);
|
||||
this.accountUpdate =
|
||||
@@ -118,6 +192,7 @@ public class AccountConfig extends VersionedMetaData {
|
||||
.setStatus(account.getStatus())
|
||||
.build());
|
||||
this.registeredOn = account.getRegisteredOn();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,8 +221,9 @@ public class AccountConfig extends VersionedMetaData {
|
||||
return loadedAccount.get();
|
||||
}
|
||||
|
||||
public void setAccountUpdate(InternalAccountUpdate accountUpdate) {
|
||||
public AccountConfig setAccountUpdate(InternalAccountUpdate accountUpdate) {
|
||||
this.accountUpdate = Optional.of(accountUpdate);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -158,9 +234,26 @@ public class AccountConfig extends VersionedMetaData {
|
||||
rw.sort(RevSort.REVERSE);
|
||||
registeredOn = new Timestamp(rw.next().getCommitTime() * 1000L);
|
||||
|
||||
Config cfg = readConfig(ACCOUNT_CONFIG);
|
||||
Config accountConfig = readConfig(ACCOUNT_CONFIG);
|
||||
loadedAccount = Optional.of(parse(accountConfig, revision.name()));
|
||||
|
||||
loadedAccount = Optional.of(parse(cfg, revision.name()));
|
||||
Ref externalIdsRef = repo.exactRef(RefNames.REFS_EXTERNAL_IDS);
|
||||
externalIdsRev =
|
||||
externalIdsRef != null ? Optional.of(externalIdsRef.getObjectId()) : Optional.empty();
|
||||
|
||||
watchConfig = new WatchConfig(accountId, readConfig(WatchConfig.WATCH_CONFIG), this);
|
||||
|
||||
prefConfig =
|
||||
new PreferencesConfig(
|
||||
accountId,
|
||||
readConfig(PreferencesConfig.PREFERENCES_CONFIG),
|
||||
PreferencesConfig.readDefaultConfig(repo),
|
||||
this);
|
||||
|
||||
if (eagerParsing) {
|
||||
watchConfig.parse();
|
||||
prefConfig.parse();
|
||||
}
|
||||
} else {
|
||||
loadedAccount = Optional.empty();
|
||||
}
|
||||
@@ -207,21 +300,28 @@ public class AccountConfig extends VersionedMetaData {
|
||||
commit.setCommitter(new PersonIdent(commit.getCommitter(), registeredOn));
|
||||
}
|
||||
|
||||
Config cfg = readConfig(ACCOUNT_CONFIG);
|
||||
if (accountUpdate.isPresent()) {
|
||||
writeToConfig(accountUpdate.get(), cfg);
|
||||
}
|
||||
saveConfig(ACCOUNT_CONFIG, cfg);
|
||||
Config accountConfig = saveAccount();
|
||||
saveProjectWatches();
|
||||
saveGeneralPreferences();
|
||||
|
||||
// metaId is set in the commit(MetaDataUpdate) method after the commit is created
|
||||
loadedAccount = Optional.of(parse(cfg, null));
|
||||
loadedAccount = Optional.of(parse(accountConfig, null));
|
||||
|
||||
accountUpdate = Optional.empty();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void writeToConfig(InternalAccountUpdate accountUpdate, Config cfg) {
|
||||
private Config saveAccount() throws IOException, ConfigInvalidException {
|
||||
Config accountConfig = readConfig(ACCOUNT_CONFIG);
|
||||
if (accountUpdate.isPresent()) {
|
||||
writeToAccountConfig(accountUpdate.get(), accountConfig);
|
||||
}
|
||||
saveConfig(ACCOUNT_CONFIG, accountConfig);
|
||||
return accountConfig;
|
||||
}
|
||||
|
||||
public static void writeToAccountConfig(InternalAccountUpdate accountUpdate, Config cfg) {
|
||||
accountUpdate.getActive().ifPresent(active -> setActive(cfg, active));
|
||||
accountUpdate.getFullName().ifPresent(fullName -> set(cfg, KEY_FULL_NAME, fullName));
|
||||
accountUpdate
|
||||
@@ -230,6 +330,28 @@ public class AccountConfig extends VersionedMetaData {
|
||||
accountUpdate.getStatus().ifPresent(status -> set(cfg, KEY_STATUS, status));
|
||||
}
|
||||
|
||||
private void saveProjectWatches() throws IOException {
|
||||
if (accountUpdate.isPresent()
|
||||
&& (!accountUpdate.get().getDeletedProjectWatches().isEmpty()
|
||||
|| !accountUpdate.get().getUpdatedProjectWatches().isEmpty())) {
|
||||
Map<ProjectWatchKey, Set<NotifyType>> projectWatches = watchConfig.getProjectWatches();
|
||||
accountUpdate.get().getDeletedProjectWatches().forEach(pw -> projectWatches.remove(pw));
|
||||
accountUpdate
|
||||
.get()
|
||||
.getUpdatedProjectWatches()
|
||||
.forEach((pw, nt) -> projectWatches.put(pw, nt));
|
||||
saveConfig(WatchConfig.WATCH_CONFIG, watchConfig.save(projectWatches));
|
||||
}
|
||||
}
|
||||
|
||||
private void saveGeneralPreferences() throws IOException, ConfigInvalidException {
|
||||
if (accountUpdate.isPresent() && accountUpdate.get().getGeneralPreferences().isPresent()) {
|
||||
saveConfig(
|
||||
PreferencesConfig.PREFERENCES_CONFIG,
|
||||
prefConfig.saveGeneralPreferences(accountUpdate.get().getGeneralPreferences().get()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets/Unsets {@code account.active} in the given config.
|
||||
*
|
||||
@@ -282,4 +404,27 @@ public class AccountConfig extends VersionedMetaData {
|
||||
private void checkLoaded() {
|
||||
checkState(loadedAccount != null, "Account %s not loaded yet", accountId.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation errors, if any were discovered during parsing the account data.
|
||||
*
|
||||
* <p>To get validation errors for all account data request eager parsing before loading the
|
||||
* account (see {@link #setEagerParsing(boolean)}).
|
||||
*
|
||||
* @return list of errors; empty list if there are no errors.
|
||||
*/
|
||||
public List<ValidationError> getValidationErrors() {
|
||||
if (validationErrors != null) {
|
||||
return ImmutableList.copyOf(validationErrors);
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(ValidationError error) {
|
||||
if (validationErrors == null) {
|
||||
validationErrors = new ArrayList<>(4);
|
||||
}
|
||||
validationErrors.add(error);
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ import com.google.common.base.Strings;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.CurrentUser.PropertyKey;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
@@ -51,17 +52,20 @@ public class AccountState {
|
||||
private final Account account;
|
||||
private final Collection<ExternalId> externalIds;
|
||||
private final Map<ProjectWatchKey, Set<NotifyType>> projectWatches;
|
||||
private final GeneralPreferencesInfo generalPreferences;
|
||||
private Cache<IdentifiedUser.PropertyKey<Object>, Object> properties;
|
||||
|
||||
public AccountState(
|
||||
AllUsersName allUsersName,
|
||||
Account account,
|
||||
Collection<ExternalId> externalIds,
|
||||
Map<ProjectWatchKey, Set<NotifyType>> projectWatches) {
|
||||
Map<ProjectWatchKey, Set<NotifyType>> projectWatches,
|
||||
GeneralPreferencesInfo generalPreferences) {
|
||||
this.allUsersName = allUsersName;
|
||||
this.account = account;
|
||||
this.externalIds = externalIds;
|
||||
this.projectWatches = projectWatches;
|
||||
this.generalPreferences = generalPreferences;
|
||||
this.account.setUserName(getUserName(externalIds));
|
||||
}
|
||||
|
||||
@@ -117,6 +121,11 @@ public class AccountState {
|
||||
return projectWatches;
|
||||
}
|
||||
|
||||
/** The general preferences of the account. */
|
||||
public GeneralPreferencesInfo getGeneralPreferences() {
|
||||
return generalPreferences;
|
||||
}
|
||||
|
||||
public static String getUserName(Collection<ExternalId> ids) {
|
||||
for (ExternalId extId : ids) {
|
||||
if (extId.isScheme(SCHEME_USERNAME)) {
|
||||
|
@@ -18,9 +18,11 @@ import static java.util.Comparator.comparing;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.account.externalids.ExternalIds;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.inject.Inject;
|
||||
@@ -45,23 +47,25 @@ public class Accounts {
|
||||
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final AllUsersName allUsersName;
|
||||
private final ExternalIds externalIds;
|
||||
|
||||
@Inject
|
||||
Accounts(GitRepositoryManager repoManager, AllUsersName allUsersName) {
|
||||
Accounts(GitRepositoryManager repoManager, AllUsersName allUsersName, ExternalIds externalIds) {
|
||||
this.repoManager = repoManager;
|
||||
this.allUsersName = allUsersName;
|
||||
this.externalIds = externalIds;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Account get(Account.Id accountId) throws IOException, ConfigInvalidException {
|
||||
public AccountState get(Account.Id accountId) throws IOException, ConfigInvalidException {
|
||||
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||
return read(repo, accountId).orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Account> get(Collection<Account.Id> accountIds)
|
||||
public List<AccountState> get(Collection<Account.Id> accountIds)
|
||||
throws IOException, ConfigInvalidException {
|
||||
List<Account> accounts = new ArrayList<>(accountIds.size());
|
||||
List<AccountState> accounts = new ArrayList<>(accountIds.size());
|
||||
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||
for (Account.Id accountId : accountIds) {
|
||||
read(repo, accountId).ifPresent(accounts::add);
|
||||
@@ -75,9 +79,9 @@ public class Accounts {
|
||||
*
|
||||
* @return all accounts
|
||||
*/
|
||||
public List<Account> all() throws IOException {
|
||||
public List<AccountState> all() throws IOException {
|
||||
Set<Account.Id> accountIds = allIds();
|
||||
List<Account> accounts = new ArrayList<>(accountIds.size());
|
||||
List<AccountState> accounts = new ArrayList<>(accountIds.size());
|
||||
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||
for (Account.Id accountId : accountIds) {
|
||||
try {
|
||||
@@ -130,11 +134,22 @@ public class Accounts {
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<Account> read(Repository allUsersRepository, Account.Id accountId)
|
||||
private Optional<AccountState> read(Repository allUsersRepository, Account.Id accountId)
|
||||
throws IOException, ConfigInvalidException {
|
||||
AccountConfig accountConfig = new AccountConfig(accountId);
|
||||
accountConfig.load(allUsersRepository);
|
||||
return accountConfig.getLoadedAccount();
|
||||
AccountConfig accountConfig = new AccountConfig(accountId, allUsersRepository).load();
|
||||
if (!accountConfig.getLoadedAccount().isPresent()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Account account = accountConfig.getLoadedAccount().get();
|
||||
return Optional.of(
|
||||
new AccountState(
|
||||
allUsersName,
|
||||
account,
|
||||
accountConfig.getExternalIdsRev().isPresent()
|
||||
? externalIds.byAccount(accountId, accountConfig.getExternalIdsRev().get())
|
||||
: ImmutableSet.of(),
|
||||
accountConfig.getProjectWatches(),
|
||||
accountConfig.getGeneralPreferences()));
|
||||
}
|
||||
|
||||
public static Stream<Account.Id> readUserRefs(Repository repo) throws IOException {
|
||||
|
@@ -16,7 +16,6 @@ package com.google.gerrit.server.account;
|
||||
|
||||
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.account.externalids.ExternalIds;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
@@ -26,21 +25,20 @@ import java.util.List;
|
||||
@Singleton
|
||||
public class AccountsConsistencyChecker {
|
||||
private final Accounts accounts;
|
||||
private final ExternalIds externalIds;
|
||||
|
||||
@Inject
|
||||
AccountsConsistencyChecker(Accounts accounts, ExternalIds externalIds) {
|
||||
AccountsConsistencyChecker(Accounts accounts) {
|
||||
this.accounts = accounts;
|
||||
this.externalIds = externalIds;
|
||||
}
|
||||
|
||||
public List<ConsistencyProblemInfo> check() throws IOException {
|
||||
List<ConsistencyProblemInfo> problems = new ArrayList<>();
|
||||
|
||||
for (Account account : accounts.all()) {
|
||||
for (AccountState accountState : accounts.all()) {
|
||||
Account account = accountState.getAccount();
|
||||
if (account.getPreferredEmail() != null) {
|
||||
if (!externalIds
|
||||
.byAccount(account.getId())
|
||||
if (!accountState
|
||||
.getExternalIds()
|
||||
.stream()
|
||||
.anyMatch(e -> account.getPreferredEmail().equals(e.email()))) {
|
||||
addError(
|
||||
|
@@ -424,11 +424,8 @@ public class AccountsUpdate {
|
||||
|
||||
private AccountConfig read(Repository allUsersRepo, Account.Id accountId)
|
||||
throws IOException, ConfigInvalidException {
|
||||
AccountConfig accountConfig = new AccountConfig(accountId);
|
||||
accountConfig.load(allUsersRepo);
|
||||
|
||||
AccountConfig accountConfig = new AccountConfig(accountId, allUsersRepo).load();
|
||||
afterReadRevision.run();
|
||||
|
||||
return accountConfig;
|
||||
}
|
||||
|
||||
|
@@ -1,198 +0,0 @@
|
||||
// Copyright (C) 2015 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.gerrit.server.config.ConfigUtil.loadSection;
|
||||
import static com.google.gerrit.server.config.ConfigUtil.skipField;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE_COLUMN;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.KEY_ID;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.KEY_MATCH;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.KEY_TARGET;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.KEY_TOKEN;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.URL_ALIAS;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.extensions.client.MenuItem;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.UserConfigSections;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Singleton
|
||||
public class GeneralPreferencesLoader {
|
||||
private static final Logger log = LoggerFactory.getLogger(GeneralPreferencesLoader.class);
|
||||
|
||||
private final GitRepositoryManager gitMgr;
|
||||
private final AllUsersName allUsersName;
|
||||
|
||||
@Inject
|
||||
public GeneralPreferencesLoader(GitRepositoryManager gitMgr, AllUsersName allUsersName) {
|
||||
this.gitMgr = gitMgr;
|
||||
this.allUsersName = allUsersName;
|
||||
}
|
||||
|
||||
public GeneralPreferencesInfo load(Account.Id id)
|
||||
throws IOException, ConfigInvalidException, RepositoryNotFoundException {
|
||||
return read(id, null);
|
||||
}
|
||||
|
||||
public GeneralPreferencesInfo merge(Account.Id id, GeneralPreferencesInfo in)
|
||||
throws IOException, ConfigInvalidException, RepositoryNotFoundException {
|
||||
return read(id, in);
|
||||
}
|
||||
|
||||
private GeneralPreferencesInfo read(Account.Id id, GeneralPreferencesInfo in)
|
||||
throws IOException, ConfigInvalidException, RepositoryNotFoundException {
|
||||
try (Repository allUsers = gitMgr.openRepository(allUsersName)) {
|
||||
// Load all users default prefs
|
||||
VersionedAccountPreferences dp = VersionedAccountPreferences.forDefault();
|
||||
dp.load(allUsers);
|
||||
|
||||
// Load user prefs
|
||||
VersionedAccountPreferences p = VersionedAccountPreferences.forUser(id);
|
||||
p.load(allUsers);
|
||||
GeneralPreferencesInfo r =
|
||||
loadSection(
|
||||
p.getConfig(),
|
||||
UserConfigSections.GENERAL,
|
||||
null,
|
||||
new GeneralPreferencesInfo(),
|
||||
readDefaultsFromGit(dp.getConfig(), in),
|
||||
in);
|
||||
loadChangeTableColumns(r, p, dp);
|
||||
return loadMyMenusAndUrlAliases(r, p, dp);
|
||||
}
|
||||
}
|
||||
|
||||
public GeneralPreferencesInfo readDefaultsFromGit(Repository git, GeneralPreferencesInfo in)
|
||||
throws ConfigInvalidException, IOException {
|
||||
VersionedAccountPreferences dp = VersionedAccountPreferences.forDefault();
|
||||
dp.load(git);
|
||||
return readDefaultsFromGit(dp.getConfig(), in);
|
||||
}
|
||||
|
||||
private GeneralPreferencesInfo readDefaultsFromGit(Config config, GeneralPreferencesInfo in)
|
||||
throws ConfigInvalidException {
|
||||
GeneralPreferencesInfo allUserPrefs = new GeneralPreferencesInfo();
|
||||
loadSection(
|
||||
config,
|
||||
UserConfigSections.GENERAL,
|
||||
null,
|
||||
allUserPrefs,
|
||||
GeneralPreferencesInfo.defaults(),
|
||||
in);
|
||||
return updateDefaults(allUserPrefs);
|
||||
}
|
||||
|
||||
private GeneralPreferencesInfo updateDefaults(GeneralPreferencesInfo input) {
|
||||
GeneralPreferencesInfo result = GeneralPreferencesInfo.defaults();
|
||||
try {
|
||||
for (Field field : input.getClass().getDeclaredFields()) {
|
||||
if (skipField(field)) {
|
||||
continue;
|
||||
}
|
||||
Object newVal = field.get(input);
|
||||
if (newVal != null) {
|
||||
field.set(result, newVal);
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
log.error("Cannot get default general preferences from " + allUsersName.get(), e);
|
||||
return GeneralPreferencesInfo.defaults();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public GeneralPreferencesInfo loadMyMenusAndUrlAliases(
|
||||
GeneralPreferencesInfo r, VersionedAccountPreferences v, VersionedAccountPreferences d) {
|
||||
r.my = my(v);
|
||||
if (r.my.isEmpty() && !v.isDefaults()) {
|
||||
r.my = my(d);
|
||||
}
|
||||
if (r.my.isEmpty()) {
|
||||
r.my.add(new MenuItem("Changes", "#/dashboard/self", null));
|
||||
r.my.add(new MenuItem("Draft Comments", "#/q/has:draft", null));
|
||||
r.my.add(new MenuItem("Edits", "#/q/has:edit", null));
|
||||
r.my.add(new MenuItem("Watched Changes", "#/q/is:watched+is:open", null));
|
||||
r.my.add(new MenuItem("Starred Changes", "#/q/is:starred", null));
|
||||
r.my.add(new MenuItem("Groups", "#/groups/self", null));
|
||||
}
|
||||
|
||||
r.urlAliases = urlAliases(v);
|
||||
if (r.urlAliases == null && !v.isDefaults()) {
|
||||
r.urlAliases = urlAliases(d);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static List<MenuItem> my(VersionedAccountPreferences v) {
|
||||
List<MenuItem> my = new ArrayList<>();
|
||||
Config cfg = v.getConfig();
|
||||
for (String subsection : cfg.getSubsections(UserConfigSections.MY)) {
|
||||
String url = my(cfg, subsection, KEY_URL, "#/");
|
||||
String target = my(cfg, subsection, KEY_TARGET, url.startsWith("#") ? null : "_blank");
|
||||
my.add(new MenuItem(subsection, url, target, my(cfg, subsection, KEY_ID, null)));
|
||||
}
|
||||
return my;
|
||||
}
|
||||
|
||||
private static String my(Config cfg, String subsection, String key, String defaultValue) {
|
||||
String val = cfg.getString(UserConfigSections.MY, subsection, key);
|
||||
return !Strings.isNullOrEmpty(val) ? val : defaultValue;
|
||||
}
|
||||
|
||||
public GeneralPreferencesInfo loadChangeTableColumns(
|
||||
GeneralPreferencesInfo r, VersionedAccountPreferences v, VersionedAccountPreferences d) {
|
||||
r.changeTable = changeTable(v);
|
||||
|
||||
if (r.changeTable.isEmpty() && !v.isDefaults()) {
|
||||
r.changeTable = changeTable(d);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static List<String> changeTable(VersionedAccountPreferences v) {
|
||||
return Lists.newArrayList(v.getConfig().getStringList(CHANGE_TABLE, null, CHANGE_TABLE_COLUMN));
|
||||
}
|
||||
|
||||
private static Map<String, String> urlAliases(VersionedAccountPreferences v) {
|
||||
HashMap<String, String> urlAliases = new HashMap<>();
|
||||
Config cfg = v.getConfig();
|
||||
for (String subsection : cfg.getSubsections(URL_ALIAS)) {
|
||||
urlAliases.put(
|
||||
cfg.getString(URL_ALIAS, subsection, KEY_MATCH),
|
||||
cfg.getString(URL_ALIAS, subsection, KEY_TOKEN));
|
||||
}
|
||||
return !urlAliases.isEmpty() ? urlAliases : null;
|
||||
}
|
||||
}
|
@@ -16,12 +16,18 @@ package com.google.gerrit.server.account;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.account.WatchConfig.NotifyType;
|
||||
import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
|
||||
import com.google.gerrit.server.account.externalids.DuplicateExternalIdKeyException;
|
||||
import com.google.gerrit.server.account.externalids.ExternalId;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Class to prepare updates to an account.
|
||||
@@ -92,6 +98,30 @@ public abstract class InternalAccountUpdate {
|
||||
*/
|
||||
public abstract ImmutableSet<ExternalId> getDeletedExternalIds();
|
||||
|
||||
/**
|
||||
* Returns external IDs that should be updated for the account.
|
||||
*
|
||||
* @return external IDs that should be updated for the account
|
||||
*/
|
||||
public abstract ImmutableMap<ProjectWatchKey, Set<NotifyType>> getUpdatedProjectWatches();
|
||||
|
||||
/**
|
||||
* Returns project watches that should be deleted for the account.
|
||||
*
|
||||
* @return project watches that should be deleted for the account
|
||||
*/
|
||||
public abstract ImmutableSet<ProjectWatchKey> getDeletedProjectWatches();
|
||||
|
||||
/**
|
||||
* Returns the new value for the general preferences.
|
||||
*
|
||||
* <p>Only preferences that are non-null in the returned GeneralPreferencesInfo should be updated.
|
||||
*
|
||||
* @return the new value for the general preferences, {@code Optional#empty()} if the general
|
||||
* preferences are not being updated, the wrapped value is never {@code null}
|
||||
*/
|
||||
public abstract Optional<GeneralPreferencesInfo> getGeneralPreferences();
|
||||
|
||||
/**
|
||||
* Class to build an account update.
|
||||
*
|
||||
@@ -239,7 +269,7 @@ public abstract class InternalAccountUpdate {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete external IDs for the account.
|
||||
* Deletes external IDs for the account.
|
||||
*
|
||||
* <p>The account IDs of the external IDs must match the account ID of the account that is
|
||||
* updated.
|
||||
@@ -277,6 +307,83 @@ public abstract class InternalAccountUpdate {
|
||||
return deleteExternalIds(extIdsToDelete).addExternalIds(extIdsToAdd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a builder for the map of updated project watches.
|
||||
*
|
||||
* @return builder for the map of updated project watches.
|
||||
*/
|
||||
abstract ImmutableMap.Builder<ProjectWatchKey, Set<NotifyType>> updatedProjectWatchesBuilder();
|
||||
|
||||
/**
|
||||
* Updates a project watch for the account.
|
||||
*
|
||||
* <p>If no project watch with the key exists the project watch is created.
|
||||
*
|
||||
* @param projectWatchKey key of the project watch that should be updated
|
||||
* @param notifyTypes the notify types that should be set for the project watch
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder updateProjectWatch(
|
||||
ProjectWatchKey projectWatchKey, Set<NotifyType> notifyTypes) {
|
||||
return updateProjectWatches(ImmutableMap.of(projectWatchKey, notifyTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates project watches for the account.
|
||||
*
|
||||
* <p>If any of the project watches already exists, it is overwritten. New project watches are
|
||||
* inserted.
|
||||
*
|
||||
* @param projectWatches project watches that should be updated
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder updateProjectWatches(Map<ProjectWatchKey, Set<NotifyType>> projectWatches) {
|
||||
updatedProjectWatchesBuilder().putAll(projectWatches);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a builder for the set of deleted project watches.
|
||||
*
|
||||
* @return builder for the set of deleted project watches.
|
||||
*/
|
||||
abstract ImmutableSet.Builder<ProjectWatchKey> deletedProjectWatchesBuilder();
|
||||
|
||||
/**
|
||||
* Deletes a project watch for the account.
|
||||
*
|
||||
* <p>If no project watch with the ID exists this is a no-op.
|
||||
*
|
||||
* @param projectWatch project watch that should be deleted
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder deleteProjectWatch(ProjectWatchKey projectWatch) {
|
||||
return deleteProjectWatches(ImmutableSet.of(projectWatch));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes project watches for the account.
|
||||
*
|
||||
* <p>For non-existing project watches this is a no-op.
|
||||
*
|
||||
* @param projectWatches project watches that should be deleted
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder deleteProjectWatches(Collection<ProjectWatchKey> projectWatches) {
|
||||
deletedProjectWatchesBuilder().addAll(projectWatches);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the general preferences for the account.
|
||||
*
|
||||
* <p>Updates any preference that is non-null in the provided GeneralPreferencesInfo.
|
||||
*
|
||||
* @param generalPreferences the general preferences that should be set
|
||||
* @return the builder
|
||||
*/
|
||||
public abstract Builder setGeneralPreferences(GeneralPreferencesInfo generalPreferences);
|
||||
|
||||
/**
|
||||
* Builds the account update.
|
||||
*
|
||||
@@ -385,6 +492,34 @@ public abstract class InternalAccountUpdate {
|
||||
delegate.deleteExternalIds(extIds);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
ImmutableMap.Builder<ProjectWatchKey, Set<NotifyType>> updatedProjectWatchesBuilder() {
|
||||
return delegate.updatedProjectWatchesBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder updateProjectWatches(Map<ProjectWatchKey, Set<NotifyType>> projectWatches) {
|
||||
delegate.updateProjectWatches(projectWatches);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
ImmutableSet.Builder<ProjectWatchKey> deletedProjectWatchesBuilder() {
|
||||
return delegate.deletedProjectWatchesBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder deleteProjectWatches(Collection<ProjectWatchKey> projectWatches) {
|
||||
delegate.deleteProjectWatches(projectWatches);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setGeneralPreferences(GeneralPreferencesInfo generalPreferences) {
|
||||
delegate.setGeneralPreferences(generalPreferences);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
368
java/com/google/gerrit/server/account/PreferencesConfig.java
Normal file
368
java/com/google/gerrit/server/account/PreferencesConfig.java
Normal file
@@ -0,0 +1,368 @@
|
||||
// Copyright (C) 2018 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.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.gerrit.server.config.ConfigUtil.loadSection;
|
||||
import static com.google.gerrit.server.config.ConfigUtil.skipField;
|
||||
import static com.google.gerrit.server.config.ConfigUtil.storeSection;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE_COLUMN;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.KEY_ID;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.KEY_MATCH;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.KEY_TARGET;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.KEY_TOKEN;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.URL_ALIAS;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
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.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.UserConfigSections;
|
||||
import com.google.gerrit.server.git.ValidationError;
|
||||
import com.google.gerrit.server.git.VersionedMetaData;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class PreferencesConfig {
|
||||
private static final Logger log = LoggerFactory.getLogger(PreferencesConfig.class);
|
||||
|
||||
public static final String PREFERENCES_CONFIG = "preferences.config";
|
||||
|
||||
private final Account.Id accountId;
|
||||
private final Config cfg;
|
||||
private final Config defaultCfg;
|
||||
private final ValidationError.Sink validationErrorSink;
|
||||
|
||||
private GeneralPreferencesInfo generalPreferences;
|
||||
|
||||
public PreferencesConfig(
|
||||
Account.Id accountId,
|
||||
Config cfg,
|
||||
Config defaultCfg,
|
||||
ValidationError.Sink validationErrorSink) {
|
||||
this.accountId = checkNotNull(accountId, "accountId");
|
||||
this.cfg = checkNotNull(cfg, "cfg");
|
||||
this.defaultCfg = checkNotNull(defaultCfg, "defaultCfg");
|
||||
this.validationErrorSink = checkNotNull(validationErrorSink, "validationErrorSink");
|
||||
}
|
||||
|
||||
public GeneralPreferencesInfo getGeneralPreferences() {
|
||||
if (generalPreferences == null) {
|
||||
parse();
|
||||
}
|
||||
return generalPreferences;
|
||||
}
|
||||
|
||||
public void parse() {
|
||||
generalPreferences = parse(null);
|
||||
}
|
||||
|
||||
public Config saveGeneralPreferences(GeneralPreferencesInfo input) throws ConfigInvalidException {
|
||||
// merge configs
|
||||
input = parse(input);
|
||||
|
||||
storeSection(
|
||||
cfg, UserConfigSections.GENERAL, null, input, parseDefaultPreferences(defaultCfg, null));
|
||||
setChangeTable(cfg, input.changeTable);
|
||||
setMy(cfg, input.my);
|
||||
setUrlAliases(cfg, input.urlAliases);
|
||||
|
||||
// evict the cached general preferences
|
||||
this.generalPreferences = null;
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
private GeneralPreferencesInfo parse(@Nullable GeneralPreferencesInfo input) {
|
||||
try {
|
||||
return parse(cfg, defaultCfg, input);
|
||||
} catch (ConfigInvalidException e) {
|
||||
validationErrorSink.error(
|
||||
new ValidationError(
|
||||
PREFERENCES_CONFIG,
|
||||
String.format(
|
||||
"Invalid general preferences for account %d: %s",
|
||||
accountId.get(), e.getMessage())));
|
||||
return new GeneralPreferencesInfo();
|
||||
}
|
||||
}
|
||||
|
||||
private static GeneralPreferencesInfo parse(
|
||||
Config cfg, @Nullable Config defaultCfg, @Nullable GeneralPreferencesInfo input)
|
||||
throws ConfigInvalidException {
|
||||
GeneralPreferencesInfo r =
|
||||
loadSection(
|
||||
cfg,
|
||||
UserConfigSections.GENERAL,
|
||||
null,
|
||||
new GeneralPreferencesInfo(),
|
||||
defaultCfg != null
|
||||
? parseDefaultPreferences(defaultCfg, input)
|
||||
: GeneralPreferencesInfo.defaults(),
|
||||
input);
|
||||
if (input != null) {
|
||||
r.changeTable = input.changeTable;
|
||||
r.my = input.my;
|
||||
r.urlAliases = input.urlAliases;
|
||||
} else {
|
||||
r.changeTable = parseChangeTableColumns(cfg, defaultCfg);
|
||||
r.my = parseMyMenus(cfg, defaultCfg);
|
||||
r.urlAliases = parseUrlAliases(cfg, defaultCfg);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static GeneralPreferencesInfo parseDefaultPreferences(
|
||||
Config defaultCfg, GeneralPreferencesInfo input) throws ConfigInvalidException {
|
||||
GeneralPreferencesInfo allUserPrefs = new GeneralPreferencesInfo();
|
||||
loadSection(
|
||||
defaultCfg,
|
||||
UserConfigSections.GENERAL,
|
||||
null,
|
||||
allUserPrefs,
|
||||
GeneralPreferencesInfo.defaults(),
|
||||
input);
|
||||
return updateDefaults(allUserPrefs);
|
||||
}
|
||||
|
||||
private static GeneralPreferencesInfo updateDefaults(GeneralPreferencesInfo input) {
|
||||
GeneralPreferencesInfo result = GeneralPreferencesInfo.defaults();
|
||||
try {
|
||||
for (Field field : input.getClass().getDeclaredFields()) {
|
||||
if (skipField(field)) {
|
||||
continue;
|
||||
}
|
||||
Object newVal = field.get(input);
|
||||
if (newVal != null) {
|
||||
field.set(result, newVal);
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
log.error("Failed to apply default general preferences", e);
|
||||
return GeneralPreferencesInfo.defaults();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<String> parseChangeTableColumns(Config cfg, @Nullable Config defaultCfg) {
|
||||
List<String> changeTable = changeTable(cfg);
|
||||
if (changeTable == null && defaultCfg != null) {
|
||||
changeTable = changeTable(defaultCfg);
|
||||
}
|
||||
return changeTable;
|
||||
}
|
||||
|
||||
private static List<MenuItem> parseMyMenus(Config cfg, @Nullable Config defaultCfg) {
|
||||
List<MenuItem> my = my(cfg);
|
||||
if (my.isEmpty() && defaultCfg != null) {
|
||||
my = my(defaultCfg);
|
||||
}
|
||||
if (my.isEmpty()) {
|
||||
my.add(new MenuItem("Changes", "#/dashboard/self", null));
|
||||
my.add(new MenuItem("Draft Comments", "#/q/has:draft", null));
|
||||
my.add(new MenuItem("Edits", "#/q/has:edit", null));
|
||||
my.add(new MenuItem("Watched Changes", "#/q/is:watched+is:open", null));
|
||||
my.add(new MenuItem("Starred Changes", "#/q/is:starred", null));
|
||||
my.add(new MenuItem("Groups", "#/groups/self", null));
|
||||
}
|
||||
return my;
|
||||
}
|
||||
|
||||
private static Map<String, String> parseUrlAliases(Config cfg, @Nullable Config defaultCfg) {
|
||||
Map<String, String> urlAliases = urlAliases(cfg);
|
||||
if (urlAliases == null && defaultCfg != null) {
|
||||
urlAliases = urlAliases(defaultCfg);
|
||||
}
|
||||
return urlAliases;
|
||||
}
|
||||
|
||||
public static GeneralPreferencesInfo readDefaultPreferences(Repository allUsersRepo)
|
||||
throws IOException, ConfigInvalidException {
|
||||
return parse(readDefaultConfig(allUsersRepo), null, null);
|
||||
}
|
||||
|
||||
static Config readDefaultConfig(Repository allUsersRepo)
|
||||
throws IOException, ConfigInvalidException {
|
||||
VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
|
||||
defaultPrefs.load(allUsersRepo);
|
||||
return defaultPrefs.getConfig();
|
||||
}
|
||||
|
||||
public static GeneralPreferencesInfo updateDefaultPreferences(
|
||||
MetaDataUpdate md, GeneralPreferencesInfo input) throws IOException, ConfigInvalidException {
|
||||
VersionedDefaultPreferences defaultPrefs = new VersionedDefaultPreferences();
|
||||
defaultPrefs.load(md);
|
||||
storeSection(
|
||||
defaultPrefs.getConfig(),
|
||||
UserConfigSections.GENERAL,
|
||||
null,
|
||||
input,
|
||||
GeneralPreferencesInfo.defaults());
|
||||
setMy(defaultPrefs.getConfig(), input.my);
|
||||
setChangeTable(defaultPrefs.getConfig(), input.changeTable);
|
||||
setUrlAliases(defaultPrefs.getConfig(), input.urlAliases);
|
||||
defaultPrefs.commit(md);
|
||||
|
||||
return parse(defaultPrefs.getConfig(), null, null);
|
||||
}
|
||||
|
||||
private static List<String> changeTable(Config cfg) {
|
||||
return Lists.newArrayList(cfg.getStringList(CHANGE_TABLE, null, CHANGE_TABLE_COLUMN));
|
||||
}
|
||||
|
||||
private static void setChangeTable(Config cfg, List<String> changeTable) {
|
||||
if (changeTable != null) {
|
||||
unsetSection(cfg, UserConfigSections.CHANGE_TABLE);
|
||||
cfg.setStringList(UserConfigSections.CHANGE_TABLE, null, CHANGE_TABLE_COLUMN, changeTable);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<MenuItem> my(Config cfg) {
|
||||
List<MenuItem> my = new ArrayList<>();
|
||||
for (String subsection : cfg.getSubsections(UserConfigSections.MY)) {
|
||||
String url = my(cfg, subsection, KEY_URL, "#/");
|
||||
String target = my(cfg, subsection, KEY_TARGET, url.startsWith("#") ? null : "_blank");
|
||||
my.add(new MenuItem(subsection, url, target, my(cfg, subsection, KEY_ID, null)));
|
||||
}
|
||||
return my;
|
||||
}
|
||||
|
||||
private static String my(Config cfg, String subsection, String key, String defaultValue) {
|
||||
String val = cfg.getString(UserConfigSections.MY, subsection, key);
|
||||
return !Strings.isNullOrEmpty(val) ? val : defaultValue;
|
||||
}
|
||||
|
||||
private static void setMy(Config cfg, List<MenuItem> my) {
|
||||
if (my != null) {
|
||||
unsetSection(cfg, UserConfigSections.MY);
|
||||
for (MenuItem item : my) {
|
||||
checkState(!isNullOrEmpty(item.name), "MenuItem.name must not be null or empty");
|
||||
checkState(!isNullOrEmpty(item.url), "MenuItem.url must not be null or empty");
|
||||
|
||||
setMy(cfg, item.name, KEY_URL, item.url);
|
||||
setMy(cfg, item.name, KEY_TARGET, item.target);
|
||||
setMy(cfg, item.name, KEY_ID, item.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void validateMy(List<MenuItem> my) throws BadRequestException {
|
||||
if (my == null) {
|
||||
return;
|
||||
}
|
||||
for (MenuItem item : my) {
|
||||
checkRequiredMenuItemField(item.name, "name");
|
||||
checkRequiredMenuItemField(item.url, "URL");
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkRequiredMenuItemField(String value, String name)
|
||||
throws BadRequestException {
|
||||
if (isNullOrEmpty(value)) {
|
||||
throw new BadRequestException(name + " for menu item is required");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isNullOrEmpty(String value) {
|
||||
return value == null || value.trim().isEmpty();
|
||||
}
|
||||
|
||||
private static void setMy(Config cfg, String section, String key, @Nullable String val) {
|
||||
if (val == null || val.trim().isEmpty()) {
|
||||
cfg.unset(UserConfigSections.MY, section.trim(), key);
|
||||
} else {
|
||||
cfg.setString(UserConfigSections.MY, section.trim(), key, val.trim());
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, String> urlAliases(Config cfg) {
|
||||
HashMap<String, String> urlAliases = new HashMap<>();
|
||||
for (String subsection : cfg.getSubsections(URL_ALIAS)) {
|
||||
urlAliases.put(
|
||||
cfg.getString(URL_ALIAS, subsection, KEY_MATCH),
|
||||
cfg.getString(URL_ALIAS, subsection, KEY_TOKEN));
|
||||
}
|
||||
return !urlAliases.isEmpty() ? urlAliases : null;
|
||||
}
|
||||
|
||||
private static void setUrlAliases(Config cfg, Map<String, String> urlAliases) {
|
||||
if (urlAliases != null) {
|
||||
for (String subsection : cfg.getSubsections(URL_ALIAS)) {
|
||||
cfg.unsetSection(URL_ALIAS, subsection);
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
for (Entry<String, String> e : urlAliases.entrySet()) {
|
||||
cfg.setString(URL_ALIAS, URL_ALIAS + i, KEY_MATCH, e.getKey());
|
||||
cfg.setString(URL_ALIAS, URL_ALIAS + i, KEY_TOKEN, e.getValue());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void unsetSection(Config cfg, String section) {
|
||||
cfg.unsetSection(section, null);
|
||||
for (String subsection : cfg.getSubsections(section)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -15,7 +15,7 @@
|
||||
package com.google.gerrit.server.account;
|
||||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@@ -23,7 +23,6 @@ import com.google.common.base.Enums;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
@@ -31,17 +30,7 @@ import com.google.common.collect.Sets;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.ValidationError;
|
||||
import com.google.gerrit.server.git.VersionedMetaData;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
@@ -49,10 +38,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
/**
|
||||
* ‘watch.config’ file in the user branch in the All-Users repository that contains the watch
|
||||
@@ -85,91 +71,7 @@ import org.eclipse.jgit.lib.Repository;
|
||||
*
|
||||
* <p>Unknown notify types are ignored and removed on save.
|
||||
*/
|
||||
public class WatchConfig extends VersionedMetaData implements ValidationError.Sink {
|
||||
@Singleton
|
||||
public static class Accessor {
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final AllUsersName allUsersName;
|
||||
private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
|
||||
private final IdentifiedUser.GenericFactory userFactory;
|
||||
|
||||
@Inject
|
||||
Accessor(
|
||||
GitRepositoryManager repoManager,
|
||||
AllUsersName allUsersName,
|
||||
Provider<MetaDataUpdate.User> metaDataUpdateFactory,
|
||||
IdentifiedUser.GenericFactory userFactory) {
|
||||
this.repoManager = repoManager;
|
||||
this.allUsersName = allUsersName;
|
||||
this.metaDataUpdateFactory = metaDataUpdateFactory;
|
||||
this.userFactory = userFactory;
|
||||
}
|
||||
|
||||
public Map<ProjectWatchKey, Set<NotifyType>> getProjectWatches(Account.Id accountId)
|
||||
throws IOException, ConfigInvalidException {
|
||||
try (Repository git = repoManager.openRepository(allUsersName)) {
|
||||
WatchConfig watchConfig = new WatchConfig(accountId);
|
||||
watchConfig.load(git);
|
||||
return watchConfig.getProjectWatches();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void upsertProjectWatches(
|
||||
Account.Id accountId, Map<ProjectWatchKey, Set<NotifyType>> newProjectWatches)
|
||||
throws IOException, ConfigInvalidException {
|
||||
WatchConfig watchConfig = read(accountId);
|
||||
Map<ProjectWatchKey, Set<NotifyType>> projectWatches = watchConfig.getProjectWatches();
|
||||
projectWatches.putAll(newProjectWatches);
|
||||
commit(watchConfig);
|
||||
}
|
||||
|
||||
public synchronized void deleteProjectWatches(
|
||||
Account.Id accountId, Collection<ProjectWatchKey> projectWatchKeys)
|
||||
throws IOException, ConfigInvalidException {
|
||||
WatchConfig watchConfig = read(accountId);
|
||||
Map<ProjectWatchKey, Set<NotifyType>> projectWatches = watchConfig.getProjectWatches();
|
||||
boolean commit = false;
|
||||
for (ProjectWatchKey key : projectWatchKeys) {
|
||||
if (projectWatches.remove(key) != null) {
|
||||
commit = true;
|
||||
}
|
||||
}
|
||||
if (commit) {
|
||||
commit(watchConfig);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void deleteAllProjectWatches(Account.Id accountId)
|
||||
throws IOException, ConfigInvalidException {
|
||||
WatchConfig watchConfig = read(accountId);
|
||||
boolean commit = false;
|
||||
if (!watchConfig.getProjectWatches().isEmpty()) {
|
||||
watchConfig.getProjectWatches().clear();
|
||||
commit = true;
|
||||
}
|
||||
if (commit) {
|
||||
commit(watchConfig);
|
||||
}
|
||||
}
|
||||
|
||||
private WatchConfig read(Account.Id accountId) throws IOException, ConfigInvalidException {
|
||||
try (Repository git = repoManager.openRepository(allUsersName)) {
|
||||
WatchConfig watchConfig = new WatchConfig(accountId);
|
||||
watchConfig.load(git);
|
||||
return watchConfig;
|
||||
}
|
||||
}
|
||||
|
||||
private void commit(WatchConfig watchConfig) throws IOException {
|
||||
try (MetaDataUpdate md =
|
||||
metaDataUpdateFactory
|
||||
.get()
|
||||
.create(allUsersName, userFactory.create(watchConfig.accountId))) {
|
||||
watchConfig.commit(md);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class WatchConfig {
|
||||
@AutoValue
|
||||
public abstract static class ProjectWatchKey {
|
||||
public static ProjectWatchKey create(Project.NameKey project, @Nullable String filter) {
|
||||
@@ -199,25 +101,26 @@ public class WatchConfig extends VersionedMetaData implements ValidationError.Si
|
||||
public static final String KEY_NOTIFY = "notify";
|
||||
|
||||
private final Account.Id accountId;
|
||||
private final String ref;
|
||||
private final Config cfg;
|
||||
private final ValidationError.Sink validationErrorSink;
|
||||
|
||||
private Map<ProjectWatchKey, Set<NotifyType>> projectWatches;
|
||||
private List<ValidationError> validationErrors;
|
||||
|
||||
public WatchConfig(Account.Id accountId) {
|
||||
this.accountId = accountId;
|
||||
this.ref = RefNames.refsUsers(accountId);
|
||||
public WatchConfig(Account.Id accountId, Config cfg, ValidationError.Sink validationErrorSink) {
|
||||
this.accountId = checkNotNull(accountId, "accountId");
|
||||
this.cfg = checkNotNull(cfg, "cfg");
|
||||
this.validationErrorSink = checkNotNull(validationErrorSink, "validationErrorSink");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRefName() {
|
||||
return ref;
|
||||
public Map<ProjectWatchKey, Set<NotifyType>> getProjectWatches() {
|
||||
if (projectWatches == null) {
|
||||
parse();
|
||||
}
|
||||
return projectWatches;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLoad() throws IOException, ConfigInvalidException {
|
||||
Config cfg = readConfig(WATCH_CONFIG);
|
||||
projectWatches = parse(accountId, cfg, this);
|
||||
public void parse() {
|
||||
projectWatches = parse(accountId, cfg, validationErrorSink);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -248,24 +151,8 @@ public class WatchConfig extends VersionedMetaData implements ValidationError.Si
|
||||
return projectWatches;
|
||||
}
|
||||
|
||||
Map<ProjectWatchKey, Set<NotifyType>> getProjectWatches() {
|
||||
checkLoaded();
|
||||
return projectWatches;
|
||||
}
|
||||
|
||||
public void setProjectWatches(Map<ProjectWatchKey, Set<NotifyType>> projectWatches) {
|
||||
public Config save(Map<ProjectWatchKey, Set<NotifyType>> projectWatches) {
|
||||
this.projectWatches = projectWatches;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
|
||||
checkLoaded();
|
||||
|
||||
if (Strings.isNullOrEmpty(commit.getMessage())) {
|
||||
commit.setMessage("Updated watch configuration\n");
|
||||
}
|
||||
|
||||
Config cfg = readConfig(WATCH_CONFIG);
|
||||
|
||||
for (String projectName : cfg.getSubsections(PROJECT)) {
|
||||
cfg.unsetSection(PROJECT, projectName);
|
||||
@@ -282,32 +169,7 @@ public class WatchConfig extends VersionedMetaData implements ValidationError.Si
|
||||
cfg.setStringList(PROJECT, e.getKey(), KEY_NOTIFY, new ArrayList<>(e.getValue()));
|
||||
}
|
||||
|
||||
saveConfig(WATCH_CONFIG, cfg);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void checkLoaded() {
|
||||
checkState(projectWatches != null, "project watches not loaded yet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(ValidationError error) {
|
||||
if (validationErrors == null) {
|
||||
validationErrors = new ArrayList<>(4);
|
||||
}
|
||||
validationErrors.add(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation errors, if any were discovered during load.
|
||||
*
|
||||
* @return list of errors; empty list if there are no errors.
|
||||
*/
|
||||
public List<ValidationError> getValidationErrors() {
|
||||
if (validationErrors != null) {
|
||||
return ImmutableList.copyOf(validationErrors);
|
||||
}
|
||||
return ImmutableList.of();
|
||||
return cfg;
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
@@ -356,7 +218,7 @@ public class WatchConfig extends VersionedMetaData implements ValidationError.Si
|
||||
return create(filter, notifyTypes);
|
||||
}
|
||||
|
||||
public static NotifyValue create(@Nullable String filter, Set<NotifyType> notifyTypes) {
|
||||
public static NotifyValue create(@Nullable String filter, Collection<NotifyType> notifyTypes) {
|
||||
return new AutoValue_WatchConfig_NotifyValue(
|
||||
Strings.emptyToNull(filter), Sets.immutableEnumSet(notifyTypes));
|
||||
}
|
||||
|
@@ -63,6 +63,11 @@ public class DisabledExternalIdCache implements ExternalIdCache {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<ExternalId> byAccount(Account.Id accountId, ObjectId rev) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSetMultimap<Account.Id, ExternalId> allByAccount() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@@ -55,6 +55,8 @@ interface ExternalIdCache {
|
||||
|
||||
ImmutableSet<ExternalId> byAccount(Account.Id accountId) throws IOException;
|
||||
|
||||
ImmutableSet<ExternalId> byAccount(Account.Id accountId, ObjectId rev) throws IOException;
|
||||
|
||||
ImmutableSetMultimap<Account.Id, ExternalId> allByAccount() throws IOException;
|
||||
|
||||
ImmutableSetMultimap<String, ExternalId> byEmails(String... emails) throws IOException;
|
||||
|
@@ -169,6 +169,11 @@ class ExternalIdCacheImpl implements ExternalIdCache {
|
||||
return get().byAccount().get(accountId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<ExternalId> byAccount(Account.Id accountId, ObjectId rev) throws IOException {
|
||||
return get(rev).byAccount().get(accountId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSetMultimap<Account.Id, ExternalId> allByAccount() throws IOException {
|
||||
return get().byAccount();
|
||||
@@ -190,8 +195,12 @@ class ExternalIdCacheImpl implements ExternalIdCache {
|
||||
}
|
||||
|
||||
private AllExternalIds get() throws IOException {
|
||||
return get(externalIdReader.readRevision());
|
||||
}
|
||||
|
||||
private AllExternalIds get(ObjectId rev) throws IOException {
|
||||
try {
|
||||
return extIdsByAccount.get(externalIdReader.readRevision());
|
||||
return extIdsByAccount.get(rev);
|
||||
} catch (ExecutionException e) {
|
||||
throw new IOException("Cannot load external ids", e);
|
||||
}
|
||||
|
@@ -171,7 +171,7 @@ public class ExternalIdNotes extends VersionedMetaData {
|
||||
private Runnable afterReadRevision;
|
||||
private boolean readOnly = false;
|
||||
|
||||
ExternalIdNotes(
|
||||
private ExternalIdNotes(
|
||||
ExternalIdCache externalIdCache,
|
||||
@Nullable AccountCache accountCache,
|
||||
Repository allUsersRepo) {
|
||||
@@ -205,7 +205,7 @@ public class ExternalIdNotes extends VersionedMetaData {
|
||||
*
|
||||
* @return {@link ExternalIdNotes} instance for chaining
|
||||
*/
|
||||
ExternalIdNotes load() throws IOException, ConfigInvalidException {
|
||||
private ExternalIdNotes load() throws IOException, ConfigInvalidException {
|
||||
load(repo);
|
||||
return this;
|
||||
}
|
||||
|
@@ -75,6 +75,20 @@ public class ExternalIds {
|
||||
return byAccount(accountId).stream().filter(e -> e.key().isScheme(scheme)).collect(toSet());
|
||||
}
|
||||
|
||||
/** Returns the external IDs of the specified account. */
|
||||
public Set<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)
|
||||
throws IOException {
|
||||
return byAccount(accountId, rev)
|
||||
.stream()
|
||||
.filter(e -> e.key().isScheme(scheme))
|
||||
.collect(toSet());
|
||||
}
|
||||
|
||||
/** Returns all external IDs by account. */
|
||||
public ImmutableSetMultimap<Account.Id, ExternalId> allByAccount() throws IOException {
|
||||
return externalIdCache.allByAccount();
|
||||
|
@@ -1334,11 +1334,10 @@ class ReceiveCommits {
|
||||
this.publish = cmd.getRefName().startsWith(MagicBranch.NEW_PUBLISH_CHANGE);
|
||||
this.labelTypes = labelTypes;
|
||||
this.notesMigration = notesMigration;
|
||||
GeneralPreferencesInfo prefs = user.getAccount().getGeneralPreferencesInfo();
|
||||
GeneralPreferencesInfo prefs = user.state().getGeneralPreferences();
|
||||
this.defaultPublishComments =
|
||||
prefs != null
|
||||
? firstNonNull(
|
||||
user.getAccount().getGeneralPreferencesInfo().publishCommentsOnPush, false)
|
||||
? firstNonNull(user.state().getGeneralPreferences().publishCommentsOnPush, false)
|
||||
: false;
|
||||
}
|
||||
|
||||
|
@@ -14,11 +14,14 @@
|
||||
|
||||
package com.google.gerrit.server.git.validators;
|
||||
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountConfig;
|
||||
import com.google.gerrit.server.git.ValidationError;
|
||||
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
@@ -28,6 +31,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
|
||||
public class AccountValidator {
|
||||
@@ -42,20 +46,21 @@ public class AccountValidator {
|
||||
}
|
||||
|
||||
public List<String> validate(
|
||||
Account.Id accountId, RevWalk rw, @Nullable ObjectId oldId, ObjectId newId)
|
||||
Account.Id accountId, Repository repo, RevWalk rw, @Nullable ObjectId oldId, ObjectId newId)
|
||||
throws IOException {
|
||||
Optional<Account> oldAccount = Optional.empty();
|
||||
if (oldId != null && !ObjectId.zeroId().equals(oldId)) {
|
||||
try {
|
||||
oldAccount = loadAccount(accountId, rw, oldId);
|
||||
oldAccount = loadAccount(accountId, repo, rw, oldId, null);
|
||||
} catch (ConfigInvalidException e) {
|
||||
// ignore, maybe the new commit is repairing it now
|
||||
}
|
||||
}
|
||||
|
||||
List<String> messages = new ArrayList<>();
|
||||
Optional<Account> newAccount;
|
||||
try {
|
||||
newAccount = loadAccount(accountId, rw, newId);
|
||||
newAccount = loadAccount(accountId, repo, rw, newId, messages);
|
||||
} catch (ConfigInvalidException e) {
|
||||
return ImmutableList.of(
|
||||
String.format(
|
||||
@@ -67,7 +72,6 @@ public class AccountValidator {
|
||||
return ImmutableList.of(String.format("account '%s' does not exist", accountId.get()));
|
||||
}
|
||||
|
||||
List<String> messages = new ArrayList<>();
|
||||
if (accountId.equals(self.get().getAccountId()) && !newAccount.get().isActive()) {
|
||||
messages.add("cannot deactivate own account");
|
||||
}
|
||||
@@ -87,11 +91,24 @@ public class AccountValidator {
|
||||
return ImmutableList.copyOf(messages);
|
||||
}
|
||||
|
||||
private Optional<Account> loadAccount(Account.Id accountId, RevWalk rw, ObjectId commit)
|
||||
private Optional<Account> loadAccount(
|
||||
Account.Id accountId,
|
||||
Repository repo,
|
||||
RevWalk rw,
|
||||
ObjectId commit,
|
||||
@Nullable List<String> messages)
|
||||
throws IOException, ConfigInvalidException {
|
||||
rw.reset();
|
||||
AccountConfig accountConfig = new AccountConfig(accountId);
|
||||
accountConfig.load(rw, commit);
|
||||
AccountConfig accountConfig = new AccountConfig(accountId, repo);
|
||||
accountConfig.setEagerParsing(true).load(rw, commit);
|
||||
if (messages != null) {
|
||||
messages.addAll(
|
||||
accountConfig
|
||||
.getValidationErrors()
|
||||
.stream()
|
||||
.map(ValidationError::getMessage)
|
||||
.collect(toSet()));
|
||||
}
|
||||
return accountConfig.getLoadedAccount();
|
||||
}
|
||||
}
|
||||
|
@@ -34,7 +34,6 @@ import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.reviewdb.client.RevId;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.WatchConfig;
|
||||
import com.google.gerrit.server.account.externalids.ExternalIdsConsistencyChecker;
|
||||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
@@ -42,6 +41,7 @@ import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.events.CommitReceivedEvent;
|
||||
import com.google.gerrit.server.git.BanCommit;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gerrit.server.git.ValidationError;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
@@ -85,6 +85,7 @@ public class CommitValidators {
|
||||
private final PersonIdent gerritIdent;
|
||||
private final String canonicalWebUrl;
|
||||
private final DynamicSet<CommitValidationListener> pluginValidators;
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final AllUsersName allUsers;
|
||||
private final AllProjectsName allProjects;
|
||||
private final ExternalIdsConsistencyChecker externalIdsConsistencyChecker;
|
||||
@@ -98,6 +99,7 @@ public class CommitValidators {
|
||||
@CanonicalWebUrl @Nullable String canonicalWebUrl,
|
||||
@GerritServerConfig Config cfg,
|
||||
DynamicSet<CommitValidationListener> pluginValidators,
|
||||
GitRepositoryManager repoManager,
|
||||
AllUsersName allUsers,
|
||||
AllProjectsName allProjects,
|
||||
ExternalIdsConsistencyChecker externalIdsConsistencyChecker,
|
||||
@@ -106,6 +108,7 @@ public class CommitValidators {
|
||||
this.gerritIdent = gerritIdent;
|
||||
this.canonicalWebUrl = canonicalWebUrl;
|
||||
this.pluginValidators = pluginValidators;
|
||||
this.repoManager = repoManager;
|
||||
this.allUsers = allUsers;
|
||||
this.allProjects = allProjects;
|
||||
this.externalIdsConsistencyChecker = externalIdsConsistencyChecker;
|
||||
@@ -138,7 +141,7 @@ public class CommitValidators {
|
||||
new BannedCommitsValidator(rejectCommits),
|
||||
new PluginCommitValidationListener(pluginValidators),
|
||||
new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker),
|
||||
new AccountCommitValidator(allUsers, accountValidator),
|
||||
new AccountCommitValidator(repoManager, allUsers, accountValidator),
|
||||
new GroupCommitValidator(allUsers)));
|
||||
}
|
||||
|
||||
@@ -164,7 +167,7 @@ public class CommitValidators {
|
||||
new ConfigValidator(branch, user, rw, allUsers, allProjects),
|
||||
new PluginCommitValidationListener(pluginValidators),
|
||||
new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker),
|
||||
new AccountCommitValidator(allUsers, accountValidator),
|
||||
new AccountCommitValidator(repoManager, allUsers, accountValidator),
|
||||
new GroupCommitValidator(allUsers)));
|
||||
}
|
||||
|
||||
@@ -418,34 +421,6 @@ public class CommitValidators {
|
||||
}
|
||||
}
|
||||
|
||||
if (allUsers.equals(branch.getParentKey()) && RefNames.isRefsUsers(branch.get())) {
|
||||
List<CommitValidationMessage> messages = new ArrayList<>();
|
||||
Account.Id accountId = Account.Id.fromRef(branch.get());
|
||||
if (accountId != null) {
|
||||
try {
|
||||
WatchConfig wc = new WatchConfig(accountId);
|
||||
wc.load(rw, receiveEvent.command.getNewId());
|
||||
if (!wc.getValidationErrors().isEmpty()) {
|
||||
addError("Invalid project configuration:", messages);
|
||||
for (ValidationError err : wc.getValidationErrors()) {
|
||||
addError(" " + err.getMessage(), messages);
|
||||
}
|
||||
throw new ConfigInvalidException("invalid watch configuration");
|
||||
}
|
||||
} catch (IOException | ConfigInvalidException e) {
|
||||
log.error(
|
||||
"User "
|
||||
+ user.getUserName()
|
||||
+ " tried to push an invalid watch configuration "
|
||||
+ receiveEvent.command.getNewId().name()
|
||||
+ " for account "
|
||||
+ accountId.get(),
|
||||
e);
|
||||
throw new CommitValidationException("invalid watch configuration", messages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -729,10 +704,15 @@ public class CommitValidators {
|
||||
}
|
||||
|
||||
public static class AccountCommitValidator implements CommitValidationListener {
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final AllUsersName allUsers;
|
||||
private final AccountValidator accountValidator;
|
||||
|
||||
public AccountCommitValidator(AllUsersName allUsers, AccountValidator accountValidator) {
|
||||
public AccountCommitValidator(
|
||||
GitRepositoryManager repoManager,
|
||||
AllUsersName allUsers,
|
||||
AccountValidator accountValidator) {
|
||||
this.repoManager = repoManager;
|
||||
this.allUsers = allUsers;
|
||||
this.accountValidator = accountValidator;
|
||||
}
|
||||
@@ -755,10 +735,11 @@ public class CommitValidators {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
try {
|
||||
try (Repository repo = repoManager.openRepository(allUsers)) {
|
||||
List<String> errorMessages =
|
||||
accountValidator.validate(
|
||||
accountId,
|
||||
repo,
|
||||
receiveEvent.revWalk,
|
||||
receiveEvent.command.getOldId(),
|
||||
receiveEvent.commit);
|
||||
|
@@ -285,7 +285,7 @@ public class MergeValidators {
|
||||
}
|
||||
|
||||
try (RevWalk rw = new RevWalk(repo)) {
|
||||
List<String> errorMessages = accountValidator.validate(accountId, rw, null, commit);
|
||||
List<String> errorMessages = accountValidator.validate(accountId, repo, rw, null, commit);
|
||||
if (!errorMessages.isEmpty()) {
|
||||
throw new MergeValidationException(
|
||||
"invalid account configuration: " + Joiner.on("; ").join(errorMessages));
|
||||
|
@@ -20,6 +20,7 @@ import static com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.Consi
|
||||
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.account.Accounts;
|
||||
import com.google.gerrit.server.account.GroupBackend;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
@@ -113,7 +114,7 @@ public class GroupsConsistencyChecker {
|
||||
}
|
||||
|
||||
for (Account.Id id : g.getMembers().asList()) {
|
||||
Account account;
|
||||
AccountState account;
|
||||
try {
|
||||
account = accounts.get(id);
|
||||
} catch (ConfigInvalidException e) {
|
||||
|
@@ -121,34 +121,38 @@ public abstract class OutgoingEmail {
|
||||
Set<Address> smtpRcptToPlaintextOnly = new HashSet<>();
|
||||
if (shouldSendMessage()) {
|
||||
if (fromId != null) {
|
||||
final Account fromUser = args.accountCache.get(fromId).getAccount();
|
||||
GeneralPreferencesInfo senderPrefs = fromUser.getGeneralPreferencesInfo();
|
||||
|
||||
if (senderPrefs != null && senderPrefs.getEmailStrategy() == CC_ON_OWN_COMMENTS) {
|
||||
// If we are impersonating a user, make sure they receive a CC of
|
||||
// this message so they can always review and audit what we sent
|
||||
// on their behalf to others.
|
||||
//
|
||||
add(RecipientType.CC, fromId);
|
||||
} else if (!accountsToNotify.containsValue(fromId) && rcptTo.remove(fromId)) {
|
||||
// If they don't want a copy, but we queued one up anyway,
|
||||
// drop them from the recipient lists.
|
||||
//
|
||||
removeUser(fromUser);
|
||||
AccountState fromUser = args.accountCache.get(fromId);
|
||||
if (fromUser != null) {
|
||||
GeneralPreferencesInfo senderPrefs = fromUser.getGeneralPreferences();
|
||||
if (senderPrefs != null && senderPrefs.getEmailStrategy() == CC_ON_OWN_COMMENTS) {
|
||||
// If we are impersonating a user, make sure they receive a CC of
|
||||
// this message so they can always review and audit what we sent
|
||||
// on their behalf to others.
|
||||
//
|
||||
add(RecipientType.CC, fromId);
|
||||
} else if (!accountsToNotify.containsValue(fromId) && rcptTo.remove(fromId)) {
|
||||
// If they don't want a copy, but we queued one up anyway,
|
||||
// drop them from the recipient lists.
|
||||
//
|
||||
removeUser(fromUser.getAccount());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check the preferences of all recipients. If any user has disabled
|
||||
// his email notifications then drop him from recipients' list.
|
||||
// In addition, check if users only want to receive plaintext email.
|
||||
for (Account.Id id : rcptTo) {
|
||||
Account thisUser = args.accountCache.get(id).getAccount();
|
||||
GeneralPreferencesInfo prefs = thisUser.getGeneralPreferencesInfo();
|
||||
if (prefs == null || prefs.getEmailStrategy() == DISABLED) {
|
||||
removeUser(thisUser);
|
||||
} else if (useHtml() && prefs.getEmailFormat() == EmailFormat.PLAINTEXT) {
|
||||
removeUser(thisUser);
|
||||
smtpRcptToPlaintextOnly.add(
|
||||
new Address(thisUser.getFullName(), thisUser.getPreferredEmail()));
|
||||
AccountState thisUser = args.accountCache.get(id);
|
||||
if (thisUser != null) {
|
||||
Account thisUserAccount = thisUser.getAccount();
|
||||
GeneralPreferencesInfo prefs = thisUser.getGeneralPreferences();
|
||||
if (prefs == null || prefs.getEmailStrategy() == DISABLED) {
|
||||
removeUser(thisUserAccount);
|
||||
} else if (useHtml() && prefs.getEmailFormat() == EmailFormat.PLAINTEXT) {
|
||||
removeUser(thisUserAccount);
|
||||
smtpRcptToPlaintextOnly.add(
|
||||
new Address(thisUserAccount.getFullName(), thisUserAccount.getPreferredEmail()));
|
||||
}
|
||||
}
|
||||
if (smtpRcptTo.isEmpty() && smtpRcptToPlaintextOnly.isEmpty()) {
|
||||
return;
|
||||
|
@@ -24,9 +24,8 @@ import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountResource;
|
||||
import com.google.gerrit.server.account.WatchConfig;
|
||||
import com.google.gerrit.server.account.AccountsUpdate;
|
||||
import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
|
||||
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
@@ -45,19 +44,16 @@ public class DeleteWatchedProjects
|
||||
implements RestModifyView<AccountResource, List<ProjectWatchInfo>> {
|
||||
private final Provider<IdentifiedUser> self;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final AccountCache accountCache;
|
||||
private final WatchConfig.Accessor watchConfig;
|
||||
private final AccountsUpdate.User accountsUpdate;
|
||||
|
||||
@Inject
|
||||
DeleteWatchedProjects(
|
||||
Provider<IdentifiedUser> self,
|
||||
PermissionBackend permissionBackend,
|
||||
AccountCache accountCache,
|
||||
WatchConfig.Accessor watchConfig) {
|
||||
AccountsUpdate.User accountsUpdate) {
|
||||
this.self = self;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.accountCache = accountCache;
|
||||
this.watchConfig = watchConfig;
|
||||
this.accountsUpdate = accountsUpdate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,14 +68,18 @@ public class DeleteWatchedProjects
|
||||
}
|
||||
|
||||
Account.Id accountId = rsrc.getUser().getAccountId();
|
||||
watchConfig.deleteProjectWatches(
|
||||
accountId,
|
||||
input
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(w -> ProjectWatchKey.create(new Project.NameKey(w.project), w.filter))
|
||||
.collect(toList()));
|
||||
accountCache.evict(accountId);
|
||||
accountsUpdate
|
||||
.create()
|
||||
.update(
|
||||
"Delete Project Watches via API",
|
||||
accountId,
|
||||
u ->
|
||||
u.deleteProjectWatches(
|
||||
input
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(w -> ProjectWatchKey.create(new Project.NameKey(w.project), w.filter))
|
||||
.collect(toList())));
|
||||
return Response.none();
|
||||
}
|
||||
}
|
||||
|
@@ -50,6 +50,6 @@ public class GetPreferences implements RestReadView<AccountResource> {
|
||||
}
|
||||
|
||||
Account.Id id = rsrc.getUser().getAccountId();
|
||||
return accountCache.get(id).getAccount().getGeneralPreferencesInfo();
|
||||
return accountCache.get(id).getGeneralPreferences();
|
||||
}
|
||||
}
|
||||
|
@@ -18,11 +18,13 @@ import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.gerrit.extensions.client.ProjectWatchInfo;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountResource;
|
||||
import com.google.gerrit.server.account.WatchConfig;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.account.Accounts;
|
||||
import com.google.gerrit.server.account.WatchConfig.NotifyType;
|
||||
import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
|
||||
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||
@@ -45,30 +47,31 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
public class GetWatchedProjects implements RestReadView<AccountResource> {
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final Provider<IdentifiedUser> self;
|
||||
private final WatchConfig.Accessor watchConfig;
|
||||
private final Accounts accounts;
|
||||
|
||||
@Inject
|
||||
public GetWatchedProjects(
|
||||
PermissionBackend permissionBackend,
|
||||
Provider<IdentifiedUser> self,
|
||||
WatchConfig.Accessor watchConfig) {
|
||||
PermissionBackend permissionBackend, Provider<IdentifiedUser> self, Accounts accounts) {
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.self = self;
|
||||
this.watchConfig = watchConfig;
|
||||
this.accounts = accounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProjectWatchInfo> apply(AccountResource rsrc)
|
||||
throws OrmException, AuthException, IOException, ConfigInvalidException,
|
||||
PermissionBackendException {
|
||||
PermissionBackendException, ResourceNotFoundException {
|
||||
if (self.get() != rsrc.getUser()) {
|
||||
permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
|
||||
}
|
||||
|
||||
Account.Id accountId = rsrc.getUser().getAccountId();
|
||||
AccountState account = accounts.get(accountId);
|
||||
if (account == null) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
List<ProjectWatchInfo> projectWatchInfos = new ArrayList<>();
|
||||
for (Map.Entry<ProjectWatchKey, Set<NotifyType>> e :
|
||||
watchConfig.getProjectWatches(accountId).entrySet()) {
|
||||
for (Map.Entry<ProjectWatchKey, Set<NotifyType>> e : account.getProjectWatches().entrySet()) {
|
||||
ProjectWatchInfo pwi = new ProjectWatchInfo();
|
||||
pwi.filter = e.getKey().filter();
|
||||
pwi.project = e.getKey().project().get();
|
||||
|
@@ -19,10 +19,9 @@ import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountResource;
|
||||
import com.google.gerrit.server.account.AccountsUpdate;
|
||||
import com.google.gerrit.server.account.WatchConfig;
|
||||
import com.google.gerrit.server.account.WatchConfig.NotifyType;
|
||||
import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
|
||||
@@ -49,8 +48,7 @@ public class PostWatchedProjects
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final GetWatchedProjects getWatchedProjects;
|
||||
private final ProjectsCollection projectsCollection;
|
||||
private final AccountCache accountCache;
|
||||
private final WatchConfig.Accessor watchConfig;
|
||||
private final AccountsUpdate.User accountsUpdate;
|
||||
|
||||
@Inject
|
||||
public PostWatchedProjects(
|
||||
@@ -58,14 +56,12 @@ public class PostWatchedProjects
|
||||
PermissionBackend permissionBackend,
|
||||
GetWatchedProjects getWatchedProjects,
|
||||
ProjectsCollection projectsCollection,
|
||||
AccountCache accountCache,
|
||||
WatchConfig.Accessor watchConfig) {
|
||||
AccountsUpdate.User accountsUpdate) {
|
||||
this.self = self;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.getWatchedProjects = getWatchedProjects;
|
||||
this.projectsCollection = projectsCollection;
|
||||
this.accountCache = accountCache;
|
||||
this.watchConfig = watchConfig;
|
||||
this.accountsUpdate = accountsUpdate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,9 +72,13 @@ public class PostWatchedProjects
|
||||
permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
|
||||
}
|
||||
|
||||
Account.Id accountId = rsrc.getUser().getAccountId();
|
||||
watchConfig.upsertProjectWatches(accountId, asMap(input));
|
||||
accountCache.evict(accountId);
|
||||
Map<ProjectWatchKey, Set<NotifyType>> projectWatches = asMap(input);
|
||||
accountsUpdate
|
||||
.create()
|
||||
.update(
|
||||
"Update Project Watches via API",
|
||||
rsrc.getUser().getAccountId(),
|
||||
u -> u.updateProjectWatches(projectWatches));
|
||||
return getWatchedProjects.apply(rsrc);
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,6 @@
|
||||
|
||||
package com.google.gerrit.server.restapi.account;
|
||||
|
||||
import static com.google.gerrit.server.config.ConfigUtil.storeSection;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE_COLUMN;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.KEY_ID;
|
||||
import static com.google.gerrit.server.git.UserConfigSections.KEY_MATCH;
|
||||
@@ -36,14 +35,14 @@ import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountResource;
|
||||
import com.google.gerrit.server.account.GeneralPreferencesLoader;
|
||||
import com.google.gerrit.server.account.AccountsUpdate;
|
||||
import com.google.gerrit.server.account.PreferencesConfig;
|
||||
import com.google.gerrit.server.account.VersionedAccountPreferences;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.UserConfigSections;
|
||||
import com.google.gerrit.server.permissions.GlobalPermission;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
@@ -52,7 +51,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
|
||||
@Singleton
|
||||
@@ -60,9 +58,7 @@ public class SetPreferences implements RestModifyView<AccountResource, GeneralPr
|
||||
private final Provider<CurrentUser> self;
|
||||
private final AccountCache cache;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final GeneralPreferencesLoader loader;
|
||||
private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
|
||||
private final AllUsersName allUsersName;
|
||||
private final AccountsUpdate.User accountsUpdate;
|
||||
private final DynamicMap<DownloadScheme> downloadSchemes;
|
||||
|
||||
@Inject
|
||||
@@ -70,60 +66,31 @@ public class SetPreferences implements RestModifyView<AccountResource, GeneralPr
|
||||
Provider<CurrentUser> self,
|
||||
AccountCache cache,
|
||||
PermissionBackend permissionBackend,
|
||||
GeneralPreferencesLoader loader,
|
||||
Provider<MetaDataUpdate.User> metaDataUpdateFactory,
|
||||
AllUsersName allUsersName,
|
||||
AccountsUpdate.User accountsUpdate,
|
||||
DynamicMap<DownloadScheme> downloadSchemes) {
|
||||
this.self = self;
|
||||
this.loader = loader;
|
||||
this.cache = cache;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.metaDataUpdateFactory = metaDataUpdateFactory;
|
||||
this.allUsersName = allUsersName;
|
||||
this.accountsUpdate = accountsUpdate;
|
||||
this.downloadSchemes = downloadSchemes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneralPreferencesInfo apply(AccountResource rsrc, GeneralPreferencesInfo i)
|
||||
public GeneralPreferencesInfo apply(AccountResource rsrc, GeneralPreferencesInfo input)
|
||||
throws AuthException, BadRequestException, IOException, ConfigInvalidException,
|
||||
PermissionBackendException {
|
||||
PermissionBackendException, OrmException {
|
||||
if (self.get() != rsrc.getUser()) {
|
||||
permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
|
||||
}
|
||||
|
||||
checkDownloadScheme(i.downloadScheme);
|
||||
checkDownloadScheme(input.downloadScheme);
|
||||
PreferencesConfig.validateMy(input.my);
|
||||
Account.Id id = rsrc.getUser().getAccountId();
|
||||
GeneralPreferencesInfo n = loader.merge(id, i);
|
||||
|
||||
n.changeTable = i.changeTable;
|
||||
n.my = i.my;
|
||||
n.urlAliases = i.urlAliases;
|
||||
|
||||
writeToGit(id, n);
|
||||
|
||||
return cache.get(id).getAccount().getGeneralPreferencesInfo();
|
||||
}
|
||||
|
||||
private void writeToGit(Account.Id id, GeneralPreferencesInfo i)
|
||||
throws RepositoryNotFoundException, IOException, ConfigInvalidException, BadRequestException {
|
||||
VersionedAccountPreferences prefs;
|
||||
try (MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName)) {
|
||||
prefs = VersionedAccountPreferences.forUser(id);
|
||||
prefs.load(md);
|
||||
|
||||
storeSection(
|
||||
prefs.getConfig(),
|
||||
UserConfigSections.GENERAL,
|
||||
null,
|
||||
i,
|
||||
loader.readDefaultsFromGit(md.getRepository(), null));
|
||||
|
||||
storeMyChangeTableColumns(prefs, i.changeTable);
|
||||
storeMyMenus(prefs, i.my);
|
||||
storeUrlAliases(prefs, i.urlAliases);
|
||||
prefs.commit(md);
|
||||
cache.evict(id);
|
||||
}
|
||||
accountsUpdate
|
||||
.create()
|
||||
.update("Set Preferences via API", id, u -> u.setGeneralPreferences(input));
|
||||
return cache.get(id).getGeneralPreferences();
|
||||
}
|
||||
|
||||
public static void storeMyMenus(VersionedAccountPreferences prefs, List<MenuItem> my)
|
||||
|
@@ -241,7 +241,7 @@ public class CreateChange
|
||||
IdentifiedUser me = user.get().asIdentifiedUser();
|
||||
PersonIdent author = me.newCommitterIdent(now, serverTimeZone);
|
||||
AccountState account = accountCache.get(me.getAccountId());
|
||||
GeneralPreferencesInfo info = account.getAccount().getGeneralPreferencesInfo();
|
||||
GeneralPreferencesInfo info = account.getGeneralPreferences();
|
||||
|
||||
ObjectId treeId = mergeTip == null ? emptyTreeId(oi) : mergeTip.getTree();
|
||||
ObjectId id = ChangeIdUtil.computeChangeId(treeId, mergeTip, author, author, input.subject);
|
||||
|
@@ -14,33 +14,25 @@
|
||||
|
||||
package com.google.gerrit.server.restapi.config;
|
||||
|
||||
import static com.google.gerrit.server.config.ConfigUtil.loadSection;
|
||||
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.server.account.GeneralPreferencesLoader;
|
||||
import com.google.gerrit.server.account.VersionedAccountPreferences;
|
||||
import com.google.gerrit.server.account.PreferencesConfig;
|
||||
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.git.UserConfigSections;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
@Singleton
|
||||
public class GetPreferences implements RestReadView<ConfigResource> {
|
||||
private final GeneralPreferencesLoader loader;
|
||||
private final GitRepositoryManager gitMgr;
|
||||
private final AllUsersName allUsersName;
|
||||
|
||||
@Inject
|
||||
public GetPreferences(
|
||||
GeneralPreferencesLoader loader, GitRepositoryManager gitMgr, AllUsersName allUsersName) {
|
||||
this.loader = loader;
|
||||
public GetPreferences(GitRepositoryManager gitMgr, AllUsersName allUsersName) {
|
||||
this.gitMgr = gitMgr;
|
||||
this.allUsersName = allUsersName;
|
||||
}
|
||||
@@ -48,30 +40,8 @@ public class GetPreferences implements RestReadView<ConfigResource> {
|
||||
@Override
|
||||
public GeneralPreferencesInfo apply(ConfigResource rsrc)
|
||||
throws IOException, ConfigInvalidException {
|
||||
return readFromGit(gitMgr, loader, allUsersName, null);
|
||||
}
|
||||
|
||||
static GeneralPreferencesInfo readFromGit(
|
||||
GitRepositoryManager gitMgr,
|
||||
GeneralPreferencesLoader loader,
|
||||
AllUsersName allUsersName,
|
||||
GeneralPreferencesInfo in)
|
||||
throws IOException, ConfigInvalidException, RepositoryNotFoundException {
|
||||
try (Repository git = gitMgr.openRepository(allUsersName)) {
|
||||
VersionedAccountPreferences p = VersionedAccountPreferences.forDefault();
|
||||
p.load(git);
|
||||
|
||||
GeneralPreferencesInfo r =
|
||||
loadSection(
|
||||
p.getConfig(),
|
||||
UserConfigSections.GENERAL,
|
||||
null,
|
||||
new GeneralPreferencesInfo(),
|
||||
GeneralPreferencesInfo.defaults(),
|
||||
in);
|
||||
|
||||
// TODO(davido): Maintain cache of default values in AllUsers repository
|
||||
return loader.loadMyMenusAndUrlAliases(r, p, null);
|
||||
return PreferencesConfig.readDefaultPreferences(git);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -14,10 +14,7 @@
|
||||
|
||||
package com.google.gerrit.server.restapi.config;
|
||||
|
||||
import static com.google.gerrit.server.config.ConfigUtil.loadSection;
|
||||
import static com.google.gerrit.server.config.ConfigUtil.skipField;
|
||||
import static com.google.gerrit.server.config.ConfigUtil.storeSection;
|
||||
import static com.google.gerrit.server.restapi.config.GetPreferences.readFromGit;
|
||||
|
||||
import com.google.gerrit.common.data.GlobalCapability;
|
||||
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
||||
@@ -25,20 +22,16 @@ import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.GeneralPreferencesLoader;
|
||||
import com.google.gerrit.server.account.VersionedAccountPreferences;
|
||||
import com.google.gerrit.server.account.PreferencesConfig;
|
||||
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.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.UserConfigSections;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -47,57 +40,31 @@ import org.slf4j.LoggerFactory;
|
||||
public class SetPreferences implements RestModifyView<ConfigResource, GeneralPreferencesInfo> {
|
||||
private static final Logger log = LoggerFactory.getLogger(SetPreferences.class);
|
||||
|
||||
private final GeneralPreferencesLoader loader;
|
||||
private final GitRepositoryManager gitManager;
|
||||
private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
|
||||
private final AllUsersName allUsersName;
|
||||
private final AccountCache accountCache;
|
||||
|
||||
@Inject
|
||||
SetPreferences(
|
||||
GeneralPreferencesLoader loader,
|
||||
GitRepositoryManager gitManager,
|
||||
Provider<MetaDataUpdate.User> metaDataUpdateFactory,
|
||||
AllUsersName allUsersName,
|
||||
AccountCache accountCache) {
|
||||
this.loader = loader;
|
||||
this.gitManager = gitManager;
|
||||
this.metaDataUpdateFactory = metaDataUpdateFactory;
|
||||
this.allUsersName = allUsersName;
|
||||
this.accountCache = accountCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneralPreferencesInfo apply(ConfigResource rsrc, GeneralPreferencesInfo i)
|
||||
public GeneralPreferencesInfo apply(ConfigResource rsrc, GeneralPreferencesInfo input)
|
||||
throws BadRequestException, IOException, ConfigInvalidException {
|
||||
if (!hasSetFields(i)) {
|
||||
if (!hasSetFields(input)) {
|
||||
throw new BadRequestException("unsupported option");
|
||||
}
|
||||
return writeToGit(readFromGit(gitManager, loader, allUsersName, i));
|
||||
}
|
||||
|
||||
private GeneralPreferencesInfo writeToGit(GeneralPreferencesInfo i)
|
||||
throws RepositoryNotFoundException, IOException, ConfigInvalidException, BadRequestException {
|
||||
PreferencesConfig.validateMy(input.my);
|
||||
try (MetaDataUpdate md = metaDataUpdateFactory.get().create(allUsersName)) {
|
||||
VersionedAccountPreferences p = VersionedAccountPreferences.forDefault();
|
||||
p.load(md);
|
||||
storeSection(
|
||||
p.getConfig(), UserConfigSections.GENERAL, null, i, GeneralPreferencesInfo.defaults());
|
||||
com.google.gerrit.server.restapi.account.SetPreferences.storeMyMenus(p, i.my);
|
||||
com.google.gerrit.server.restapi.account.SetPreferences.storeUrlAliases(p, i.urlAliases);
|
||||
p.commit(md);
|
||||
|
||||
GeneralPreferencesInfo updatedPrefs = PreferencesConfig.updateDefaultPreferences(md, input);
|
||||
accountCache.evictAllNoReindex();
|
||||
|
||||
GeneralPreferencesInfo r =
|
||||
loadSection(
|
||||
p.getConfig(),
|
||||
UserConfigSections.GENERAL,
|
||||
null,
|
||||
new GeneralPreferencesInfo(),
|
||||
GeneralPreferencesInfo.defaults(),
|
||||
null);
|
||||
return loader.loadMyMenusAndUrlAliases(r, p, null);
|
||||
return updatedPrefs;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -24,7 +24,8 @@ import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.account.WatchConfig;
|
||||
import com.google.gerrit.server.account.AccountConfig;
|
||||
import com.google.gerrit.server.account.InternalAccountUpdate;
|
||||
import com.google.gerrit.server.account.WatchConfig.NotifyType;
|
||||
import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
|
||||
import com.google.gerrit.server.config.AllUsersName;
|
||||
@@ -147,10 +148,14 @@ public class Schema_139 extends SchemaVersion {
|
||||
md.getCommitBuilder().setCommitter(serverUser);
|
||||
md.setMessage(MSG);
|
||||
|
||||
WatchConfig watchConfig = new WatchConfig(e.getKey());
|
||||
watchConfig.load(md);
|
||||
watchConfig.setProjectWatches(projectWatches);
|
||||
watchConfig.commit(md);
|
||||
AccountConfig accountConfig = new AccountConfig(e.getKey(), git);
|
||||
accountConfig.load(md);
|
||||
accountConfig.setAccountUpdate(
|
||||
InternalAccountUpdate.builder()
|
||||
.deleteProjectWatches(accountConfig.getProjectWatches().keySet())
|
||||
.updateProjectWatches(projectWatches)
|
||||
.build());
|
||||
accountConfig.commit(md);
|
||||
}
|
||||
}
|
||||
bru.execute(rw, NullProgressMonitor.INSTANCE);
|
||||
|
@@ -139,10 +139,7 @@ public class Schema_154 extends SchemaVersion {
|
||||
PersonIdent ident = serverIdent.get();
|
||||
md.getCommitBuilder().setAuthor(ident);
|
||||
md.getCommitBuilder().setCommitter(ident);
|
||||
AccountConfig accountConfig = new AccountConfig(account.getId());
|
||||
accountConfig.load(allUsersRepo);
|
||||
accountConfig.setAccount(account);
|
||||
accountConfig.commit(md);
|
||||
new AccountConfig(account.getId(), allUsersRepo).load().setAccount(account).commit(md);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
|
@@ -17,6 +17,7 @@ package com.google.gerrit.testing;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
@@ -79,6 +80,7 @@ public class FakeAccountCache implements AccountCache {
|
||||
new AllUsersName(AllUsersNameProvider.DEFAULT),
|
||||
account,
|
||||
ImmutableSet.of(),
|
||||
new HashMap<>());
|
||||
new HashMap<>(),
|
||||
GeneralPreferencesInfo.defaults());
|
||||
}
|
||||
}
|
||||
|
@@ -1345,7 +1345,7 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
WatchConfig.WATCH_CONFIG,
|
||||
wc.toText());
|
||||
PushOneCommit.Result r = push.to(RefNames.REFS_USERS_SELF);
|
||||
r.assertErrorStatus("invalid watch configuration");
|
||||
r.assertErrorStatus("invalid account configuration");
|
||||
r.assertMessage(
|
||||
String.format(
|
||||
"%s: Invalid project watch of account %d for project %s: %s",
|
||||
@@ -1935,7 +1935,8 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
@Test
|
||||
public void checkMetaId() throws Exception {
|
||||
// metaId is set when account is loaded
|
||||
assertThat(accounts.get(admin.getId()).getMetaId()).isEqualTo(getMetaId(admin.getId()));
|
||||
assertThat(accounts.get(admin.getId()).getAccount().getMetaId())
|
||||
.isEqualTo(getMetaId(admin.getId()));
|
||||
|
||||
// metaId is set when account is created
|
||||
AccountsUpdate au = accountsUpdate.create();
|
||||
@@ -2106,7 +2107,7 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
}
|
||||
assertThat(bgCounter.get()).isEqualTo(status.size());
|
||||
|
||||
Account updatedAccount = accounts.get(admin.id);
|
||||
Account updatedAccount = accounts.get(admin.id).getAccount();
|
||||
assertThat(updatedAccount.getStatus()).isEqualTo(Iterables.getLast(status));
|
||||
assertThat(updatedAccount.getFullName()).isEqualTo(admin.fullName);
|
||||
|
||||
|
@@ -27,7 +27,7 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
|
||||
@Test
|
||||
public void getGeneralPreferences() throws Exception {
|
||||
GeneralPreferencesInfo result = gApi.config().server().getDefaultPreferences();
|
||||
assertPrefs(result, GeneralPreferencesInfo.defaults(), "my");
|
||||
assertPrefs(result, GeneralPreferencesInfo.defaults(), "changeTable", "my");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -41,6 +41,6 @@ public class GeneralPreferencesIT extends AbstractDaemonTest {
|
||||
result = gApi.config().server().getDefaultPreferences();
|
||||
GeneralPreferencesInfo expected = GeneralPreferencesInfo.defaults();
|
||||
expected.signedOffBy = newSignedOffBy;
|
||||
assertPrefs(result, expected, "my");
|
||||
assertPrefs(result, expected, "changeTable", "my");
|
||||
}
|
||||
}
|
||||
|
@@ -26,30 +26,21 @@ import com.google.gerrit.common.data.Permission;
|
||||
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
||||
import com.google.gerrit.extensions.api.changes.StarsInput;
|
||||
import com.google.gerrit.extensions.common.GroupInfo;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.account.WatchConfig;
|
||||
import com.google.gerrit.server.account.WatchConfig.NotifyType;
|
||||
import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
|
||||
import com.google.gerrit.server.git.NotifyConfig;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gerrit.server.mail.Address;
|
||||
import com.google.gerrit.testing.FakeEmailSender.Message;
|
||||
import com.google.inject.Inject;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
||||
import org.eclipse.jgit.junit.TestRepository;
|
||||
import org.junit.Test;
|
||||
|
||||
@NoHttpd
|
||||
public class ProjectWatchIT extends AbstractDaemonTest {
|
||||
@Inject private WatchConfig.Accessor watchConfig;
|
||||
|
||||
@Test
|
||||
public void newPatchSetsNotifyConfig() throws Exception {
|
||||
Address addr = new Address("Watcher", "watcher@example.com");
|
||||
@@ -493,34 +484,6 @@ public class ProjectWatchIT extends AbstractDaemonTest {
|
||||
assertThat(sender.getMessages()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAllProjectWatches() throws Exception {
|
||||
Map<ProjectWatchKey, Set<NotifyType>> watches = new HashMap<>();
|
||||
watches.put(ProjectWatchKey.create(project, "*"), ImmutableSet.of(NotifyType.ALL));
|
||||
watchConfig.upsertProjectWatches(admin.getId(), watches);
|
||||
assertThat(watchConfig.getProjectWatches(admin.getId())).isNotEmpty();
|
||||
|
||||
watchConfig.deleteAllProjectWatches(admin.getId());
|
||||
assertThat(watchConfig.getProjectWatches(admin.getId())).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAllProjectWatchesIfWatchConfigIsTheOnlyFileInUserBranch() throws Exception {
|
||||
// Create account that has no files in its refs/users/ branch.
|
||||
Account.Id id = accountCreator.create().id;
|
||||
|
||||
// Add a project watch so that a watch.config file in the refs/users/ branch is created.
|
||||
Map<ProjectWatchKey, Set<NotifyType>> watches = new HashMap<>();
|
||||
watches.put(ProjectWatchKey.create(project, "*"), ImmutableSet.of(NotifyType.ALL));
|
||||
watchConfig.upsertProjectWatches(id, watches);
|
||||
assertThat(watchConfig.getProjectWatches(id)).isNotEmpty();
|
||||
|
||||
// Delete all project watches so that the watch.config file in the refs/users/ branch is
|
||||
// deleted.
|
||||
watchConfig.deleteAllProjectWatches(id);
|
||||
assertThat(watchConfig.getProjectWatches(id)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void watchProjectNoNotificationForPrivateChange() throws Exception {
|
||||
// watch project
|
||||
|
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
@@ -44,7 +45,12 @@ public class AccountFieldTest extends GerritBaseTests {
|
||||
List<String> values =
|
||||
toStrings(
|
||||
AccountField.REF_STATE.get(
|
||||
new AccountState(allUsersName, account, ImmutableSet.of(), ImmutableMap.of())));
|
||||
new AccountState(
|
||||
allUsersName,
|
||||
account,
|
||||
ImmutableSet.of(),
|
||||
ImmutableMap.of(),
|
||||
GeneralPreferencesInfo.defaults())));
|
||||
assertThat(values).hasSize(1);
|
||||
String expectedValue =
|
||||
allUsersName.get() + ":" + RefNames.refsUsers(account.getId()) + ":" + metaId;
|
||||
@@ -73,7 +79,11 @@ public class AccountFieldTest extends GerritBaseTests {
|
||||
toStrings(
|
||||
AccountField.EXTERNAL_ID_STATE.get(
|
||||
new AccountState(
|
||||
null, account, ImmutableSet.of(extId1, extId2), ImmutableMap.of())));
|
||||
null,
|
||||
account,
|
||||
ImmutableSet.of(extId1, extId2),
|
||||
ImmutableMap.of(),
|
||||
GeneralPreferencesInfo.defaults())));
|
||||
String expectedValue1 = extId1.key().sha1().name() + ":" + extId1.blobId().name();
|
||||
String expectedValue2 = extId2.key().sha1().name() + ":" + extId2.blobId().name();
|
||||
assertThat(values).containsExactly(expectedValue1, expectedValue2);
|
||||
|
@@ -22,6 +22,7 @@ import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
@@ -388,6 +389,7 @@ public class FromAddressGeneratorProviderTest {
|
||||
new AllUsersName(AllUsersNameProvider.DEFAULT),
|
||||
account,
|
||||
Collections.emptySet(),
|
||||
new HashMap<>());
|
||||
new HashMap<>(),
|
||||
GeneralPreferencesInfo.defaults());
|
||||
}
|
||||
}
|
||||
|
@@ -529,10 +529,10 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
|
||||
PersonIdent ident = serverIdent.get();
|
||||
md.getCommitBuilder().setAuthor(ident);
|
||||
md.getCommitBuilder().setCommitter(ident);
|
||||
AccountConfig accountConfig = new AccountConfig(accountId);
|
||||
accountConfig.load(repo);
|
||||
accountConfig.setAccountUpdate(InternalAccountUpdate.builder().setFullName(newName).build());
|
||||
accountConfig.commit(md);
|
||||
new AccountConfig(accountId, repo)
|
||||
.load()
|
||||
.setAccountUpdate(InternalAccountUpdate.builder().setFullName(newName).build())
|
||||
.commit(md);
|
||||
}
|
||||
|
||||
assertQuery("name:" + quote(user1.name), user1);
|
||||
|
@@ -106,7 +106,7 @@ public class Schema_159_to_160_Test {
|
||||
assertThat(myMenusFromApi(accountId).keySet()).containsExactlyElementsIn(newNames).inOrder();
|
||||
}
|
||||
|
||||
// Raw config values, bypassing the defaults set by GeneralPreferencesLoader.
|
||||
// Raw config values, bypassing the defaults set by PreferencesConfig.
|
||||
private ImmutableMap<String, String> myMenusFromNoteDb(Account.Id id) throws Exception {
|
||||
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||
VersionedAccountPreferences prefs = VersionedAccountPreferences.forUser(id);
|
||||
|
Reference in New Issue
Block a user