Remove unused GroupRebuilder
GroupRebuilder was rebuilding an entire group's NoteDb refs from group
data read from ReviewDb. Since ReviewDb is gone, we no longer can
rebuild group NoteDb refs.
Change-Id: I00bc26b44e3842d42faedd39d745c9a75bc6d70a
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/java/com/google/gerrit/server/schema/GroupBundle.java b/java/com/google/gerrit/server/schema/GroupBundle.java
deleted file mode 100644
index 26cd96a..0000000
--- a/java/com/google/gerrit/server/schema/GroupBundle.java
+++ /dev/null
@@ -1,778 +0,0 @@
-// 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.schema;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.checkColumns;
-import static java.util.Comparator.naturalOrder;
-import static java.util.Comparator.nullsLast;
-import static java.util.stream.Collectors.toList;
-
-import com.google.auto.value.AutoValue;
-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;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.Streams;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gerrit.server.group.InternalGroup;
-import com.google.gerrit.server.group.db.AuditLogReader;
-import com.google.gerrit.server.group.db.GroupConfig;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Stream;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Repository;
-
-/**
- * A bundle of all entities rooted at a single {@link AccountGroup} entity.
- *
- * <p>Used primarily during the migration process. Most callers should prefer {@link InternalGroup}
- * instead.
- */
-@AutoValue
-abstract class GroupBundle {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
- static {
- // Initialization-time checks that the column set hasn't changed since the
- // last time this file was updated.
- checkColumns(AccountGroup.NameKey.class, 1);
- checkColumns(AccountGroup.UUID.class, 1);
- checkColumns(AccountGroup.Id.class, 1);
- checkColumns(AccountGroup.class, 1, 2, 4, 7, 9, 10, 11);
-
- checkColumns(AccountGroupById.Key.class, 1, 2);
- checkColumns(AccountGroupById.class, 1);
-
- checkColumns(AccountGroupByIdAud.Key.class, 1, 2, 3);
- checkColumns(AccountGroupByIdAud.class, 1, 2, 3, 4);
-
- checkColumns(AccountGroupMember.Key.class, 1, 2);
- checkColumns(AccountGroupMember.class, 1);
-
- checkColumns(AccountGroupMemberAudit.Key.class, 1, 2, 3);
- checkColumns(AccountGroupMemberAudit.class, 1, 2, 3, 4);
- }
-
- public enum Source {
- REVIEW_DB("ReviewDb"),
- NOTE_DB("NoteDb");
-
- private final String name;
-
- private Source(String name) {
- this.name = name;
- }
-
- @Override
- public String toString() {
- return name;
- }
- }
-
- @Singleton
- public static class Factory {
- private final AuditLogReader auditLogReader;
-
- @Inject
- Factory(AuditLogReader auditLogReader) {
- this.auditLogReader = auditLogReader;
- }
-
- public GroupBundle fromNoteDb(
- Project.NameKey projectName, Repository repo, AccountGroup.UUID uuid)
- throws ConfigInvalidException, IOException {
- GroupConfig groupConfig = GroupConfig.loadForGroup(projectName, repo, uuid);
- InternalGroup internalGroup = groupConfig.getLoadedGroup().get();
- AccountGroup.Id groupId = internalGroup.getId();
-
- AccountGroup accountGroup =
- new AccountGroup(
- internalGroup.getNameKey(),
- internalGroup.getId(),
- internalGroup.getGroupUUID(),
- internalGroup.getCreatedOn());
- accountGroup.setDescription(internalGroup.getDescription());
- accountGroup.setOwnerGroupUUID(internalGroup.getOwnerGroupUUID());
- accountGroup.setVisibleToAll(internalGroup.isVisibleToAll());
-
- return create(
- Source.NOTE_DB,
- accountGroup,
- internalGroup
- .getMembers()
- .stream()
- .map(
- accountId ->
- new AccountGroupMember(new AccountGroupMember.Key(accountId, groupId)))
- .collect(toImmutableSet()),
- auditLogReader.getMembersAudit(repo, uuid),
- internalGroup
- .getSubgroups()
- .stream()
- .map(
- subgroupUuid ->
- new AccountGroupById(new AccountGroupById.Key(groupId, subgroupUuid)))
- .collect(toImmutableSet()),
- auditLogReader.getSubgroupsAudit(repo, uuid));
- }
-
- public static GroupBundle fromReviewDb(ReviewDb db, AccountGroup.UUID groupUuid)
- throws OrmException {
- JdbcSchema jdbcSchema = ReviewDbWrapper.unwrapJbdcSchema(db);
- AccountGroup group = readAccountGroupFromReviewDb(jdbcSchema, groupUuid);
- AccountGroup.Id groupId = group.getId();
-
- return create(
- Source.REVIEW_DB,
- group,
- readAccountGroupMembersFromReviewDb(jdbcSchema, groupId),
- readAccountGroupMemberAuditsFromReviewDb(jdbcSchema, groupId),
- readAccountGroupSubgroupsFromReviewDb(jdbcSchema, groupId),
- readAccountGroupSubgroupAuditsFromReviewDb(jdbcSchema, groupId));
- }
-
- private static AccountGroup readAccountGroupFromReviewDb(
- JdbcSchema jdbcSchema, AccountGroup.UUID groupUuid) throws OrmException {
- try (Statement stmt = jdbcSchema.getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT group_id,"
- + " name,"
- + " created_on,"
- + " description,"
- + " owner_group_uuid,"
- + " visible_to_all"
- + " FROM account_groups"
- + " WHERE group_uuid = '"
- + groupUuid.get()
- + "'")) {
- if (!rs.next()) {
- throw new OrmException(String.format("Group %s not found", groupUuid));
- }
-
- AccountGroup.Id groupId = new AccountGroup.Id(rs.getInt(1));
- AccountGroup.NameKey groupName = new AccountGroup.NameKey(rs.getString(2));
- Timestamp createdOn = rs.getTimestamp(3);
- String description = rs.getString(4);
- AccountGroup.UUID ownerGroupUuid = new AccountGroup.UUID(rs.getString(5));
- boolean visibleToAll = "Y".equals(rs.getString(6));
-
- AccountGroup group = new AccountGroup(groupName, groupId, groupUuid, createdOn);
- group.setDescription(description);
- group.setOwnerGroupUUID(ownerGroupUuid);
- group.setVisibleToAll(visibleToAll);
-
- if (rs.next()) {
- throw new OrmException(String.format("Group UUID %s is ambiguous", groupUuid));
- }
-
- return group;
- } catch (SQLException e) {
- throw new OrmException(
- String.format("Failed to read account group %s from ReviewDb", groupUuid.get()), e);
- }
- }
-
- private static List<AccountGroupMember> readAccountGroupMembersFromReviewDb(
- JdbcSchema jdbcSchema, AccountGroup.Id groupId) throws OrmException {
- try (Statement stmt = jdbcSchema.getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT account_id"
- + " FROM account_group_members"
- + " WHERE group_id = '"
- + groupId.get()
- + "'")) {
- List<AccountGroupMember> members = new ArrayList<>();
- while (rs.next()) {
- Account.Id accountId = new Account.Id(rs.getInt(1));
- members.add(new AccountGroupMember(new AccountGroupMember.Key(accountId, groupId)));
- }
- return members;
- } catch (SQLException e) {
- throw new OrmException(
- String.format(
- "Failed to read members of account group %s from ReviewDb", groupId.get()),
- e);
- }
- }
-
- private static List<AccountGroupMemberAudit> readAccountGroupMemberAuditsFromReviewDb(
- JdbcSchema jdbcSchema, AccountGroup.Id groupId) throws OrmException {
- try (Statement stmt = jdbcSchema.getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT account_id, added_by, added_on, removed_by, removed_on"
- + " FROM account_group_members_audit"
- + " WHERE group_id = '"
- + groupId.get()
- + "'")) {
- List<AccountGroupMemberAudit> audits = new ArrayList<>();
- while (rs.next()) {
- Account.Id accountId = new Account.Id(rs.getInt(1));
-
- Account.Id addedBy = new Account.Id(rs.getInt(2));
- Timestamp addedOn = rs.getTimestamp(3);
-
- Timestamp removedOn = rs.getTimestamp(5);
- Account.Id removedBy = removedOn != null ? new Account.Id(rs.getInt(4)) : null;
-
- AccountGroupMemberAudit.Key key =
- new AccountGroupMemberAudit.Key(accountId, groupId, addedOn);
- AccountGroupMemberAudit audit = new AccountGroupMemberAudit(key, addedBy);
- audit.removed(removedBy, removedOn);
- audits.add(audit);
- }
- return audits;
- } catch (SQLException e) {
- throw new OrmException(
- String.format(
- "Failed to read member audits of account group %s from ReviewDb", groupId.get()),
- e);
- }
- }
-
- private static List<AccountGroupById> readAccountGroupSubgroupsFromReviewDb(
- JdbcSchema jdbcSchema, AccountGroup.Id groupId) throws OrmException {
- try (Statement stmt = jdbcSchema.getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT include_uuid"
- + " FROM account_group_by_id"
- + " WHERE group_id = '"
- + groupId.get()
- + "'")) {
- List<AccountGroupById> subgroups = new ArrayList<>();
- while (rs.next()) {
- AccountGroup.UUID includedGroupUuid = new AccountGroup.UUID(rs.getString(1));
- subgroups.add(new AccountGroupById(new AccountGroupById.Key(groupId, includedGroupUuid)));
- }
- return subgroups;
- } catch (SQLException e) {
- throw new OrmException(
- String.format(
- "Failed to read subgroups of account group %s from ReviewDb", groupId.get()),
- e);
- }
- }
-
- private static List<AccountGroupByIdAud> readAccountGroupSubgroupAuditsFromReviewDb(
- JdbcSchema jdbcSchema, AccountGroup.Id groupId) throws OrmException {
- try (Statement stmt = jdbcSchema.getConnection().createStatement();
- ResultSet rs =
- stmt.executeQuery(
- "SELECT include_uuid, added_by, added_on, removed_by, removed_on"
- + " FROM account_group_by_id_aud"
- + " WHERE group_id = '"
- + groupId.get()
- + "'")) {
- List<AccountGroupByIdAud> audits = new ArrayList<>();
- while (rs.next()) {
- AccountGroup.UUID includedGroupUuid = new AccountGroup.UUID(rs.getString(1));
-
- Account.Id addedBy = new Account.Id(rs.getInt(2));
- Timestamp addedOn = rs.getTimestamp(3);
-
- Timestamp removedOn = rs.getTimestamp(5);
- Account.Id removedBy = removedOn != null ? new Account.Id(rs.getInt(4)) : null;
-
- AccountGroupByIdAud.Key key =
- new AccountGroupByIdAud.Key(groupId, includedGroupUuid, addedOn);
- AccountGroupByIdAud audit = new AccountGroupByIdAud(key, addedBy);
- audit.removed(removedBy, removedOn);
- audits.add(audit);
- }
- return audits;
- } catch (SQLException e) {
- throw new OrmException(
- String.format(
- "Failed to read subgroup audits of account group %s from ReviewDb", groupId.get()),
- e);
- }
- }
- }
-
- private static final Comparator<AccountGroupMember> ACCOUNT_GROUP_MEMBER_COMPARATOR =
- Comparator.comparingInt((AccountGroupMember m) -> m.getAccountGroupId().get())
- .thenComparingInt(m -> m.getAccountId().get());
-
- private static final Comparator<AccountGroupMemberAudit> ACCOUNT_GROUP_MEMBER_AUDIT_COMPARATOR =
- Comparator.comparingInt((AccountGroupMemberAudit a) -> a.getGroupId().get())
- .thenComparing(AccountGroupMemberAudit::getAddedOn)
- .thenComparingInt(a -> a.getAddedBy().get())
- .thenComparingInt(a -> a.getMemberId().get())
- .thenComparing(
- a -> a.getRemovedBy() != null ? a.getRemovedBy().get() : null,
- nullsLast(naturalOrder()))
- .thenComparing(AccountGroupMemberAudit::getRemovedOn, nullsLast(naturalOrder()));
-
- private static final Comparator<AccountGroupById> ACCOUNT_GROUP_BY_ID_COMPARATOR =
- Comparator.comparingInt((AccountGroupById m) -> m.getGroupId().get())
- .thenComparing(AccountGroupById::getIncludeUUID);
-
- private static final Comparator<AccountGroupByIdAud> ACCOUNT_GROUP_BY_ID_AUD_COMPARATOR =
- Comparator.comparingInt((AccountGroupByIdAud a) -> a.getGroupId().get())
- .thenComparing(AccountGroupByIdAud::getAddedOn)
- .thenComparingInt(a -> a.getAddedBy().get())
- .thenComparing(AccountGroupByIdAud::getIncludeUUID)
- .thenComparing(
- a -> a.getRemovedBy() != null ? a.getRemovedBy().get() : null,
- nullsLast(naturalOrder()))
- .thenComparing(AccountGroupByIdAud::getRemovedOn, nullsLast(naturalOrder()));
-
- private static final Comparator<AuditEntry> AUDIT_ENTRY_COMPARATOR =
- Comparator.comparing(AuditEntry::getTimestamp)
- .thenComparing(AuditEntry::getAction, Comparator.comparingInt(Action::getOrder));
-
- public static GroupBundle create(
- Source source,
- AccountGroup group,
- Iterable<AccountGroupMember> members,
- Iterable<AccountGroupMemberAudit> memberAudit,
- Iterable<AccountGroupById> byId,
- Iterable<AccountGroupByIdAud> byIdAudit) {
- AccountGroup.UUID uuid = group.getGroupUUID();
- return new AutoValue_GroupBundle.Builder()
- .source(source)
- .group(group)
- .members(
- logIfNotUnique(
- source, uuid, members, ACCOUNT_GROUP_MEMBER_COMPARATOR, AccountGroupMember.class))
- .memberAudit(
- logIfNotUnique(
- source,
- uuid,
- memberAudit,
- ACCOUNT_GROUP_MEMBER_AUDIT_COMPARATOR,
- AccountGroupMemberAudit.class))
- .byId(
- logIfNotUnique(
- source, uuid, byId, ACCOUNT_GROUP_BY_ID_COMPARATOR, AccountGroupById.class))
- .byIdAudit(
- logIfNotUnique(
- source,
- uuid,
- byIdAudit,
- ACCOUNT_GROUP_BY_ID_AUD_COMPARATOR,
- AccountGroupByIdAud.class))
- .build();
- }
-
- private static <T> ImmutableSet<T> logIfNotUnique(
- Source source,
- AccountGroup.UUID uuid,
- Iterable<T> iterable,
- Comparator<T> comparator,
- Class<T> clazz) {
- List<T> list = Streams.stream(iterable).sorted(comparator).collect(toList());
- ImmutableSet<T> set = ImmutableSet.copyOf(list);
- if (set.size() != list.size()) {
- // One way this can happen is that distinct audit entities can compare equal, because
- // AccountGroup{MemberAudit,ByIdAud}.Key does not include the addedOn timestamp in its
- // members() list. However, this particular issue only applies to pure adds, since removedOn
- // *is* included in equality. As a result, if this happens, it means the audit log is already
- // corrupt, and it's not clear if we can programmatically repair it. For migrating to NoteDb,
- // we'll try our best to recreate it, but no guarantees it will match the real sequence of
- // attempted operations, which is in any case lost in the mists of time.
- logger.atWarning().log(
- "group %s in %s has duplicate %s entities: %s",
- uuid, source, clazz.getSimpleName(), iterable);
- }
- return set;
- }
-
- static Builder builder() {
- return new AutoValue_GroupBundle.Builder().members().memberAudit().byId().byIdAudit();
- }
-
- public static ImmutableList<String> compareWithAudits(
- GroupBundle reviewDbBundle, GroupBundle noteDbBundle) {
- return compare(reviewDbBundle, noteDbBundle, true);
- }
-
- public static ImmutableList<String> compareWithoutAudits(
- GroupBundle reviewDbBundle, GroupBundle noteDbBundle) {
- return compare(reviewDbBundle, noteDbBundle, false);
- }
-
- private static ImmutableList<String> compare(
- GroupBundle reviewDbBundle, GroupBundle noteDbBundle, boolean compareAudits) {
- // Normalize the ReviewDb bundle to what we expect in NoteDb. This means that values in error
- // messages will not reflect the actual data in ReviewDb, but it will make it easier for humans
- // to see the difference.
- reviewDbBundle = reviewDbBundle.truncateToSecond();
- AccountGroup reviewDbGroup = new AccountGroup(reviewDbBundle.group());
- reviewDbGroup.setDescription(Strings.emptyToNull(reviewDbGroup.getDescription()));
- reviewDbBundle = reviewDbBundle.toBuilder().group(reviewDbGroup).build();
-
- checkArgument(
- reviewDbBundle.source() == Source.REVIEW_DB,
- "first bundle's source must be %s: %s",
- Source.REVIEW_DB,
- reviewDbBundle);
- checkArgument(
- noteDbBundle.source() == Source.NOTE_DB,
- "second bundle's source must be %s: %s",
- Source.NOTE_DB,
- noteDbBundle);
-
- ImmutableList.Builder<String> result = ImmutableList.builder();
- if (!reviewDbBundle.group().equals(noteDbBundle.group())) {
- result.add(
- "AccountGroups differ\n"
- + ("ReviewDb: " + reviewDbBundle.group() + "\n")
- + ("NoteDb : " + noteDbBundle.group()));
- }
- if (!reviewDbBundle.members().equals(noteDbBundle.members())) {
- result.add(
- "AccountGroupMembers differ\n"
- + ("ReviewDb: " + reviewDbBundle.members() + "\n")
- + ("NoteDb : " + noteDbBundle.members()));
- }
- if (compareAudits
- && !areMemberAuditsConsideredEqual(
- reviewDbBundle.memberAudit(), noteDbBundle.memberAudit())) {
- result.add(
- "AccountGroupMemberAudits differ\n"
- + ("ReviewDb: " + reviewDbBundle.memberAudit() + "\n")
- + ("NoteDb : " + noteDbBundle.memberAudit()));
- }
- if (!reviewDbBundle.byId().equals(noteDbBundle.byId())) {
- result.add(
- "AccountGroupByIds differ\n"
- + ("ReviewDb: " + reviewDbBundle.byId() + "\n")
- + ("NoteDb : " + noteDbBundle.byId()));
- }
- if (compareAudits
- && !areByIdAuditsConsideredEqual(reviewDbBundle.byIdAudit(), noteDbBundle.byIdAudit())) {
- result.add(
- "AccountGroupByIdAudits differ\n"
- + ("ReviewDb: " + reviewDbBundle.byIdAudit() + "\n")
- + ("NoteDb : " + noteDbBundle.byIdAudit()));
- }
- return result.build();
- }
-
- private static boolean areMemberAuditsConsideredEqual(
- ImmutableSet<AccountGroupMemberAudit> reviewDbMemberAudits,
- ImmutableSet<AccountGroupMemberAudit> noteDbMemberAudits) {
- ListMultimap<String, AuditEntry> reviewDbMemberAuditsByMemberId =
- toMemberAuditEntriesByMemberId(reviewDbMemberAudits);
- ListMultimap<String, AuditEntry> noteDbMemberAuditsByMemberId =
- toMemberAuditEntriesByMemberId(noteDbMemberAudits);
-
- return areConsideredEqual(reviewDbMemberAuditsByMemberId, noteDbMemberAuditsByMemberId);
- }
-
- private static boolean areByIdAuditsConsideredEqual(
- ImmutableSet<AccountGroupByIdAud> reviewDbByIdAudits,
- ImmutableSet<AccountGroupByIdAud> noteDbByIdAudits) {
- ListMultimap<String, AuditEntry> reviewDbByIdAuditsById =
- toByIdAuditEntriesById(reviewDbByIdAudits);
- ListMultimap<String, AuditEntry> noteDbByIdAuditsById =
- toByIdAuditEntriesById(noteDbByIdAudits);
-
- return areConsideredEqual(reviewDbByIdAuditsById, noteDbByIdAuditsById);
- }
-
- private static ListMultimap<String, AuditEntry> toMemberAuditEntriesByMemberId(
- ImmutableSet<AccountGroupMemberAudit> memberAudits) {
- return memberAudits
- .stream()
- .flatMap(GroupBundle::toAuditEntries)
- .collect(
- Multimaps.toMultimap(
- AuditEntry::getTarget,
- Function.identity(),
- MultimapBuilder.hashKeys().arrayListValues()::build));
- }
-
- private static Stream<AuditEntry> toAuditEntries(AccountGroupMemberAudit memberAudit) {
- AuditEntry additionAuditEntry =
- AuditEntry.create(
- Action.ADD,
- memberAudit.getAddedBy(),
- memberAudit.getMemberId(),
- memberAudit.getAddedOn());
- if (memberAudit.isActive()) {
- return Stream.of(additionAuditEntry);
- }
-
- AuditEntry removalAuditEntry =
- AuditEntry.create(
- Action.REMOVE,
- memberAudit.getRemovedBy(),
- memberAudit.getMemberId(),
- memberAudit.getRemovedOn());
- return Stream.of(additionAuditEntry, removalAuditEntry);
- }
-
- private static ListMultimap<String, AuditEntry> toByIdAuditEntriesById(
- ImmutableSet<AccountGroupByIdAud> byIdAudits) {
- return byIdAudits
- .stream()
- .flatMap(GroupBundle::toAuditEntries)
- .collect(
- Multimaps.toMultimap(
- AuditEntry::getTarget,
- Function.identity(),
- MultimapBuilder.hashKeys().arrayListValues()::build));
- }
-
- private static Stream<AuditEntry> toAuditEntries(AccountGroupByIdAud byIdAudit) {
- AuditEntry additionAuditEntry =
- AuditEntry.create(
- Action.ADD, byIdAudit.getAddedBy(), byIdAudit.getIncludeUUID(), byIdAudit.getAddedOn());
- if (byIdAudit.isActive()) {
- return Stream.of(additionAuditEntry);
- }
-
- AuditEntry removalAuditEntry =
- AuditEntry.create(
- Action.REMOVE,
- byIdAudit.getRemovedBy(),
- byIdAudit.getIncludeUUID(),
- byIdAudit.getRemovedOn());
- return Stream.of(additionAuditEntry, removalAuditEntry);
- }
-
- /**
- * Determines whether the audit log entries are equal except for redundant entries. Entries of the
- * same type (addition/removal) which follow directly on each other according to their timestamp
- * are considered redundant.
- */
- private static boolean areConsideredEqual(
- ListMultimap<String, AuditEntry> reviewDbMemberAuditsByTarget,
- ListMultimap<String, AuditEntry> noteDbMemberAuditsByTarget) {
- for (String target : reviewDbMemberAuditsByTarget.keySet()) {
- ImmutableList<AuditEntry> reviewDbAuditEntries =
- reviewDbMemberAuditsByTarget
- .get(target)
- .stream()
- .sorted(AUDIT_ENTRY_COMPARATOR)
- .collect(toImmutableList());
- ImmutableSet<AuditEntry> noteDbAuditEntries =
- noteDbMemberAuditsByTarget
- .get(target)
- .stream()
- .sorted(AUDIT_ENTRY_COMPARATOR)
- .collect(toImmutableSet());
-
- int reviewDbIndex = 0;
- for (AuditEntry noteDbAuditEntry : noteDbAuditEntries) {
- Set<AuditEntry> redundantReviewDbAuditEntries = new HashSet<>();
- while (reviewDbIndex < reviewDbAuditEntries.size()) {
- AuditEntry reviewDbAuditEntry = reviewDbAuditEntries.get(reviewDbIndex);
- if (!reviewDbAuditEntry.getAction().equals(noteDbAuditEntry.getAction())) {
- break;
- }
- redundantReviewDbAuditEntries.add(reviewDbAuditEntry);
- reviewDbIndex++;
- }
-
- // The order of the entries is not perfect as ReviewDb included milliseconds for timestamps
- // and we cut off everything below seconds due to NoteDb/git. Consequently, we don't have a
- // way to know in this method in which exact order additions/removals within the same second
- // happened. The best we can do is to group all additions within the same second as
- // redundant entries and the removals afterward. To compensate that we possibly group
- // non-redundant additions/removals, we also accept NoteDb audit entries which just occur
- // anywhere as ReviewDb audit entries.
- if (!redundantReviewDbAuditEntries.contains(noteDbAuditEntry)
- && !reviewDbAuditEntries.contains(noteDbAuditEntry)) {
- return false;
- }
- }
-
- if (reviewDbIndex < reviewDbAuditEntries.size()) {
- // Some of the ReviewDb audit log entries aren't matched by NoteDb audit log entries.
- return false;
- }
- }
- return true;
- }
-
- public AccountGroup.Id id() {
- return group().getId();
- }
-
- public AccountGroup.UUID uuid() {
- return group().getGroupUUID();
- }
-
- public abstract Source source();
-
- public abstract AccountGroup group();
-
- public abstract ImmutableSet<AccountGroupMember> members();
-
- public abstract ImmutableSet<AccountGroupMemberAudit> memberAudit();
-
- public abstract ImmutableSet<AccountGroupById> byId();
-
- public abstract ImmutableSet<AccountGroupByIdAud> byIdAudit();
-
- public abstract Builder toBuilder();
-
- public GroupBundle truncateToSecond() {
- AccountGroup newGroup = new AccountGroup(group());
- if (newGroup.getCreatedOn() != null) {
- newGroup.setCreatedOn(TimeUtil.truncateToSecond(newGroup.getCreatedOn()));
- }
- return toBuilder()
- .group(newGroup)
- .memberAudit(
- memberAudit().stream().map(GroupBundle::truncateToSecond).collect(toImmutableSet()))
- .byIdAudit(
- byIdAudit().stream().map(GroupBundle::truncateToSecond).collect(toImmutableSet()))
- .build();
- }
-
- private static AccountGroupMemberAudit truncateToSecond(AccountGroupMemberAudit a) {
- AccountGroupMemberAudit result =
- new AccountGroupMemberAudit(
- new AccountGroupMemberAudit.Key(
- a.getKey().getParentKey(),
- a.getKey().getGroupId(),
- TimeUtil.truncateToSecond(a.getKey().getAddedOn())),
- a.getAddedBy());
- if (a.getRemovedOn() != null) {
- result.removed(a.getRemovedBy(), TimeUtil.truncateToSecond(a.getRemovedOn()));
- }
- return result;
- }
-
- private static AccountGroupByIdAud truncateToSecond(AccountGroupByIdAud a) {
- AccountGroupByIdAud result =
- new AccountGroupByIdAud(
- new AccountGroupByIdAud.Key(
- a.getKey().getParentKey(),
- a.getKey().getIncludeUUID(),
- TimeUtil.truncateToSecond(a.getKey().getAddedOn())),
- a.getAddedBy());
- if (a.getRemovedOn() != null) {
- result.removed(a.getRemovedBy(), TimeUtil.truncateToSecond(a.getRemovedOn()));
- }
- return result;
- }
-
- public InternalGroup toInternalGroup() {
- return InternalGroup.create(
- group(),
- members().stream().map(AccountGroupMember::getAccountId).collect(toImmutableSet()),
- byId().stream().map(AccountGroupById::getIncludeUUID).collect(toImmutableSet()));
- }
-
- @Override
- public int hashCode() {
- throw new UnsupportedOperationException(
- "hashCode is not supported because equals is not supported");
- }
-
- @Override
- public boolean equals(Object o) {
- throw new UnsupportedOperationException("Use GroupBundle.compare(a, b) instead of equals");
- }
-
- @AutoValue
- abstract static class AuditEntry {
- private static AuditEntry create(
- Action action, Account.Id userId, Account.Id memberId, Timestamp timestamp) {
- return new AutoValue_GroupBundle_AuditEntry(
- action, userId, String.valueOf(memberId.get()), timestamp);
- }
-
- private static AuditEntry create(
- Action action, Account.Id userId, AccountGroup.UUID subgroupId, Timestamp timestamp) {
- return new AutoValue_GroupBundle_AuditEntry(action, userId, subgroupId.get(), timestamp);
- }
-
- abstract Action getAction();
-
- abstract Account.Id getUserId();
-
- abstract String getTarget();
-
- abstract Timestamp getTimestamp();
- }
-
- enum Action {
- ADD(1),
- REMOVE(2);
-
- private final int order;
-
- Action(int order) {
- this.order = order;
- }
-
- public int getOrder() {
- return order;
- }
- }
-
- @AutoValue.Builder
- abstract static class Builder {
- abstract Builder source(Source source);
-
- abstract Builder group(AccountGroup group);
-
- abstract Builder members(AccountGroupMember... member);
-
- abstract Builder members(Iterable<AccountGroupMember> member);
-
- abstract Builder memberAudit(AccountGroupMemberAudit... audit);
-
- abstract Builder memberAudit(Iterable<AccountGroupMemberAudit> audit);
-
- abstract Builder byId(AccountGroupById... byId);
-
- abstract Builder byId(Iterable<AccountGroupById> byId);
-
- abstract Builder byIdAudit(AccountGroupByIdAud... audit);
-
- abstract Builder byIdAudit(Iterable<AccountGroupByIdAud> audit);
-
- abstract GroupBundle build();
- }
-}
diff --git a/java/com/google/gerrit/server/schema/GroupRebuilder.java b/java/com/google/gerrit/server/schema/GroupRebuilder.java
deleted file mode 100644
index 0157025a..0000000
--- a/java/com/google/gerrit/server/schema/GroupRebuilder.java
+++ /dev/null
@@ -1,303 +0,0 @@
-// 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.schema;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MultimapBuilder;
-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.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gerrit.server.git.meta.VersionedMetaData.BatchMetaDataUpdate;
-import com.google.gerrit.server.group.db.AuditLogFormatter;
-import com.google.gerrit.server.group.db.GroupConfig;
-import com.google.gerrit.server.group.db.InternalGroupCreation;
-import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gerrit.server.group.db.InternalGroupUpdate.MemberModification;
-import com.google.gerrit.server.group.db.InternalGroupUpdate.SubgroupModification;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Map;
-import java.util.NavigableSet;
-import java.util.Optional;
-import java.util.function.Consumer;
-import java.util.stream.Stream;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-
-/** Helper for rebuilding an entire group's NoteDb refs. */
-class GroupRebuilder {
- private final PersonIdent serverIdent;
- private final AllUsersName allUsers;
- private final AuditLogFormatter auditLogFormatter;
-
- public GroupRebuilder(
- PersonIdent serverIdent, AllUsersName allUsers, AuditLogFormatter auditLogFormatter) {
- this.serverIdent = serverIdent;
- this.allUsers = allUsers;
- this.auditLogFormatter = auditLogFormatter;
- }
-
- public void rebuild(Repository allUsersRepo, GroupBundle bundle, @Nullable BatchRefUpdate bru)
- throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
- AccountGroup group = bundle.group();
- InternalGroupCreation groupCreation =
- InternalGroupCreation.builder()
- .setId(bundle.id())
- .setNameKey(group.getNameKey())
- .setGroupUUID(group.getGroupUUID())
- .build();
- GroupConfig groupConfig = GroupConfig.createForNewGroup(allUsers, allUsersRepo, groupCreation);
- groupConfig.setAllowSaveEmptyName();
-
- InternalGroupUpdate.Builder updateBuilder =
- InternalGroupUpdate.builder()
- .setOwnerGroupUUID(group.getOwnerGroupUUID())
- .setVisibleToAll(group.isVisibleToAll())
- .setUpdatedOn(group.getCreatedOn());
- if (bundle.group().getDescription() != null) {
- updateBuilder.setDescription(group.getDescription());
- }
- groupConfig.setGroupUpdate(updateBuilder.build(), auditLogFormatter);
-
- Map<Key, Collection<Event>> events = toEvents(bundle).asMap();
- PersonIdent nowServerIdent = getServerIdent(events);
-
- MetaDataUpdate md = createMetaDataUpdate(allUsers, allUsersRepo, bru);
-
- // Creation is done by the server (unlike later audit events).
- PersonIdent created = new PersonIdent(nowServerIdent, group.getCreatedOn());
- md.getCommitBuilder().setAuthor(created);
- md.getCommitBuilder().setCommitter(created);
-
- // Rebuild group ref.
- try (BatchMetaDataUpdate batch = groupConfig.openUpdate(md)) {
- batch.write(groupConfig, md.getCommitBuilder());
-
- for (Map.Entry<Key, Collection<Event>> e : events.entrySet()) {
- InternalGroupUpdate.Builder ub = InternalGroupUpdate.builder();
- e.getValue().forEach(event -> event.update().accept(ub));
- ub.setUpdatedOn(e.getKey().when());
- groupConfig.setGroupUpdate(ub.build(), auditLogFormatter);
-
- PersonIdent currServerIdent = new PersonIdent(nowServerIdent, e.getKey().when());
- CommitBuilder cb = new CommitBuilder();
- cb.setAuthor(
- e.getKey()
- .accountId()
- .map(id -> auditLogFormatter.getParsableAuthorIdent(id, currServerIdent))
- .orElse(currServerIdent));
- cb.setCommitter(currServerIdent);
- batch.write(groupConfig, cb);
- }
-
- batch.createRef(groupConfig.getRefName());
- }
- }
-
- private ListMultimap<Key, Event> toEvents(GroupBundle bundle) {
- ListMultimap<Key, Event> result =
- MultimapBuilder.treeKeys(Key.COMPARATOR).arrayListValues(1).build();
- Event e;
-
- for (AccountGroupMemberAudit a : bundle.memberAudit()) {
- checkArgument(
- a.getKey().getGroupId().equals(bundle.id()),
- "key %s does not match group %s",
- a.getKey(),
- bundle.id());
- Account.Id accountId = a.getKey().getParentKey();
- e = event(Type.ADD_MEMBER, a.getAddedBy(), a.getKey().getAddedOn(), addMember(accountId));
- result.put(e.key(), e);
- if (!a.isActive()) {
- e = event(Type.REMOVE_MEMBER, a.getRemovedBy(), a.getRemovedOn(), removeMember(accountId));
- result.put(e.key(), e);
- }
- }
-
- for (AccountGroupByIdAud a : bundle.byIdAudit()) {
- checkArgument(
- a.getKey().getParentKey().equals(bundle.id()),
- "key %s does not match group %s",
- a.getKey(),
- bundle.id());
- AccountGroup.UUID uuid = a.getKey().getIncludeUUID();
- e = event(Type.ADD_GROUP, a.getAddedBy(), a.getKey().getAddedOn(), addGroup(uuid));
- result.put(e.key(), e);
- if (!a.isActive()) {
- e = event(Type.REMOVE_GROUP, a.getRemovedBy(), a.getRemovedOn(), removeGroup(uuid));
- result.put(e.key(), e);
- }
- }
-
- // Due to clock skew, audit events may be in the future relative to this machine. Ensure the
- // fixup event happens after any other events, both for the purposes of sorting Keys correctly
- // and to avoid non-monotonic timestamps in the commit history.
- Timestamp maxTs =
- Stream.concat(result.keySet().stream().map(Key::when), Stream.of(TimeUtil.nowTs()))
- .max(Comparator.naturalOrder())
- .get();
- Timestamp fixupTs = new Timestamp(maxTs.getTime() + 1);
- e = serverEvent(Type.FIXUP, fixupTs, setCurrentMembership(bundle));
- result.put(e.key(), e);
-
- return result;
- }
-
- private PersonIdent getServerIdent(Map<Key, Collection<Event>> events) {
- // Created with MultimapBuilder.treeKeys, so the keySet is navigable.
- Key lastKey = ((NavigableSet<Key>) events.keySet()).last();
- checkState(lastKey.type() == Type.FIXUP);
- return new PersonIdent(
- serverIdent.getName(),
- serverIdent.getEmailAddress(),
- Iterables.getOnlyElement(events.get(lastKey)).when(),
- serverIdent.getTimeZone());
- }
-
- private static MetaDataUpdate createMetaDataUpdate(
- Project.NameKey projectName, Repository repository, @Nullable BatchRefUpdate batchRefUpdate) {
- return new MetaDataUpdate(
- GitReferenceUpdated.DISABLED, projectName, repository, batchRefUpdate);
- }
-
- private static Consumer<InternalGroupUpdate.Builder> addMember(Account.Id toAdd) {
- return b -> {
- MemberModification prev = b.getMemberModification();
- b.setMemberModification(in -> Sets.union(prev.apply(in), ImmutableSet.of(toAdd)));
- };
- }
-
- private static Consumer<InternalGroupUpdate.Builder> removeMember(Account.Id toRemove) {
- return b -> {
- MemberModification prev = b.getMemberModification();
- b.setMemberModification(in -> Sets.difference(prev.apply(in), ImmutableSet.of(toRemove)));
- };
- }
-
- private static Consumer<InternalGroupUpdate.Builder> addGroup(AccountGroup.UUID toAdd) {
- return b -> {
- SubgroupModification prev = b.getSubgroupModification();
- b.setSubgroupModification(in -> Sets.union(prev.apply(in), ImmutableSet.of(toAdd)));
- };
- }
-
- private static Consumer<InternalGroupUpdate.Builder> removeGroup(AccountGroup.UUID toRemove) {
- return b -> {
- SubgroupModification prev = b.getSubgroupModification();
- b.setSubgroupModification(in -> Sets.difference(prev.apply(in), ImmutableSet.of(toRemove)));
- };
- }
-
- private static Consumer<InternalGroupUpdate.Builder> setCurrentMembership(GroupBundle bundle) {
- // Overwrite members and subgroups with the current values. The storage layer will do the
- // set differences to compute the appropriate delta, if any.
- return b ->
- b.setMemberModification(
- in ->
- bundle
- .members()
- .stream()
- .map(AccountGroupMember::getAccountId)
- .collect(toImmutableSet()))
- .setSubgroupModification(
- in ->
- bundle
- .byId()
- .stream()
- .map(AccountGroupById::getIncludeUUID)
- .collect(toImmutableSet()));
- }
-
- private static Event event(
- Type type,
- Account.Id accountId,
- Timestamp when,
- Consumer<InternalGroupUpdate.Builder> update) {
- return new AutoValue_GroupRebuilder_Event(type, Optional.of(accountId), when, update);
- }
-
- private static Event serverEvent(
- Type type, Timestamp when, Consumer<InternalGroupUpdate.Builder> update) {
- return new AutoValue_GroupRebuilder_Event(type, Optional.empty(), when, update);
- }
-
- @AutoValue
- abstract static class Event {
- abstract Type type();
-
- abstract Optional<Account.Id> accountId();
-
- abstract Timestamp when();
-
- abstract Consumer<InternalGroupUpdate.Builder> update();
-
- Key key() {
- return new AutoValue_GroupRebuilder_Key(accountId(), when(), type());
- }
- }
-
- /**
- * Distinct event types.
- *
- * <p>Events at the same time by the same user are batched together by type. The types should
- * correspond to the possible batch operations supported by AuditService.
- */
- enum Type {
- ADD_MEMBER,
- REMOVE_MEMBER,
- ADD_GROUP,
- REMOVE_GROUP,
- FIXUP;
- }
-
- @AutoValue
- abstract static class Key {
- static final Comparator<Key> COMPARATOR =
- Comparator.comparing(Key::when)
- .thenComparing(
- k -> k.accountId().map(Account.Id::get).orElse(null),
- Comparator.nullsFirst(Comparator.naturalOrder()))
- .thenComparing(Key::type);
-
- abstract Optional<Account.Id> accountId();
-
- abstract Timestamp when();
-
- abstract Type type();
- }
-}
diff --git a/javatests/com/google/gerrit/server/schema/GroupBundleTest.java b/javatests/com/google/gerrit/server/schema/GroupBundleTest.java
deleted file mode 100644
index c1de3a3..0000000
--- a/javatests/com/google/gerrit/server/schema/GroupBundleTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-// 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.schema;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.server.schema.GroupBundle.Source;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gerrit.testing.TestTimeUtil;
-import java.sql.Timestamp;
-import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class GroupBundleTest extends GerritBaseTests {
- // This class just contains sanity checks that GroupBundle#compare correctly compares all parts of
- // the bundle. Most other test coverage should come via the slightly more realistic
- // GroupRebuilderTest.
-
- private static final String TIMEZONE_ID = "US/Eastern";
-
- private String systemTimeZoneProperty;
- private TimeZone systemTimeZone;
- private Timestamp ts;
-
- @Before
- public void setUp() {
- systemTimeZoneProperty = System.setProperty("user.timezone", TIMEZONE_ID);
- systemTimeZone = TimeZone.getDefault();
- TimeZone.setDefault(TimeZone.getTimeZone(TIMEZONE_ID));
- TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
- ts = TimeUtil.nowTs();
- }
-
- @After
- public void tearDown() {
- TestTimeUtil.useSystemTime();
- System.setProperty("user.timezone", systemTimeZoneProperty);
- TimeZone.setDefault(systemTimeZone);
- }
-
- @Test
- public void compareNonEqual() throws Exception {
- GroupBundle reviewDbBundle = newBundle().source(Source.REVIEW_DB).build();
- AccountGroup g2 = new AccountGroup(reviewDbBundle.group());
- g2.setDescription("Hello!");
- GroupBundle noteDbBundle = GroupBundle.builder().source(Source.NOTE_DB).group(g2).build();
- assertThat(GroupBundle.compareWithAudits(reviewDbBundle, noteDbBundle))
- .containsExactly(
- "AccountGroups differ\n"
- + ("ReviewDb: AccountGroup{name=group, groupId=1, description=null,"
- + " visibleToAll=false, groupUUID=group-1, ownerGroupUUID=group-1,"
- + " createdOn=2009-09-30 17:00:00.0}\n")
- + ("NoteDb : AccountGroup{name=group, groupId=1, description=Hello!,"
- + " visibleToAll=false, groupUUID=group-1, ownerGroupUUID=group-1,"
- + " createdOn=2009-09-30 17:00:00.0}"),
- "AccountGroupMembers differ\n"
- + "ReviewDb: [AccountGroupMember{key=1000,1}]\n"
- + "NoteDb : []",
- "AccountGroupMemberAudits differ\n"
- + ("ReviewDb: [AccountGroupMemberAudit{key=Key{groupId=1, accountId=1000,"
- + " addedOn=2009-09-30 17:00:00.0}, addedBy=2000, removedBy=null,"
- + " removedOn=null}]\n")
- + "NoteDb : []",
- "AccountGroupByIds differ\n"
- + "ReviewDb: [AccountGroupById{key=1,subgroup}]\n"
- + "NoteDb : []",
- "AccountGroupByIdAudits differ\n"
- + ("ReviewDb: [AccountGroupByIdAud{key=Key{groupId=1, includeUUID=subgroup,"
- + " addedOn=2009-09-30 17:00:00.0}, addedBy=3000, removedBy=null,"
- + " removedOn=null}]\n")
- + "NoteDb : []");
- }
-
- @Test
- public void compareIgnoreAudits() throws Exception {
- GroupBundle reviewDbBundle = newBundle().source(Source.REVIEW_DB).build();
- AccountGroup group = new AccountGroup(reviewDbBundle.group());
-
- AccountGroupMember member =
- new AccountGroupMember(new AccountGroupMember.Key(new Account.Id(1), group.getId()));
- AccountGroupMemberAudit memberAudit =
- new AccountGroupMemberAudit(member, new Account.Id(2), ts);
- AccountGroupById byId =
- new AccountGroupById(
- new AccountGroupById.Key(group.getId(), new AccountGroup.UUID("subgroup-2")));
- AccountGroupByIdAud byIdAudit = new AccountGroupByIdAud(byId, new Account.Id(3), ts);
-
- GroupBundle noteDbBundle =
- newBundle().source(Source.NOTE_DB).memberAudit(memberAudit).byIdAudit(byIdAudit).build();
-
- assertThat(GroupBundle.compareWithAudits(reviewDbBundle, noteDbBundle)).isNotEmpty();
- assertThat(GroupBundle.compareWithoutAudits(reviewDbBundle, noteDbBundle)).isEmpty();
- }
-
- @Test
- public void compareEqual() throws Exception {
- GroupBundle reviewDbBundle = newBundle().source(Source.REVIEW_DB).build();
- GroupBundle noteDbBundle = newBundle().source(Source.NOTE_DB).build();
- assertThat(GroupBundle.compareWithAudits(reviewDbBundle, noteDbBundle)).isEmpty();
- }
-
- private GroupBundle.Builder newBundle() {
- AccountGroup group =
- new AccountGroup(
- new AccountGroup.NameKey("group"),
- new AccountGroup.Id(1),
- new AccountGroup.UUID("group-1"),
- ts);
- AccountGroupMember member =
- new AccountGroupMember(new AccountGroupMember.Key(new Account.Id(1000), group.getId()));
- AccountGroupMemberAudit memberAudit =
- new AccountGroupMemberAudit(member, new Account.Id(2000), ts);
- AccountGroupById byId =
- new AccountGroupById(
- new AccountGroupById.Key(group.getId(), new AccountGroup.UUID("subgroup")));
- AccountGroupByIdAud byIdAudit = new AccountGroupByIdAud(byId, new Account.Id(3000), ts);
- return GroupBundle.builder()
- .group(group)
- .members(member)
- .memberAudit(memberAudit)
- .byId(byId)
- .byIdAudit(byIdAudit);
- }
-}
diff --git a/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java b/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java
deleted file mode 100644
index 6a8a55a..0000000
--- a/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java
+++ /dev/null
@@ -1,747 +0,0 @@
-// 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.schema;
-
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assert_;
-import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.assertThat;
-import static com.google.gerrit.reviewdb.client.RefNames.REFS_GROUPNAMES;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.extensions.common.CommitInfo;
-import com.google.gerrit.git.RefUpdateUtil;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.AllUsersNameProvider;
-import com.google.gerrit.server.group.db.AuditLogFormatter;
-import com.google.gerrit.server.group.db.AuditLogReader;
-import com.google.gerrit.server.group.db.GroupNameNotes;
-import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gerrit.testing.GitTestUtil;
-import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import java.sql.Timestamp;
-import java.util.Optional;
-import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.IntStream;
-import org.eclipse.jgit.lib.BatchRefUpdate;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class GroupRebuilderTest extends GerritBaseTests {
- private static final TimeZone TZ = TimeZone.getTimeZone("America/Los_Angeles");
- private static final String SERVER_ID = "server-id";
- private static final String SERVER_NAME = "Gerrit Server";
- private static final String SERVER_EMAIL = "noreply@gerritcodereview.com";
-
- private AtomicInteger idCounter;
- private AllUsersName allUsersName;
- private Repository repo;
- private GroupRebuilder rebuilder;
- private GroupBundle.Factory bundleFactory;
-
- @Before
- public void setUp() throws Exception {
- TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
- idCounter = new AtomicInteger();
- allUsersName = new AllUsersName(AllUsersNameProvider.DEFAULT);
- repo = new InMemoryRepositoryManager().createRepository(allUsersName);
- rebuilder =
- new GroupRebuilder(
- GroupRebuilderTest.newPersonIdent(),
- allUsersName,
- // Note that the expected name/email values in tests are not necessarily realistic,
- // since they use these trivial name/email functions.
- getAuditLogFormatter());
- bundleFactory = new GroupBundle.Factory(new AuditLogReader(SERVER_ID, allUsersName));
- }
-
- @After
- public void tearDown() {
- TestTimeUtil.useSystemTime();
- }
-
- @Test
- public void minimalGroupFields() throws Exception {
- AccountGroup g = newGroup("a");
- GroupBundle b = builder().group(g).build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(1);
- assertCommit(log.get(0), "Create group", SERVER_NAME, SERVER_EMAIL);
- assertThat(logGroupNames()).isEmpty();
- }
-
- @Test
- public void allGroupFields() throws Exception {
- AccountGroup g = newGroup("a");
- g.setDescription("Description");
- g.setOwnerGroupUUID(new AccountGroup.UUID("owner"));
- g.setVisibleToAll(true);
- GroupBundle b = builder().group(g).build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(1);
- assertServerCommit(log.get(0), "Create group");
- }
-
- @Test
- public void emptyGroupName() throws Exception {
- AccountGroup g = newGroup("");
- GroupBundle b = builder().group(g).build();
-
- rebuilder.rebuild(repo, b, null);
-
- GroupBundle noteDbBundle = reload(g);
- assertMigratedCleanly(noteDbBundle, b);
- assertThat(noteDbBundle.group().getName()).isEmpty();
- }
-
- @Test
- public void nullGroupDescription() throws Exception {
- AccountGroup g = newGroup("a");
- g.setDescription(null);
- assertThat(g.getDescription()).isNull();
- GroupBundle b = builder().group(g).build();
-
- rebuilder.rebuild(repo, b, null);
-
- GroupBundle noteDbBundle = reload(g);
- assertMigratedCleanly(noteDbBundle, b);
- assertThat(noteDbBundle.group().getDescription()).isNull();
- }
-
- @Test
- public void emptyGroupDescription() throws Exception {
- AccountGroup g = newGroup("a");
- g.setDescription("");
- assertThat(g.getDescription()).isEmpty();
- GroupBundle b = builder().group(g).build();
-
- rebuilder.rebuild(repo, b, null);
-
- GroupBundle noteDbBundle = reload(g);
- assertMigratedCleanly(noteDbBundle, b);
- assertThat(noteDbBundle.group().getDescription()).isNull();
- }
-
- @Test
- public void membersAndSubgroups() throws Exception {
- AccountGroup g = newGroup("a");
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 1), member(g, 2))
- .byId(byId(g, "x"), byId(g, "y"))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(2);
- assertServerCommit(log.get(0), "Create group");
- assertServerCommit(
- log.get(1),
- "Update group\n"
- + "\n"
- + "Add-group: Group x <x>\n"
- + "Add-group: Group y <y>\n"
- + "Add: Account 1 <1@server-id>\n"
- + "Add: Account 2 <2@server-id>");
- }
-
- @Test
- public void memberAudit() throws Exception {
- AccountGroup g = newGroup("a");
- Timestamp t1 = TimeUtil.nowTs();
- Timestamp t2 = TimeUtil.nowTs();
- Timestamp t3 = TimeUtil.nowTs();
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 1))
- .memberAudit(addMember(g, 1, 8, t2), addAndRemoveMember(g, 2, 8, t1, 9, t3))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(4);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(2), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(3), "Update group\n\nRemove: Account 2 <2@server-id>", "Account 9", "9@server-id");
- }
-
- @Test
- public void memberAuditLegacyRemoved() throws Exception {
- AccountGroup g = newGroup("a");
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 2))
- .memberAudit(
- addAndLegacyRemoveMember(g, 1, 8, TimeUtil.nowTs()),
- addMember(g, 2, 8, TimeUtil.nowTs()))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(4);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(2), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(3), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 8", "8@server-id");
- }
-
- @Test
- public void unauditedMembershipsAddedAtEnd() throws Exception {
- AccountGroup g = newGroup("a");
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 1), member(g, 2), member(g, 3))
- .memberAudit(addMember(g, 1, 8, TimeUtil.nowTs()))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(3);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
- assertServerCommit(
- log.get(2), "Update group\n\nAdd: Account 2 <2@server-id>\nAdd: Account 3 <3@server-id>");
- }
-
- @Test
- public void byIdAudit() throws Exception {
- AccountGroup g = newGroup("a");
- Timestamp t1 = TimeUtil.nowTs();
- Timestamp t2 = TimeUtil.nowTs();
- Timestamp t3 = TimeUtil.nowTs();
- GroupBundle b =
- builder()
- .group(g)
- .byId(byId(g, "x"))
- .byIdAudit(addById(g, "x", 8, t2), addAndRemoveById(g, "y", 8, t1, 9, t3))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(4);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(log.get(1), "Update group\n\nAdd-group: Group y <y>", "Account 8", "8@server-id");
- assertCommit(log.get(2), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
- assertCommit(
- log.get(3), "Update group\n\nRemove-group: Group y <y>", "Account 9", "9@server-id");
- }
-
- @Test
- public void unauditedByIdAddedAtEnd() throws Exception {
- AccountGroup g = newGroup("a");
- GroupBundle b =
- builder()
- .group(g)
- .byId(byId(g, "x"), byId(g, "y"), byId(g, "z"))
- .byIdAudit(addById(g, "x", 8, TimeUtil.nowTs()))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(3);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(log.get(1), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
- assertServerCommit(
- log.get(2), "Update group\n\nAdd-group: Group y <y>\nAdd-group: Group z <z>");
- }
-
- @Test
- public void auditsAtSameTimestampBrokenDownByType() throws Exception {
- AccountGroup g = newGroup("a");
- Timestamp ts = TimeUtil.nowTs();
- int user = 8;
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 1), member(g, 2))
- .memberAudit(
- addMember(g, 1, user, ts),
- addMember(g, 2, user, ts),
- addAndRemoveMember(g, 3, user, ts, user, ts))
- .byId(byId(g, "x"), byId(g, "y"))
- .byIdAudit(
- addById(g, "x", user, ts),
- addById(g, "y", user, ts),
- addAndRemoveById(g, "z", user, ts, user, ts))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(5);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1),
- "Update group\n"
- + "\n"
- + "Add: Account 1 <1@server-id>\n"
- + "Add: Account 2 <2@server-id>\n"
- + "Add: Account 3 <3@server-id>",
- "Account 8",
- "8@server-id");
- assertCommit(
- log.get(2), "Update group\n\nRemove: Account 3 <3@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(3),
- "Update group\n"
- + "\n"
- + "Add-group: Group x <x>\n"
- + "Add-group: Group y <y>\n"
- + "Add-group: Group z <z>",
- "Account 8",
- "8@server-id");
- assertCommit(
- log.get(4), "Update group\n\nRemove-group: Group z <z>", "Account 8", "8@server-id");
- }
-
- @Test
- public void auditsAtSameTimestampBrokenDownByUserAndType() throws Exception {
- AccountGroup g = newGroup("a");
- Timestamp ts = TimeUtil.nowTs();
- int user1 = 8;
- int user2 = 9;
-
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 1), member(g, 2), member(g, 3))
- .memberAudit(
- addMember(g, 1, user1, ts), addMember(g, 2, user2, ts), addMember(g, 3, user1, ts))
- .byId(byId(g, "x"), byId(g, "y"), byId(g, "z"))
- .byIdAudit(
- addById(g, "x", user1, ts), addById(g, "y", user2, ts), addById(g, "z", user1, ts))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(5);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1),
- "Update group\n" + "\n" + "Add: Account 1 <1@server-id>\n" + "Add: Account 3 <3@server-id>",
- "Account 8",
- "8@server-id");
- assertCommit(
- log.get(2),
- "Update group\n\nAdd-group: Group x <x>\nAdd-group: Group z <z>",
- "Account 8",
- "8@server-id");
- assertCommit(
- log.get(3), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 9", "9@server-id");
- assertCommit(log.get(4), "Update group\n\nAdd-group: Group y <y>", "Account 9", "9@server-id");
- }
-
- @Test
- public void fixupCommitPostDatesAllAuditEventsEvenIfAuditEventsAreInTheFuture() throws Exception {
- AccountGroup g = newGroup("a");
- IntStream.range(0, 20).forEach(i -> TimeUtil.nowTs());
- Timestamp future = TimeUtil.nowTs();
- TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
-
- GroupBundle b =
- builder()
- .group(g)
- .byId(byId(g, "x"), byId(g, "y"), byId(g, "z"))
- .byIdAudit(addById(g, "x", 8, future))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(3);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(log.get(1), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
- assertServerCommit(
- log.get(2), "Update group\n\nAdd-group: Group y <y>\nAdd-group: Group z <z>");
-
- assertThat(log.stream().map(c -> c.committer.date).collect(toImmutableList()))
- .named("%s", log)
- .isOrdered();
- assertThat(TimeUtil.nowTs()).isLessThan(future);
- }
-
- @Test
- public void redundantMemberAuditsAreIgnored() throws Exception {
- AccountGroup g = newGroup("a");
- Timestamp t1 = TimeUtil.nowTs();
- Timestamp t2 = TimeUtil.nowTs();
- Timestamp t3 = TimeUtil.nowTs();
- Timestamp t4 = TimeUtil.nowTs();
- Timestamp t5 = TimeUtil.nowTs();
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 2))
- .memberAudit(
- addMember(g, 1, 8, t1),
- addMember(g, 1, 8, t1),
- addMember(g, 1, 8, t3),
- addMember(g, 1, 9, t4),
- addAndRemoveMember(g, 1, 8, t2, 9, t5),
- addAndLegacyRemoveMember(g, 2, 9, t3),
- addMember(g, 2, 8, t1),
- addMember(g, 2, 9, t4),
- addMember(g, 1, 8, t5))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(5);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1),
- "Update group\n\nAdd: Account 1 <1@server-id>\nAdd: Account 2 <2@server-id>",
- "Account 8",
- "8@server-id");
- assertCommit(
- log.get(2), "Update group\n\nRemove: Account 2 <2@server-id>", "Account 9", "9@server-id");
- assertCommit(
- log.get(3), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 9", "9@server-id");
- assertCommit(
- log.get(4), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 9", "9@server-id");
- }
-
- @Test
- public void additionsAndRemovalsWithinSameSecondCanBeMigrated() throws Exception {
- TestTimeUtil.resetWithClockStep(1, TimeUnit.MILLISECONDS);
- AccountGroup g = newGroup("a");
- Timestamp t1 = TimeUtil.nowTs();
- Timestamp t2 = TimeUtil.nowTs();
- Timestamp t3 = TimeUtil.nowTs();
- Timestamp t4 = TimeUtil.nowTs();
- Timestamp t5 = TimeUtil.nowTs();
- GroupBundle b =
- builder()
- .group(g)
- .members(member(g, 1))
- .memberAudit(
- addAndLegacyRemoveMember(g, 1, 8, t1),
- addMember(g, 1, 10, t2),
- addAndRemoveMember(g, 1, 8, t3, 9, t4),
- addMember(g, 1, 8, t5))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(6);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(
- log.get(1), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(2), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 8", "8@server-id");
- assertCommit(
- log.get(3), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 10", "10@server-id");
- assertCommit(
- log.get(4), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 9", "9@server-id");
- assertCommit(
- log.get(5), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
- }
-
- @Test
- public void redundantByIdAuditsAreIgnored() throws Exception {
- AccountGroup g = newGroup("a");
- Timestamp t1 = TimeUtil.nowTs();
- Timestamp t2 = TimeUtil.nowTs();
- Timestamp t3 = TimeUtil.nowTs();
- Timestamp t4 = TimeUtil.nowTs();
- Timestamp t5 = TimeUtil.nowTs();
- GroupBundle b =
- builder()
- .group(g)
- .byId()
- .byIdAudit(
- addById(g, "x", 8, t1),
- addById(g, "x", 8, t3),
- addById(g, "x", 9, t4),
- addAndRemoveById(g, "x", 8, t2, 9, t5))
- .build();
-
- rebuilder.rebuild(repo, b, null);
-
- assertMigratedCleanly(reload(g), b);
- ImmutableList<CommitInfo> log = log(g);
- assertThat(log).hasSize(3);
- assertServerCommit(log.get(0), "Create group");
- assertCommit(log.get(1), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
- assertCommit(
- log.get(2), "Update group\n\nRemove-group: Group x <x>", "Account 9", "9@server-id");
- }
-
- @Test
- public void combineWithBatchGroupNameNotes() throws Exception {
- AccountGroup g1 = newGroup("a");
- AccountGroup g2 = newGroup("b");
- GroupReference gr1 = new GroupReference(g1.getGroupUUID(), g1.getName());
- GroupReference gr2 = new GroupReference(g2.getGroupUUID(), g2.getName());
-
- GroupBundle b1 = builder().group(g1).build();
- GroupBundle b2 = builder().group(g2).build();
-
- BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
-
- rebuilder.rebuild(repo, b1, bru);
- rebuilder.rebuild(repo, b2, bru);
- try (ObjectInserter inserter = repo.newObjectInserter()) {
- ImmutableList<GroupReference> refs = ImmutableList.of(gr1, gr2);
- GroupNameNotes.updateAllGroups(repo, inserter, bru, refs, newPersonIdent());
- inserter.flush();
- }
-
- assertThat(log(g1)).isEmpty();
- assertThat(log(g2)).isEmpty();
- assertThat(logGroupNames()).isEmpty();
-
- RefUpdateUtil.executeChecked(bru, repo);
-
- assertThat(log(g1)).hasSize(1);
- assertThat(log(g2)).hasSize(1);
- assertThat(logGroupNames()).hasSize(1);
- assertMigratedCleanly(reload(g1), b1);
- assertMigratedCleanly(reload(g2), b2);
-
- assertThat(GroupNameNotes.loadAllGroups(repo)).containsExactly(gr1, gr2);
- }
-
- @Test
- public void groupNamesWithLeadingAndTrailingWhitespace() throws Exception {
- for (String leading : ImmutableList.of("", " ", " ")) {
- for (String trailing : ImmutableList.of("", " ", " ")) {
- AccountGroup g = newGroup(leading + "a" + trailing);
- GroupBundle b = builder().group(g).build();
- rebuilder.rebuild(repo, b, null);
- assertMigratedCleanly(reload(g), b);
- }
- }
- }
-
- @Test
- public void disallowExisting() throws Exception {
- AccountGroup g = newGroup("a");
- GroupBundle b = builder().group(g).build();
-
- rebuilder.rebuild(repo, b, null);
- assertMigratedCleanly(reload(g), b);
- String refName = RefNames.refsGroups(g.getGroupUUID());
- ObjectId oldId = repo.exactRef(refName).getObjectId();
-
- try {
- rebuilder.rebuild(repo, b, null);
- assert_().fail("expected OrmDuplicateKeyException");
- } catch (OrmDuplicateKeyException e) {
- // Expected.
- }
-
- assertThat(repo.exactRef(refName).getObjectId()).isEqualTo(oldId);
- }
-
- private GroupBundle reload(AccountGroup g) throws Exception {
- return bundleFactory.fromNoteDb(allUsersName, repo, g.getGroupUUID());
- }
-
- private void assertMigratedCleanly(GroupBundle noteDbBundle, GroupBundle expectedReviewDbBundle) {
- assertThat(GroupBundle.compareWithAudits(expectedReviewDbBundle, noteDbBundle)).isEmpty();
- }
-
- private AccountGroup newGroup(String name) {
- int id = idCounter.incrementAndGet();
- return new AccountGroup(
- new AccountGroup.NameKey(name),
- new AccountGroup.Id(id),
- new AccountGroup.UUID(name.trim() + "-" + id),
- TimeUtil.nowTs());
- }
-
- private AccountGroupMember member(AccountGroup g, int accountId) {
- return new AccountGroupMember(new AccountGroupMember.Key(new Account.Id(accountId), g.getId()));
- }
-
- private AccountGroupMemberAudit addMember(
- AccountGroup g, int accountId, int adder, Timestamp addedOn) {
- return new AccountGroupMemberAudit(member(g, accountId), new Account.Id(adder), addedOn);
- }
-
- private AccountGroupMemberAudit addAndLegacyRemoveMember(
- AccountGroup g, int accountId, int adder, Timestamp addedOn) {
- AccountGroupMemberAudit a = addMember(g, accountId, adder, addedOn);
- a.removedLegacy();
- return a;
- }
-
- private AccountGroupMemberAudit addAndRemoveMember(
- AccountGroup g,
- int accountId,
- int adder,
- Timestamp addedOn,
- int removedBy,
- Timestamp removedOn) {
- AccountGroupMemberAudit a = addMember(g, accountId, adder, addedOn);
- a.removed(new Account.Id(removedBy), removedOn);
- return a;
- }
-
- private AccountGroupByIdAud addById(
- AccountGroup g, String subgroupUuid, int adder, Timestamp addedOn) {
- return new AccountGroupByIdAud(byId(g, subgroupUuid), new Account.Id(adder), addedOn);
- }
-
- private AccountGroupByIdAud addAndRemoveById(
- AccountGroup g,
- String subgroupUuid,
- int adder,
- Timestamp addedOn,
- int removedBy,
- Timestamp removedOn) {
- AccountGroupByIdAud a = addById(g, subgroupUuid, adder, addedOn);
- a.removed(new Account.Id(removedBy), removedOn);
- return a;
- }
-
- private AccountGroupById byId(AccountGroup g, String subgroupUuid) {
- return new AccountGroupById(
- new AccountGroupById.Key(g.getId(), new AccountGroup.UUID(subgroupUuid)));
- }
-
- private ImmutableList<CommitInfo> log(AccountGroup g) throws Exception {
- return GitTestUtil.log(repo, RefNames.refsGroups(g.getGroupUUID()));
- }
-
- private ImmutableList<CommitInfo> logGroupNames() throws Exception {
- return GitTestUtil.log(repo, REFS_GROUPNAMES);
- }
-
- private static GroupBundle.Builder builder() {
- return GroupBundle.builder().source(GroupBundle.Source.REVIEW_DB);
- }
-
- private static PersonIdent newPersonIdent() {
- return new PersonIdent(SERVER_NAME, SERVER_EMAIL, TimeUtil.nowTs(), TZ);
- }
-
- private static void assertServerCommit(CommitInfo commitInfo, String expectedMessage) {
- assertCommit(commitInfo, expectedMessage, SERVER_NAME, SERVER_EMAIL);
- }
-
- private static void assertCommit(
- CommitInfo commitInfo, String expectedMessage, String expectedName, String expectedEmail) {
- assertThat(commitInfo).message().isEqualTo(expectedMessage);
- assertThat(commitInfo).author().name().isEqualTo(expectedName);
- assertThat(commitInfo).author().email().isEqualTo(expectedEmail);
-
- // Committer should always be the server, regardless of author.
- assertThat(commitInfo).committer().name().isEqualTo(SERVER_NAME);
- assertThat(commitInfo).committer().email().isEqualTo(SERVER_EMAIL);
- assertThat(commitInfo).committer().date().isEqualTo(commitInfo.author.date);
- assertThat(commitInfo).committer().tz().isEqualTo(commitInfo.author.tz);
- }
-
- private static AuditLogFormatter getAuditLogFormatter() {
- return AuditLogFormatter.create(
- GroupRebuilderTest::getAccount, GroupRebuilderTest::getGroup, SERVER_ID);
- }
-
- private static Optional<Account> getAccount(Account.Id id) {
- Account account = new Account(id, TimeUtil.nowTs());
- account.setFullName("Account " + id);
- return Optional.of(account);
- }
-
- private static Optional<GroupDescription.Basic> getGroup(AccountGroup.UUID uuid) {
- GroupDescription.Basic group =
- new GroupDescription.Basic() {
- @Override
- public AccountGroup.UUID getGroupUUID() {
- return uuid;
- }
-
- @Override
- public String getName() {
- return "Group " + uuid;
- }
-
- @Nullable
- @Override
- public String getEmailAddress() {
- return null;
- }
-
- @Nullable
- @Override
- public String getUrl() {
- return null;
- }
- };
- return Optional.of(group);
- }
-}