blob: 5b5895faaaee74014f5319280e2f38d2f93f5034 [file] [log] [blame]
// 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.acceptance.testsuite.account;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.gerrit.entities.Account;
import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountDelta;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.Accounts;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.AccountsUpdate.ConfigureDeltaFromState;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIdFactory;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.Optional;
import java.util.function.Consumer;
import org.eclipse.jgit.errors.ConfigInvalidException;
/**
* The implementation of {@code AccountOperations}.
*
* <p>There is only one implementation of {@code AccountOperations}. Nevertheless, we keep the
* separation between interface and implementation to enhance clarity.
*/
public class AccountOperationsImpl implements AccountOperations {
private final Accounts accounts;
private final AccountsUpdate accountsUpdate;
private final Sequences seq;
private final ExternalIdFactory externalIdFactory;
@Inject
public AccountOperationsImpl(
Accounts accounts,
@ServerInitiated AccountsUpdate accountsUpdate,
Sequences seq,
ExternalIdFactory externalIdFactory) {
this.accounts = accounts;
this.accountsUpdate = accountsUpdate;
this.seq = seq;
this.externalIdFactory = externalIdFactory;
}
@Override
public PerAccountOperations account(Account.Id accountId) {
return new PerAccountOperationsImpl(accountId);
}
@Override
public TestAccountCreation.Builder newAccount() {
return TestAccountCreation.builder(
this::createAccount, externalIdFactory.arePasswordsAllowed());
}
protected Account.Id createAccount(TestAccountCreation testAccountCreation) throws Exception {
Account.Id accountId = Account.id(seq.nextAccountId());
Consumer<AccountDelta.Builder> accountCreation =
deltaBuilder -> initAccountDelta(deltaBuilder, testAccountCreation, accountId);
AccountState createdAccount =
accountsUpdate.insert("Create Test Account", accountId, accountCreation);
return createdAccount.account().id();
}
private void initAccountDelta(
AccountDelta.Builder builder, TestAccountCreation accountCreation, Account.Id accountId) {
accountCreation.fullname().ifPresent(builder::setFullName);
accountCreation.preferredEmail().ifPresent(e -> setPreferredEmail(builder, accountId, e));
String httpPassword = accountCreation.httpPassword().orElse(null);
accountCreation.username().ifPresent(u -> setUsername(builder, accountId, u, httpPassword));
accountCreation.status().ifPresent(builder::setStatus);
accountCreation.active().ifPresent(builder::setActive);
accountCreation
.secondaryEmails()
.forEach(
secondaryEmail ->
builder.addExternalId(externalIdFactory.createEmail(accountId, secondaryEmail)));
}
private void setPreferredEmail(
AccountDelta.Builder builder, Account.Id accountId, String preferredEmail) {
builder
.setPreferredEmail(preferredEmail)
.addExternalId(externalIdFactory.createEmail(accountId, preferredEmail));
}
private void setUsername(
AccountDelta.Builder builder, Account.Id accountId, String username, String httpPassword) {
builder.addExternalId(externalIdFactory.createUsername(username, accountId, httpPassword));
}
private class PerAccountOperationsImpl implements PerAccountOperations {
private final Account.Id accountId;
PerAccountOperationsImpl(Account.Id accountId) {
this.accountId = accountId;
}
@Override
public boolean exists() {
return getAccountState(accountId).isPresent();
}
@Override
public TestAccount get() {
AccountState account =
getAccountState(accountId)
.orElseThrow(
() -> new IllegalStateException("Tried to get non-existing test account"));
return toTestAccount(account);
}
private Optional<AccountState> getAccountState(Account.Id accountId) {
try {
return accounts.get(accountId);
} catch (IOException | ConfigInvalidException e) {
throw new IllegalStateException(e);
}
}
private TestAccount toTestAccount(AccountState accountState) {
Account account = accountState.account();
return TestAccount.builder()
.accountId(account.id())
.preferredEmail(Optional.ofNullable(account.preferredEmail()))
.fullname(Optional.ofNullable(account.fullName()))
.username(accountState.userName())
.active(accountState.account().isActive())
.emails(ExternalId.getEmails(accountState.externalIds()).collect(toImmutableSet()))
.build();
}
@Override
public TestAccountUpdate.Builder forUpdate() {
return TestAccountUpdate.builder(this::updateAccount);
}
private void updateAccount(TestAccountUpdate accountUpdate)
throws IOException, ConfigInvalidException {
ConfigureDeltaFromState configureDeltaFromState =
(accountState, deltaBuilder) -> fillBuilder(deltaBuilder, accountUpdate, accountState);
Optional<AccountState> updatedAccount = updateAccount(configureDeltaFromState);
checkState(updatedAccount.isPresent(), "Tried to update non-existing test account");
}
@CanIgnoreReturnValue
private Optional<AccountState> updateAccount(ConfigureDeltaFromState configureDeltaFromState)
throws IOException, ConfigInvalidException {
return accountsUpdate.update("Update Test Account", accountId, configureDeltaFromState);
}
private void fillBuilder(
AccountDelta.Builder builder, TestAccountUpdate accountUpdate, AccountState accountState) {
accountUpdate.fullname().ifPresent(builder::setFullName);
accountUpdate.preferredEmail().ifPresent(e -> setPreferredEmail(builder, accountId, e));
String httpPassword = accountUpdate.httpPassword().orElse(null);
accountUpdate.username().ifPresent(u -> setUsername(builder, accountId, u, httpPassword));
accountUpdate.status().ifPresent(builder::setStatus);
accountUpdate.active().ifPresent(builder::setActive);
ImmutableSet<String> secondaryEmails = getSecondaryEmails(accountUpdate, accountState);
ImmutableSet<String> newSecondaryEmails =
ImmutableSet.copyOf(accountUpdate.secondaryEmailsModification().apply(secondaryEmails));
if (!secondaryEmails.equals(newSecondaryEmails)) {
setSecondaryEmails(builder, accountUpdate, accountState, newSecondaryEmails);
}
}
private ImmutableSet<String> getSecondaryEmails(
TestAccountUpdate accountUpdate, AccountState accountState) {
ImmutableSet<String> allEmails =
ExternalId.getEmails(accountState.externalIds()).collect(toImmutableSet());
if (accountUpdate.preferredEmail().isPresent()) {
return ImmutableSet.copyOf(
Sets.difference(allEmails, ImmutableSet.of(accountUpdate.preferredEmail().get())));
} else if (accountState.account().preferredEmail() != null) {
return ImmutableSet.copyOf(
Sets.difference(allEmails, ImmutableSet.of(accountState.account().preferredEmail())));
}
return allEmails;
}
private void setSecondaryEmails(
AccountDelta.Builder builder,
TestAccountUpdate accountUpdate,
AccountState accountState,
ImmutableSet<String> newSecondaryEmails) {
// delete all external IDs of SCHEME_MAILTO scheme, then add back SCHEME_MAILTO external IDs
// for the new secondary emails and the preferred email
builder.deleteExternalIds(
accountState.externalIds().stream()
.filter(e -> e.isScheme(ExternalId.SCHEME_MAILTO))
.collect(toImmutableSet()));
builder.addExternalIds(
newSecondaryEmails.stream()
.map(secondaryEmail -> externalIdFactory.createEmail(accountId, secondaryEmail))
.collect(toImmutableSet()));
if (accountUpdate.preferredEmail().isPresent()) {
builder.addExternalId(
externalIdFactory.createEmail(accountId, accountUpdate.preferredEmail().get()));
} else if (accountState.account().preferredEmail() != null) {
builder.addExternalId(
externalIdFactory.createEmail(accountId, accountState.account().preferredEmail()));
}
}
@Override
public TestAccountInvalidation.Builder forInvalidation() {
return TestAccountInvalidation.builder(this::invalidateAccount);
}
private void invalidateAccount(TestAccountInvalidation testAccountInvalidation)
throws Exception {
Optional<AccountState> accountState = getAccountState(accountId);
checkState(accountState.isPresent(), "Tried to invalidate a non-existing test account");
if (testAccountInvalidation.preferredEmailWithoutExternalId().isPresent()) {
updateAccount(
(account, deltaBuilder) ->
deltaBuilder.setPreferredEmail(
testAccountInvalidation.preferredEmailWithoutExternalId().get()));
}
}
}
}