All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gerrit.server.account.AccountsUpdate Maven / Gradle / Ivy

There is a newer version: 3.10.1
Show newest version
// Copyright (C) 2017 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.checkArgument;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.gerrit.entities.Account;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.git.LockFailureException;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.Sequences;
import com.google.inject.BindingAnnotation;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.PersonIdent;

/**
 * Creates and updates accounts.
 *
 * 

This interface should be used for all account updates. See {@link AccountDelta} for what can * be updated. * *

For creating a new account a new account ID can be retrieved from {@link * Sequences#nextAccountId()}. * *

See the implementing classes for more information. */ public abstract class AccountsUpdate { public interface AccountsUpdateLoader { /** * Creates an {@code AccountsUpdate} which uses the identity of the specified user as author for * all commits related to accounts. The server identity will be used as committer. * *

Note: Please use this method with care and consider using the {@link * com.google.gerrit.server.UserInitiated} annotation on the provider of an {@code * AccountsUpdate} instead. * * @param currentUser the user to which modifications should be attributed */ AccountsUpdate create(IdentifiedUser currentUser); /** * Creates an {@code AccountsUpdate} which uses the server identity as author and committer for * all commits related to accounts. * *

Note: Please use this method with care and consider using the {@link * com.google.gerrit.server.ServerInitiated} annotation on the provider of an {@code * AccountsUpdate} instead. */ AccountsUpdate createWithServerIdent(); @BindingAnnotation @Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) @interface WithReindex {} @BindingAnnotation @Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) @interface NoReindex {} } /** Data holder for the set of arguments required to update an account. Used for batch updates. */ public static class UpdateArguments { public final String message; public final Account.Id accountId; public final AccountsUpdate.ConfigureDeltaFromState configureDeltaFromState; public UpdateArguments( String message, Account.Id accountId, AccountsUpdate.ConfigureDeltaFromState configureDeltaFromState) { this.message = message; this.accountId = accountId; this.configureDeltaFromState = configureDeltaFromState; } } /** * Account updates are commonly performed by evaluating the current account state and creating a * delta to be applied to it in a later step. This is done by implementing this interface. * *

If the current account state is not needed, use a {@link Consumer} of {@link * com.google.gerrit.server.account.AccountDelta.Builder} instead. */ @FunctionalInterface public interface ConfigureDeltaFromState { /** * Receives the current {@link AccountState} (which is immutable) and configures an {@link * com.google.gerrit.server.account.AccountDelta.Builder} with changes to the account. * * @param accountState the state of the account that is being updated * @param delta the changes to be applied */ void configure(AccountState accountState, AccountDelta.Builder delta) throws IOException; } /** Returns an instance that runs all specified consumers. */ public static ConfigureDeltaFromState joinConsumers( List> consumers) { return (accountStateIgnored, update) -> consumers.forEach(c -> c.accept(update)); } static ConfigureDeltaFromState fromConsumer(Consumer consumer) { return (a, u) -> consumer.accept(u); } protected final PersonIdent committerIdent; protected final PersonIdent authorIdent; protected final Optional currentUser; protected AccountsUpdate(PersonIdent serverIdent, Optional user) { this.currentUser = user; this.committerIdent = serverIdent; this.authorIdent = createPersonIdent(serverIdent, user); } /** * Like {@link #insert(String, Account.Id, ConfigureDeltaFromState)}, but using a {@link Consumer} * instead, i.e. the update does not depend on the current account state (which, for insertion, * would only contain the account ID). */ public AccountState insert( String message, Account.Id accountId, Consumer init) throws IOException, ConfigInvalidException { return insert(message, accountId, AccountsUpdate.fromConsumer(init)); } /** * Inserts a new account. * * @param message commit message for the account creation, must not be {@code null or empty} * @param accountId ID of the new account * @param init to populate the new account * @return the newly created account * @throws DuplicateKeyException if the account already exists * @throws IOException if creating the user branch fails due to an IO error * @throws ConfigInvalidException if any of the account fields has an invalid value */ public abstract AccountState insert( String message, Account.Id accountId, ConfigureDeltaFromState init) throws IOException, ConfigInvalidException; /** * Like {@link #update(String, Account.Id, ConfigureDeltaFromState)}, but using a {@link Consumer} * instead, i.e. the update does not depend on the current account state. */ @CanIgnoreReturnValue public Optional update( String message, Account.Id accountId, Consumer update) throws IOException, ConfigInvalidException { return update(message, accountId, AccountsUpdate.fromConsumer(update)); } /** * Gets the account and updates it atomically. * *

Changing the registration date of an account is not supported. * * @param message commit message for the account update, must not be {@code null or empty} * @param accountId ID of the account * @param configureDeltaFromState deltaBuilder to update the account, only invoked if the account * exists * @return the updated account, {@link Optional#empty} if the account doesn't exist * @throws IOException if updating the user branch fails due to an IO error * @throws LockFailureException if updating the user branch still fails due to concurrent updates * after the retry timeout exceeded * @throws ConfigInvalidException if any of the account fields has an invalid value */ @CanIgnoreReturnValue public Optional update( String message, Account.Id accountId, ConfigureDeltaFromState configureDeltaFromState) throws IOException, ConfigInvalidException { return updateBatch( ImmutableList.of(new UpdateArguments(message, accountId, configureDeltaFromState))) .get(0); } /** * Updates multiple different accounts atomically. This will only store a single new value (aka * set of all external IDs of the host) in the external ID cache, which is important for storage * economy. All {@code updates} must be for different accounts. * *

NOTE on error handling: Since updates are executed in multiple stages, with some stages * resulting from the union of all individual updates, we cannot point to the update that caused * the error. Callers should be aware that a single "update of death" (or a set of updates that * together have this property) will always prevent the entire batch from being executed. */ public ImmutableList> updateBatch(List updates) throws IOException, ConfigInvalidException { checkArgument( updates.stream().map(u -> u.accountId.get()).distinct().count() == updates.size(), "updates must all be for different accounts"); return executeUpdates(updates); } /** * Deletes all the account state data. * * @param message commit message for the account update, must not be {@code null or empty} * @param accountId ID of the account * @throws IOException if updating the user branch fails due to an IO error * @throws ConfigInvalidException if any of the account fields has an invalid value */ public abstract void delete(String message, Account.Id accountId) throws IOException, ConfigInvalidException; protected abstract ImmutableList> executeUpdates( List updates) throws ConfigInvalidException, IOException; private static PersonIdent createPersonIdent( PersonIdent serverIdent, Optional user) { return user.isPresent() ? user.get().newCommitterIdent(serverIdent) : serverIdent; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy