Send group audit events also when completely on NoteDb
This commit migrates the existing group audit listener from the old GroupMemberAuditListener to the new GroupAuditListener interface. Before this commit, group audit events are dispatched only for ReviewDb updates. After this, they will be dispatched for both ReviewDb and NoteDb updates. To make DbGroupMemberAuditListener easier for review, this commit doesn't rename it. In the child change, it's renamed to DbGroupAuditListener. Change-Id: Ie9ad8ec3af0fb298556de677dcfcc0bf9f479a6c
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.server.audit;
|
||||
|
||||
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||
import com.google.gerrit.server.audit.group.GroupAuditListener;
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
public class AuditModule extends AbstractModule {
|
||||
@@ -22,7 +23,7 @@ public class AuditModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
DynamicSet.setOf(binder(), AuditListener.class);
|
||||
DynamicSet.setOf(binder(), GroupMemberAuditListener.class);
|
||||
DynamicSet.setOf(binder(), GroupAuditListener.class);
|
||||
bind(AuditService.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,16 @@
|
||||
|
||||
package com.google.gerrit.server.audit;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroupById;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroupMember;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.server.audit.group.GroupAuditListener;
|
||||
import com.google.gerrit.server.audit.group.GroupMemberAuditEvent;
|
||||
import com.google.gerrit.server.audit.group.GroupSubgroupAuditEvent;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Collection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -30,14 +32,14 @@ public class AuditService {
|
||||
private static final Logger log = LoggerFactory.getLogger(AuditService.class);
|
||||
|
||||
private final DynamicSet<AuditListener> auditListeners;
|
||||
private final DynamicSet<GroupMemberAuditListener> groupMemberAuditListeners;
|
||||
private final DynamicSet<GroupAuditListener> groupAuditListeners;
|
||||
|
||||
@Inject
|
||||
public AuditService(
|
||||
DynamicSet<AuditListener> auditListeners,
|
||||
DynamicSet<GroupMemberAuditListener> groupMemberAuditListeners) {
|
||||
DynamicSet<GroupAuditListener> groupAuditListeners) {
|
||||
this.auditListeners = auditListeners;
|
||||
this.groupMemberAuditListeners = groupMemberAuditListeners;
|
||||
this.groupAuditListeners = groupAuditListeners;
|
||||
}
|
||||
|
||||
public void dispatch(AuditEvent action) {
|
||||
@@ -46,44 +48,64 @@ public class AuditService {
|
||||
}
|
||||
}
|
||||
|
||||
public void dispatchAddAccountsToGroup(
|
||||
Account.Id actor, Collection<AccountGroupMember> added, Timestamp addedOn) {
|
||||
for (GroupMemberAuditListener auditListener : groupMemberAuditListeners) {
|
||||
public void dispatchAddMembers(
|
||||
Account.Id actor,
|
||||
AccountGroup.UUID updatedGroup,
|
||||
ImmutableSet<Account.Id> addedMembers,
|
||||
Timestamp addedOn) {
|
||||
for (GroupAuditListener auditListener : groupAuditListeners) {
|
||||
try {
|
||||
auditListener.onAddAccountsToGroup(actor, added, addedOn);
|
||||
GroupMemberAuditEvent event =
|
||||
GroupMemberAuditEvent.create(actor, updatedGroup, addedMembers, addedOn);
|
||||
auditListener.onAddMembers(event);
|
||||
} catch (RuntimeException e) {
|
||||
log.error("failed to log add accounts to group event", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dispatchDeleteAccountsFromGroup(
|
||||
Account.Id actor, Collection<AccountGroupMember> removed, Timestamp removedOn) {
|
||||
for (GroupMemberAuditListener auditListener : groupMemberAuditListeners) {
|
||||
public void dispatchDeleteMembers(
|
||||
Account.Id actor,
|
||||
AccountGroup.UUID updatedGroup,
|
||||
ImmutableSet<Account.Id> deletedMembers,
|
||||
Timestamp deletedOn) {
|
||||
for (GroupAuditListener auditListener : groupAuditListeners) {
|
||||
try {
|
||||
auditListener.onDeleteAccountsFromGroup(actor, removed, removedOn);
|
||||
GroupMemberAuditEvent event =
|
||||
GroupMemberAuditEvent.create(actor, updatedGroup, deletedMembers, deletedOn);
|
||||
auditListener.onDeleteMembers(event);
|
||||
} catch (RuntimeException e) {
|
||||
log.error("failed to log delete accounts from group event", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dispatchAddGroupsToGroup(
|
||||
Account.Id actor, Collection<AccountGroupById> added, Timestamp addedOn) {
|
||||
for (GroupMemberAuditListener auditListener : groupMemberAuditListeners) {
|
||||
public void dispatchAddSubgroups(
|
||||
Account.Id actor,
|
||||
AccountGroup.UUID updatedGroup,
|
||||
ImmutableSet<AccountGroup.UUID> addedSubgroups,
|
||||
Timestamp addedOn) {
|
||||
for (GroupAuditListener auditListener : groupAuditListeners) {
|
||||
try {
|
||||
auditListener.onAddGroupsToGroup(actor, added, addedOn);
|
||||
GroupSubgroupAuditEvent event =
|
||||
GroupSubgroupAuditEvent.create(actor, updatedGroup, addedSubgroups, addedOn);
|
||||
auditListener.onAddSubgroups(event);
|
||||
} catch (RuntimeException e) {
|
||||
log.error("failed to log add groups to group event", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dispatchDeleteGroupsFromGroup(
|
||||
Account.Id actor, Collection<AccountGroupById> removed, Timestamp removedOn) {
|
||||
for (GroupMemberAuditListener auditListener : groupMemberAuditListeners) {
|
||||
public void dispatchDeleteSubgroups(
|
||||
Account.Id actor,
|
||||
AccountGroup.UUID updatedGroup,
|
||||
ImmutableSet<AccountGroup.UUID> deletedSubgroups,
|
||||
Timestamp deletedOn) {
|
||||
for (GroupAuditListener auditListener : groupAuditListeners) {
|
||||
try {
|
||||
auditListener.onDeleteGroupsFromGroup(actor, removed, removedOn);
|
||||
GroupSubgroupAuditEvent event =
|
||||
GroupSubgroupAuditEvent.create(actor, updatedGroup, deletedSubgroups, deletedOn);
|
||||
auditListener.onDeleteSubgroups(event);
|
||||
} catch (RuntimeException e) {
|
||||
log.error("failed to log delete groups from group event", e);
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (C) 2014 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.audit;
|
||||
|
||||
import com.google.gerrit.extensions.annotations.ExtensionPoint;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroupById;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroupMember;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Collection;
|
||||
|
||||
@ExtensionPoint
|
||||
public interface GroupMemberAuditListener {
|
||||
|
||||
void onAddAccountsToGroup(
|
||||
Account.Id actor, Collection<AccountGroupMember> added, Timestamp addedOn);
|
||||
|
||||
void onDeleteAccountsFromGroup(
|
||||
Account.Id actor, Collection<AccountGroupMember> removed, Timestamp removedOn);
|
||||
|
||||
void onAddGroupsToGroup(Account.Id actor, Collection<AccountGroupById> added, Timestamp addedOn);
|
||||
|
||||
void onDeleteGroupsFromGroup(
|
||||
Account.Id actor, Collection<AccountGroupById> deleted, Timestamp removedOn);
|
||||
}
|
||||
@@ -14,6 +14,9 @@
|
||||
|
||||
package com.google.gerrit.server.audit.group;
|
||||
|
||||
import com.google.gerrit.extensions.annotations.ExtensionPoint;
|
||||
|
||||
@ExtensionPoint
|
||||
public interface GroupAuditListener {
|
||||
void onAddMembers(GroupMemberAuditEvent groupMemberAuditEvent);
|
||||
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
|
||||
package com.google.gerrit.server.group;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
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;
|
||||
@@ -28,19 +30,22 @@ import com.google.gerrit.server.account.AccountCache;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.account.GroupCache;
|
||||
import com.google.gerrit.server.account.UniversalGroupBackend;
|
||||
import com.google.gerrit.server.audit.GroupMemberAuditListener;
|
||||
import com.google.gerrit.server.audit.group.GroupAuditEvent;
|
||||
import com.google.gerrit.server.audit.group.GroupAuditListener;
|
||||
import com.google.gerrit.server.audit.group.GroupMemberAuditEvent;
|
||||
import com.google.gerrit.server.audit.group.GroupSubgroupAuditEvent;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.ResultSet;
|
||||
import com.google.gwtorm.server.SchemaFactory;
|
||||
import com.google.inject.Inject;
|
||||
import java.sql.Timestamp;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
class DbGroupMemberAuditListener implements GroupMemberAuditListener {
|
||||
class DbGroupMemberAuditListener implements GroupAuditListener {
|
||||
private static final Logger log =
|
||||
org.slf4j.LoggerFactory.getLogger(DbGroupMemberAuditListener.class);
|
||||
|
||||
@@ -62,31 +67,39 @@ class DbGroupMemberAuditListener implements GroupMemberAuditListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddAccountsToGroup(
|
||||
Account.Id me, Collection<AccountGroupMember> added, Timestamp addedOn) {
|
||||
List<AccountGroupMemberAudit> auditInserts = new ArrayList<>();
|
||||
for (AccountGroupMember m : added) {
|
||||
AccountGroupMemberAudit audit = new AccountGroupMemberAudit(m, me, addedOn);
|
||||
auditInserts.add(audit);
|
||||
public void onAddMembers(GroupMemberAuditEvent event) {
|
||||
Optional<InternalGroup> updatedGroup = groupCache.get(event.getUpdatedGroup());
|
||||
if (!updatedGroup.isPresent()) {
|
||||
logFailToLoadUpdatedGroup(event);
|
||||
return;
|
||||
}
|
||||
|
||||
InternalGroup group = updatedGroup.get();
|
||||
try (ReviewDb db = unwrapDb(schema.open())) {
|
||||
db.accountGroupMembersAudit().insert(auditInserts);
|
||||
db.accountGroupMembersAudit().insert(toAccountGroupMemberAudits(event, group.getId()));
|
||||
} catch (OrmException e) {
|
||||
logOrmExceptionForAccounts(
|
||||
"Cannot log add accounts to group event performed by user", me, added, e);
|
||||
logOrmException(
|
||||
"Cannot log add accounts to group event performed by user", event, group.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteAccountsFromGroup(
|
||||
Account.Id me, Collection<AccountGroupMember> removed, Timestamp removedOn) {
|
||||
public void onDeleteMembers(GroupMemberAuditEvent event) {
|
||||
Optional<InternalGroup> updatedGroup = groupCache.get(event.getUpdatedGroup());
|
||||
if (!updatedGroup.isPresent()) {
|
||||
logFailToLoadUpdatedGroup(event);
|
||||
return;
|
||||
}
|
||||
|
||||
InternalGroup group = updatedGroup.get();
|
||||
List<AccountGroupMemberAudit> auditInserts = new ArrayList<>();
|
||||
List<AccountGroupMemberAudit> auditUpdates = new ArrayList<>();
|
||||
try (ReviewDb db = unwrapDb(schema.open())) {
|
||||
for (AccountGroupMember m : removed) {
|
||||
for (Account.Id accountId : event.getModifiedMembers()) {
|
||||
AccountGroupMemberAudit audit = null;
|
||||
for (AccountGroupMemberAudit a :
|
||||
db.accountGroupMembersAudit().byGroupAccount(m.getAccountGroupId(), m.getAccountId())) {
|
||||
ResultSet<AccountGroupMemberAudit> audits =
|
||||
db.accountGroupMembersAudit().byGroupAccount(group.getId(), accountId);
|
||||
for (AccountGroupMemberAudit a : audits) {
|
||||
if (a.isActive()) {
|
||||
audit = a;
|
||||
break;
|
||||
@@ -94,47 +107,61 @@ class DbGroupMemberAuditListener implements GroupMemberAuditListener {
|
||||
}
|
||||
|
||||
if (audit != null) {
|
||||
audit.removed(me, removedOn);
|
||||
audit.removed(event.getActor(), event.getTimestamp());
|
||||
auditUpdates.add(audit);
|
||||
} else {
|
||||
audit = new AccountGroupMemberAudit(m, me, removedOn);
|
||||
audit.removedLegacy();
|
||||
auditInserts.add(audit);
|
||||
continue;
|
||||
}
|
||||
AccountGroupMember.Key key = new AccountGroupMember.Key(accountId, group.getId());
|
||||
audit =
|
||||
new AccountGroupMemberAudit(
|
||||
new AccountGroupMember(key), event.getActor(), event.getTimestamp());
|
||||
audit.removedLegacy();
|
||||
auditInserts.add(audit);
|
||||
}
|
||||
db.accountGroupMembersAudit().update(auditUpdates);
|
||||
db.accountGroupMembersAudit().insert(auditInserts);
|
||||
} catch (OrmException e) {
|
||||
logOrmExceptionForAccounts(
|
||||
"Cannot log delete accounts from group event performed by user", me, removed, e);
|
||||
logOrmException(
|
||||
"Cannot log delete accounts from group event performed by user",
|
||||
event,
|
||||
group.getName(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddGroupsToGroup(
|
||||
Account.Id me, Collection<AccountGroupById> added, Timestamp addedOn) {
|
||||
List<AccountGroupByIdAud> includesAudit = new ArrayList<>();
|
||||
for (AccountGroupById groupInclude : added) {
|
||||
AccountGroupByIdAud audit = new AccountGroupByIdAud(groupInclude, me, addedOn);
|
||||
includesAudit.add(audit);
|
||||
public void onAddSubgroups(GroupSubgroupAuditEvent event) {
|
||||
Optional<InternalGroup> updatedGroup = groupCache.get(event.getUpdatedGroup());
|
||||
if (!updatedGroup.isPresent()) {
|
||||
logFailToLoadUpdatedGroup(event);
|
||||
return;
|
||||
}
|
||||
|
||||
InternalGroup group = updatedGroup.get();
|
||||
try (ReviewDb db = unwrapDb(schema.open())) {
|
||||
db.accountGroupByIdAud().insert(includesAudit);
|
||||
db.accountGroupByIdAud().insert(toAccountGroupByIdAudits(event, group.getId()));
|
||||
} catch (OrmException e) {
|
||||
logOrmExceptionForGroups(
|
||||
"Cannot log add groups to group event performed by user", me, added, e);
|
||||
logOrmException(
|
||||
"Cannot log add groups to group event performed by user", event, group.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteGroupsFromGroup(
|
||||
Account.Id me, Collection<AccountGroupById> removed, Timestamp removedOn) {
|
||||
final List<AccountGroupByIdAud> auditUpdates = new ArrayList<>();
|
||||
public void onDeleteSubgroups(GroupSubgroupAuditEvent event) {
|
||||
Optional<InternalGroup> updatedGroup = groupCache.get(event.getUpdatedGroup());
|
||||
if (!updatedGroup.isPresent()) {
|
||||
logFailToLoadUpdatedGroup(event);
|
||||
return;
|
||||
}
|
||||
|
||||
InternalGroup group = updatedGroup.get();
|
||||
List<AccountGroupByIdAud> auditUpdates = new ArrayList<>();
|
||||
try (ReviewDb db = unwrapDb(schema.open())) {
|
||||
for (AccountGroupById g : removed) {
|
||||
for (AccountGroup.UUID uuid : event.getModifiedSubgroups()) {
|
||||
AccountGroupByIdAud audit = null;
|
||||
for (AccountGroupByIdAud a :
|
||||
db.accountGroupByIdAud().byGroupInclude(g.getGroupId(), g.getIncludeUUID())) {
|
||||
ResultSet<AccountGroupByIdAud> audits =
|
||||
db.accountGroupByIdAud().byGroupInclude(updatedGroup.get().getId(), uuid);
|
||||
for (AccountGroupByIdAud a : audits) {
|
||||
if (a.isActive()) {
|
||||
audit = a;
|
||||
break;
|
||||
@@ -142,66 +169,98 @@ class DbGroupMemberAuditListener implements GroupMemberAuditListener {
|
||||
}
|
||||
|
||||
if (audit != null) {
|
||||
audit.removed(me, removedOn);
|
||||
audit.removed(event.getActor(), event.getTimestamp());
|
||||
auditUpdates.add(audit);
|
||||
}
|
||||
}
|
||||
db.accountGroupByIdAud().update(auditUpdates);
|
||||
} catch (OrmException e) {
|
||||
logOrmExceptionForGroups(
|
||||
"Cannot log delete groups from group event performed by user", me, removed, e);
|
||||
logOrmException(
|
||||
"Cannot log delete groups from group event performed by user", event, group.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logOrmExceptionForAccounts(
|
||||
String header, Account.Id me, Collection<AccountGroupMember> values, OrmException e) {
|
||||
List<String> descriptions = new ArrayList<>();
|
||||
for (AccountGroupMember m : values) {
|
||||
Account.Id accountId = m.getAccountId();
|
||||
String userName = getUserName(accountId).orElse(null);
|
||||
AccountGroup.Id groupId = m.getAccountGroupId();
|
||||
String groupName = getGroupName(groupId);
|
||||
private void logFailToLoadUpdatedGroup(GroupAuditEvent event) {
|
||||
ImmutableList<String> descriptions = createEventDescriptions(event, "(fail to load group)");
|
||||
String message =
|
||||
createErrorMessage("Fail to load the updated group", event.getActor(), descriptions);
|
||||
log.error(message);
|
||||
}
|
||||
|
||||
descriptions.add(
|
||||
MessageFormat.format(
|
||||
"account {0}/{1}, group {2}/{3}", accountId, userName, groupId, groupName));
|
||||
private void logOrmException(
|
||||
String header, GroupAuditEvent event, String updatedGroupName, OrmException e) {
|
||||
ImmutableList<String> descriptions = createEventDescriptions(event, updatedGroupName);
|
||||
String message = createErrorMessage(header, event.getActor(), descriptions);
|
||||
log.error(message, e);
|
||||
}
|
||||
|
||||
private ImmutableList<String> createEventDescriptions(
|
||||
GroupAuditEvent event, String updatedGroupName) {
|
||||
ImmutableList.Builder<String> builder = ImmutableList.builder();
|
||||
if (event instanceof GroupMemberAuditEvent) {
|
||||
GroupMemberAuditEvent memberAuditEvent = (GroupMemberAuditEvent) event;
|
||||
for (Account.Id accountId : memberAuditEvent.getModifiedMembers()) {
|
||||
String userName = getUserName(accountId).orElse("");
|
||||
builder.add(
|
||||
MessageFormat.format(
|
||||
"account {0}/{1}, group {2}/{3}",
|
||||
accountId, userName, event.getUpdatedGroup(), updatedGroupName));
|
||||
}
|
||||
} else if (event instanceof GroupSubgroupAuditEvent) {
|
||||
GroupSubgroupAuditEvent subgroupAuditEvent = (GroupSubgroupAuditEvent) event;
|
||||
for (AccountGroup.UUID groupUuid : subgroupAuditEvent.getModifiedSubgroups()) {
|
||||
String groupName = groupBackend.get(groupUuid).getName();
|
||||
builder.add(
|
||||
MessageFormat.format(
|
||||
"group {0}/{1}, group {2}/{3}",
|
||||
groupUuid, groupName, subgroupAuditEvent.getUpdatedGroup(), updatedGroupName));
|
||||
}
|
||||
}
|
||||
logOrmException(header, me, descriptions, e);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void logOrmExceptionForGroups(
|
||||
String header, Account.Id me, Collection<AccountGroupById> values, OrmException e) {
|
||||
List<String> descriptions = new ArrayList<>();
|
||||
for (AccountGroupById m : values) {
|
||||
AccountGroup.UUID groupUuid = m.getIncludeUUID();
|
||||
String groupName = groupBackend.get(groupUuid).getName();
|
||||
AccountGroup.Id targetGroupId = m.getGroupId();
|
||||
String targetGroupName = getGroupName(targetGroupId);
|
||||
|
||||
descriptions.add(
|
||||
MessageFormat.format(
|
||||
"group {0}/{1}, group {2}/{3}",
|
||||
groupUuid, groupName, targetGroupId, targetGroupName));
|
||||
}
|
||||
logOrmException(header, me, descriptions, e);
|
||||
}
|
||||
|
||||
private String getGroupName(AccountGroup.Id groupId) {
|
||||
return groupCache.get(groupId).map(InternalGroup::getName).orElse("Deleted group " + groupId);
|
||||
}
|
||||
|
||||
private void logOrmException(String header, Account.Id me, Iterable<?> values, OrmException e) {
|
||||
private String createErrorMessage(
|
||||
String header, Account.Id me, ImmutableList<String> descriptions) {
|
||||
StringBuilder message = new StringBuilder(header);
|
||||
message.append(" ");
|
||||
message.append(me);
|
||||
message.append("/");
|
||||
message.append(getUserName(me).orElse(null));
|
||||
message.append(": ");
|
||||
message.append(Joiner.on("; ").join(values));
|
||||
log.error(message.toString(), e);
|
||||
message.append(Joiner.on("; ").join(descriptions));
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
private Optional<String> getUserName(Account.Id accountId) {
|
||||
return accountCache.get(accountId).map(AccountState::getUserName).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
private static ImmutableSet<AccountGroupMemberAudit> toAccountGroupMemberAudits(
|
||||
GroupMemberAuditEvent event, AccountGroup.Id updatedGroupId) {
|
||||
Timestamp timestamp = event.getTimestamp();
|
||||
Account.Id actor = event.getActor();
|
||||
return event
|
||||
.getModifiedMembers()
|
||||
.stream()
|
||||
.map(
|
||||
member ->
|
||||
new AccountGroupMemberAudit(
|
||||
new AccountGroupMemberAudit.Key(member, updatedGroupId, timestamp), actor))
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
private static ImmutableSet<AccountGroupByIdAud> toAccountGroupByIdAudits(
|
||||
GroupSubgroupAuditEvent event, AccountGroup.Id updatedGroupId) {
|
||||
Timestamp timestamp = event.getTimestamp();
|
||||
Account.Id actor = event.getActor();
|
||||
return event
|
||||
.getModifiedSubgroups()
|
||||
.stream()
|
||||
.map(
|
||||
subgroup ->
|
||||
new AccountGroupByIdAud(
|
||||
new AccountGroupByIdAud.Key(updatedGroupId, subgroup, timestamp), actor))
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.google.gerrit.server.group;
|
||||
|
||||
import com.google.gerrit.extensions.config.FactoryModule;
|
||||
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||
import com.google.gerrit.server.audit.GroupMemberAuditListener;
|
||||
import com.google.gerrit.server.audit.group.GroupAuditListener;
|
||||
import com.google.gerrit.server.notedb.GroupsMigration;
|
||||
|
||||
public class Module extends FactoryModule {
|
||||
@@ -18,8 +18,7 @@ public class Module extends FactoryModule {
|
||||
// DbGroupMemberAuditListener is used solely for the ReviewDb audit log. It does not respect
|
||||
// ReviewDb wrappers that disable reads. Hence, we don't want to bind it if ReviewDb is
|
||||
// disabled.
|
||||
DynamicSet.bind(binder(), GroupMemberAuditListener.class)
|
||||
.to(DbGroupMemberAuditListener.class);
|
||||
DynamicSet.bind(binder(), GroupAuditListener.class).to(DbGroupMemberAuditListener.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,12 +210,14 @@ public class GroupsUpdate {
|
||||
|
||||
if (!groupsMigration.writeToNoteDb()) {
|
||||
updateCachesOnGroupCreation(createdGroupInReviewDb);
|
||||
dispatchAuditEventsOnGroupCreation(createdGroupInReviewDb);
|
||||
return createdGroupInReviewDb;
|
||||
}
|
||||
}
|
||||
|
||||
InternalGroup createdGroup = createGroupInNoteDbWithRetry(groupCreation, groupUpdate);
|
||||
updateCachesOnGroupCreation(createdGroup);
|
||||
dispatchAuditEventsOnGroupCreation(createdGroup);
|
||||
return createdGroup;
|
||||
}
|
||||
|
||||
@@ -234,8 +236,17 @@ public class GroupsUpdate {
|
||||
*/
|
||||
public void updateGroup(ReviewDb db, AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
|
||||
throws OrmException, IOException, NoSuchGroupException, ConfigInvalidException {
|
||||
Optional<Timestamp> updatedOn = groupUpdate.getUpdatedOn();
|
||||
if (!updatedOn.isPresent()) {
|
||||
// Set updatedOn to a specific value so that the same timestamp is used for ReviewDb and
|
||||
// NoteDb. This timestamp is also used by audit events.
|
||||
updatedOn = Optional.of(TimeUtil.nowTs());
|
||||
groupUpdate = groupUpdate.toBuilder().setUpdatedOn(updatedOn.get()).build();
|
||||
}
|
||||
|
||||
UpdateResult result = updateGroupInDb(db, groupUuid, groupUpdate);
|
||||
updateCachesOnGroupUpdate(result);
|
||||
dispatchAuditEventsOnGroupUpdate(result, updatedOn.get());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -244,12 +255,6 @@ public class GroupsUpdate {
|
||||
throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
|
||||
UpdateResult reviewDbUpdateResult = null;
|
||||
if (!groupsMigration.disableGroupReviewDb()) {
|
||||
if (!groupUpdate.getUpdatedOn().isPresent()) {
|
||||
// Set updatedOn to a specific value so that the same timestamp is used for ReviewDb and
|
||||
// NoteDb.
|
||||
groupUpdate = groupUpdate.toBuilder().setUpdatedOn(TimeUtil.nowTs()).build();
|
||||
}
|
||||
|
||||
AccountGroup group = getExistingGroupFromReviewDb(ReviewDbUtil.unwrapDb(db), groupUuid);
|
||||
reviewDbUpdateResult = updateGroupInReviewDb(ReviewDbUtil.unwrapDb(db), group, groupUpdate);
|
||||
|
||||
@@ -278,8 +283,8 @@ public class GroupsUpdate {
|
||||
UpdateResult updateResult = updateGroupInReviewDb(db, group, groupUpdate);
|
||||
return InternalGroup.create(
|
||||
group,
|
||||
updateResult.getModifiedMembers(),
|
||||
updateResult.getModifiedSubgroups(),
|
||||
updateResult.getAddedMembers(),
|
||||
updateResult.getAddedSubgroups(),
|
||||
updateResult.getRefState());
|
||||
}
|
||||
|
||||
@@ -315,18 +320,34 @@ public class GroupsUpdate {
|
||||
// The name must be inserted first so that we stop early for already used names.
|
||||
updateNameInReviewDb(db, group.getId(), originalName, updatedName);
|
||||
db.accountGroups().upsert(ImmutableList.of(group));
|
||||
ImmutableSet<Account.Id> modifiedMembers =
|
||||
updateMembersInReviewDb(db, group.getId(), groupUpdate);
|
||||
ImmutableSet<AccountGroup.UUID> modifiedSubgroups =
|
||||
updateSubgroupsInReviewDb(db, group.getId(), groupUpdate);
|
||||
|
||||
ImmutableSet<Account.Id> originalMembers =
|
||||
Groups.getMembersFromReviewDb(db, group.getId()).collect(toImmutableSet());
|
||||
ImmutableSet<Account.Id> updatedMembers =
|
||||
ImmutableSet.copyOf(groupUpdate.getMemberModification().apply(originalMembers));
|
||||
ImmutableSet<AccountGroup.UUID> originalSubgroups =
|
||||
Groups.getSubgroupsFromReviewDb(db, group.getId()).collect(toImmutableSet());
|
||||
ImmutableSet<AccountGroup.UUID> updatedSubgroups =
|
||||
ImmutableSet.copyOf(groupUpdate.getSubgroupModification().apply(originalSubgroups));
|
||||
|
||||
Set<Account.Id> addedMembers =
|
||||
addGroupMembersInReviewDb(db, group.getId(), originalMembers, updatedMembers);
|
||||
Set<Account.Id> deletedMembers =
|
||||
deleteGroupMembersInReviewDb(db, group.getId(), originalMembers, updatedMembers);
|
||||
Set<AccountGroup.UUID> addedSubgroups =
|
||||
addSubgroupsInReviewDb(db, group.getId(), originalSubgroups, updatedSubgroups);
|
||||
Set<AccountGroup.UUID> deletedSubgroups =
|
||||
deleteSubgroupsInReviewDb(db, group.getId(), originalSubgroups, updatedSubgroups);
|
||||
|
||||
UpdateResult.Builder resultBuilder =
|
||||
UpdateResult.builder()
|
||||
.setGroupUuid(group.getGroupUUID())
|
||||
.setGroupId(group.getId())
|
||||
.setGroupName(group.getNameKey())
|
||||
.setModifiedMembers(modifiedMembers)
|
||||
.setModifiedSubgroups(modifiedSubgroups);
|
||||
.setAddedMembers(addedMembers)
|
||||
.setDeletedMembers(deletedMembers)
|
||||
.setAddedSubgroups(addedSubgroups)
|
||||
.setDeletedSubgroups(deletedSubgroups);
|
||||
if (!Objects.equals(originalName, updatedName)) {
|
||||
resultBuilder.setPreviousGroupName(originalName);
|
||||
}
|
||||
@@ -355,118 +376,87 @@ public class GroupsUpdate {
|
||||
db.accountGroupNames().deleteKeys(ImmutableList.of(originalName));
|
||||
}
|
||||
|
||||
private ImmutableSet<Account.Id> updateMembersInReviewDb(
|
||||
ReviewDb db, AccountGroup.Id groupId, InternalGroupUpdate groupUpdate) throws OrmException {
|
||||
Timestamp updatedOn = groupUpdate.getUpdatedOn().orElseGet(TimeUtil::nowTs);
|
||||
ImmutableSet<Account.Id> originalMembers =
|
||||
Groups.getMembersFromReviewDb(db, groupId).collect(toImmutableSet());
|
||||
ImmutableSet<Account.Id> updatedMembers =
|
||||
ImmutableSet.copyOf(groupUpdate.getMemberModification().apply(originalMembers));
|
||||
|
||||
Set<Account.Id> addedMembers = Sets.difference(updatedMembers, originalMembers);
|
||||
if (!addedMembers.isEmpty()) {
|
||||
addGroupMembersInReviewDb(db, groupId, addedMembers, updatedOn);
|
||||
}
|
||||
|
||||
Set<Account.Id> removedMembers = Sets.difference(originalMembers, updatedMembers);
|
||||
if (!removedMembers.isEmpty()) {
|
||||
removeGroupMembersInReviewDb(db, groupId, removedMembers, updatedOn);
|
||||
}
|
||||
|
||||
return Sets.union(addedMembers, removedMembers).immutableCopy();
|
||||
}
|
||||
|
||||
private void addGroupMembersInReviewDb(
|
||||
ReviewDb db, AccountGroup.Id groupId, Set<Account.Id> newMemberIds, Timestamp addedOn)
|
||||
private static Set<Account.Id> addGroupMembersInReviewDb(
|
||||
ReviewDb db,
|
||||
AccountGroup.Id groupId,
|
||||
ImmutableSet<Account.Id> originalMembers,
|
||||
ImmutableSet<Account.Id> updatedMembers)
|
||||
throws OrmException {
|
||||
Set<AccountGroupMember> newMembers =
|
||||
newMemberIds
|
||||
.stream()
|
||||
.map(accountId -> new AccountGroupMember.Key(accountId, groupId))
|
||||
.map(AccountGroupMember::new)
|
||||
.collect(toImmutableSet());
|
||||
|
||||
if (currentUser != null) {
|
||||
auditService.dispatchAddAccountsToGroup(currentUser.getAccountId(), newMembers, addedOn);
|
||||
Set<Account.Id> accountIds = Sets.difference(updatedMembers, originalMembers);
|
||||
if (accountIds.isEmpty()) {
|
||||
return accountIds;
|
||||
}
|
||||
|
||||
ImmutableSet<AccountGroupMember> newMembers = toAccountGroupMembers(groupId, accountIds);
|
||||
db.accountGroupMembers().insert(newMembers);
|
||||
return accountIds;
|
||||
}
|
||||
|
||||
private void removeGroupMembersInReviewDb(
|
||||
ReviewDb db, AccountGroup.Id groupId, Set<Account.Id> accountIds, Timestamp removedOn)
|
||||
private static Set<Account.Id> deleteGroupMembersInReviewDb(
|
||||
ReviewDb db,
|
||||
AccountGroup.Id groupId,
|
||||
ImmutableSet<Account.Id> originalMembers,
|
||||
ImmutableSet<Account.Id> updatedMembers)
|
||||
throws OrmException {
|
||||
Set<AccountGroupMember> membersToRemove =
|
||||
accountIds
|
||||
.stream()
|
||||
.map(accountId -> new AccountGroupMember.Key(accountId, groupId))
|
||||
.map(AccountGroupMember::new)
|
||||
.collect(toImmutableSet());
|
||||
|
||||
if (currentUser != null) {
|
||||
auditService.dispatchDeleteAccountsFromGroup(
|
||||
currentUser.getAccountId(), membersToRemove, removedOn);
|
||||
Set<Account.Id> accountIds = Sets.difference(originalMembers, updatedMembers);
|
||||
if (accountIds.isEmpty()) {
|
||||
return accountIds;
|
||||
}
|
||||
|
||||
ImmutableSet<AccountGroupMember> membersToRemove = toAccountGroupMembers(groupId, accountIds);
|
||||
db.accountGroupMembers().delete(membersToRemove);
|
||||
return accountIds;
|
||||
}
|
||||
|
||||
private ImmutableSet<AccountGroup.UUID> updateSubgroupsInReviewDb(
|
||||
ReviewDb db, AccountGroup.Id groupId, InternalGroupUpdate groupUpdate) throws OrmException {
|
||||
Timestamp updatedOn = groupUpdate.getUpdatedOn().orElseGet(TimeUtil::nowTs);
|
||||
ImmutableSet<AccountGroup.UUID> originalSubgroups =
|
||||
Groups.getSubgroupsFromReviewDb(db, groupId).collect(toImmutableSet());
|
||||
ImmutableSet<AccountGroup.UUID> updatedSubgroups =
|
||||
ImmutableSet.copyOf(groupUpdate.getSubgroupModification().apply(originalSubgroups));
|
||||
|
||||
Set<AccountGroup.UUID> addedSubgroups = Sets.difference(updatedSubgroups, originalSubgroups);
|
||||
if (!addedSubgroups.isEmpty()) {
|
||||
addSubgroupsInReviewDb(db, groupId, addedSubgroups, updatedOn);
|
||||
}
|
||||
|
||||
Set<AccountGroup.UUID> removedSubgroups = Sets.difference(originalSubgroups, updatedSubgroups);
|
||||
if (!removedSubgroups.isEmpty()) {
|
||||
removeSubgroupsInReviewDb(db, groupId, removedSubgroups, updatedOn);
|
||||
}
|
||||
|
||||
return Sets.union(addedSubgroups, removedSubgroups).immutableCopy();
|
||||
private static ImmutableSet<AccountGroupMember> toAccountGroupMembers(
|
||||
AccountGroup.Id groupId, Set<Account.Id> accountIds) {
|
||||
return accountIds
|
||||
.stream()
|
||||
.map(accountId -> new AccountGroupMember.Key(accountId, groupId))
|
||||
.map(AccountGroupMember::new)
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
private void addSubgroupsInReviewDb(
|
||||
private static Set<AccountGroup.UUID> addSubgroupsInReviewDb(
|
||||
ReviewDb db,
|
||||
AccountGroup.Id parentGroupId,
|
||||
Set<AccountGroup.UUID> subgroupUuids,
|
||||
Timestamp addedOn)
|
||||
ImmutableSet<AccountGroup.UUID> originalSubgroups,
|
||||
ImmutableSet<AccountGroup.UUID> updatedSubgroups)
|
||||
throws OrmException {
|
||||
Set<AccountGroupById> newSubgroups =
|
||||
subgroupUuids
|
||||
.stream()
|
||||
.map(subgroupUuid -> new AccountGroupById.Key(parentGroupId, subgroupUuid))
|
||||
.map(AccountGroupById::new)
|
||||
.collect(toImmutableSet());
|
||||
|
||||
if (currentUser != null) {
|
||||
auditService.dispatchAddGroupsToGroup(currentUser.getAccountId(), newSubgroups, addedOn);
|
||||
Set<AccountGroup.UUID> subgroupUuids = Sets.difference(updatedSubgroups, originalSubgroups);
|
||||
if (subgroupUuids.isEmpty()) {
|
||||
return subgroupUuids;
|
||||
}
|
||||
|
||||
ImmutableSet<AccountGroupById> newSubgroups = toAccountGroupByIds(parentGroupId, subgroupUuids);
|
||||
db.accountGroupById().insert(newSubgroups);
|
||||
return subgroupUuids;
|
||||
}
|
||||
|
||||
private void removeSubgroupsInReviewDb(
|
||||
private static Set<AccountGroup.UUID> deleteSubgroupsInReviewDb(
|
||||
ReviewDb db,
|
||||
AccountGroup.Id parentGroupId,
|
||||
Set<AccountGroup.UUID> subgroupUuids,
|
||||
Timestamp removedOn)
|
||||
ImmutableSet<AccountGroup.UUID> originalSubgroups,
|
||||
ImmutableSet<AccountGroup.UUID> updatedSubgroups)
|
||||
throws OrmException {
|
||||
Set<AccountGroupById> subgroupsToRemove =
|
||||
subgroupUuids
|
||||
.stream()
|
||||
.map(subgroupUuid -> new AccountGroupById.Key(parentGroupId, subgroupUuid))
|
||||
.map(AccountGroupById::new)
|
||||
.collect(toImmutableSet());
|
||||
|
||||
if (currentUser != null) {
|
||||
auditService.dispatchDeleteGroupsFromGroup(
|
||||
currentUser.getAccountId(), subgroupsToRemove, removedOn);
|
||||
Set<AccountGroup.UUID> subgroupUuids = Sets.difference(originalSubgroups, updatedSubgroups);
|
||||
if (subgroupUuids.isEmpty()) {
|
||||
return subgroupUuids;
|
||||
}
|
||||
|
||||
ImmutableSet<AccountGroupById> subgroupsToRemove =
|
||||
toAccountGroupByIds(parentGroupId, subgroupUuids);
|
||||
db.accountGroupById().delete(subgroupsToRemove);
|
||||
return subgroupUuids;
|
||||
}
|
||||
|
||||
private static ImmutableSet<AccountGroupById> toAccountGroupByIds(
|
||||
AccountGroup.Id parentGroupId, Set<AccountGroup.UUID> subgroupUuids) {
|
||||
return subgroupUuids
|
||||
.stream()
|
||||
.map(subgroupUuid -> new AccountGroupById.Key(parentGroupId, subgroupUuid))
|
||||
.map(AccountGroupById::new)
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
private InternalGroup createGroupInNoteDbWithRetry(
|
||||
@@ -538,7 +528,6 @@ public class GroupsUpdate {
|
||||
}
|
||||
|
||||
InternalGroup originalGroup = groupConfig.getLoadedGroup().get();
|
||||
|
||||
GroupNameNotes groupNameNotes = null;
|
||||
if (groupUpdate.getName().isPresent()) {
|
||||
AccountGroup.NameKey oldName = originalGroup.getNameKey();
|
||||
@@ -559,18 +548,24 @@ public class GroupsUpdate {
|
||||
|
||||
private static UpdateResult getUpdateResult(
|
||||
InternalGroup originalGroup, InternalGroup updatedGroup) {
|
||||
Set<Account.Id> modifiedMembers =
|
||||
Sets.symmetricDifference(originalGroup.getMembers(), updatedGroup.getMembers());
|
||||
Set<AccountGroup.UUID> modifiedSubgroups =
|
||||
Sets.symmetricDifference(originalGroup.getSubgroups(), updatedGroup.getSubgroups());
|
||||
Set<Account.Id> addedMembers =
|
||||
Sets.difference(updatedGroup.getMembers(), originalGroup.getMembers());
|
||||
Set<Account.Id> deletedMembers =
|
||||
Sets.difference(originalGroup.getMembers(), updatedGroup.getMembers());
|
||||
Set<AccountGroup.UUID> addedSubgroups =
|
||||
Sets.difference(updatedGroup.getSubgroups(), originalGroup.getSubgroups());
|
||||
Set<AccountGroup.UUID> deletedSubgroups =
|
||||
Sets.difference(originalGroup.getSubgroups(), updatedGroup.getSubgroups());
|
||||
|
||||
UpdateResult.Builder resultBuilder =
|
||||
UpdateResult.builder()
|
||||
.setGroupUuid(updatedGroup.getGroupUUID())
|
||||
.setGroupId(updatedGroup.getId())
|
||||
.setGroupName(updatedGroup.getNameKey())
|
||||
.setModifiedMembers(modifiedMembers)
|
||||
.setModifiedSubgroups(modifiedSubgroups)
|
||||
.setAddedMembers(addedMembers)
|
||||
.setDeletedMembers(deletedMembers)
|
||||
.setAddedSubgroups(addedSubgroups)
|
||||
.setDeletedSubgroups(deletedSubgroups)
|
||||
.setRefState(updatedGroup.getRefState());
|
||||
if (!Objects.equals(originalGroup.getNameKey(), updatedGroup.getNameKey())) {
|
||||
resultBuilder.setPreviousGroupName(originalGroup.getNameKey());
|
||||
@@ -626,12 +621,11 @@ public class GroupsUpdate {
|
||||
.start(0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
groupCache.evict(result.getGroupUuid(), result.getGroupId(), result.getGroupName());
|
||||
for (Account.Id modifiedMember : result.getModifiedMembers()) {
|
||||
groupIncludeCache.evictGroupsWithMember(modifiedMember);
|
||||
}
|
||||
for (AccountGroup.UUID modifiedSubgroup : result.getModifiedSubgroups()) {
|
||||
groupIncludeCache.evictParentGroupsOf(modifiedSubgroup);
|
||||
}
|
||||
|
||||
result.getAddedMembers().forEach(groupIncludeCache::evictGroupsWithMember);
|
||||
result.getDeletedMembers().forEach(groupIncludeCache::evictGroupsWithMember);
|
||||
result.getAddedSubgroups().forEach(groupIncludeCache::evictParentGroupsOf);
|
||||
result.getDeletedSubgroups().forEach(groupIncludeCache::evictParentGroupsOf);
|
||||
}
|
||||
|
||||
private void checkIfReviewDbUpdatesAreBlocked() throws OrmException {
|
||||
@@ -640,6 +634,53 @@ public class GroupsUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchAuditEventsOnGroupCreation(InternalGroup createdGroup) {
|
||||
if (currentUser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!createdGroup.getMembers().isEmpty()) {
|
||||
auditService.dispatchAddMembers(
|
||||
currentUser.getAccountId(),
|
||||
createdGroup.getGroupUUID(),
|
||||
createdGroup.getMembers(),
|
||||
createdGroup.getCreatedOn());
|
||||
}
|
||||
if (!createdGroup.getSubgroups().isEmpty()) {
|
||||
auditService.dispatchAddSubgroups(
|
||||
currentUser.getAccountId(),
|
||||
createdGroup.getGroupUUID(),
|
||||
createdGroup.getSubgroups(),
|
||||
createdGroup.getCreatedOn());
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchAuditEventsOnGroupUpdate(UpdateResult result, Timestamp updatedOn) {
|
||||
if (currentUser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.getAddedMembers().isEmpty()) {
|
||||
auditService.dispatchAddMembers(
|
||||
currentUser.getAccountId(), result.getGroupUuid(), result.getAddedMembers(), updatedOn);
|
||||
}
|
||||
if (!result.getDeletedMembers().isEmpty()) {
|
||||
auditService.dispatchDeleteMembers(
|
||||
currentUser.getAccountId(), result.getGroupUuid(), result.getDeletedMembers(), updatedOn);
|
||||
}
|
||||
if (!result.getAddedSubgroups().isEmpty()) {
|
||||
auditService.dispatchAddSubgroups(
|
||||
currentUser.getAccountId(), result.getGroupUuid(), result.getAddedSubgroups(), updatedOn);
|
||||
}
|
||||
if (!result.getDeletedSubgroups().isEmpty()) {
|
||||
auditService.dispatchDeleteSubgroups(
|
||||
currentUser.getAccountId(),
|
||||
result.getGroupUuid(),
|
||||
result.getDeletedSubgroups(),
|
||||
updatedOn);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface MetaDataUpdateFactory {
|
||||
MetaDataUpdate create(
|
||||
@@ -657,9 +698,13 @@ public class GroupsUpdate {
|
||||
|
||||
abstract Optional<AccountGroup.NameKey> getPreviousGroupName();
|
||||
|
||||
abstract ImmutableSet<Account.Id> getModifiedMembers();
|
||||
abstract ImmutableSet<Account.Id> getAddedMembers();
|
||||
|
||||
abstract ImmutableSet<AccountGroup.UUID> getModifiedSubgroups();
|
||||
abstract ImmutableSet<Account.Id> getDeletedMembers();
|
||||
|
||||
abstract ImmutableSet<AccountGroup.UUID> getAddedSubgroups();
|
||||
|
||||
abstract ImmutableSet<AccountGroup.UUID> getDeletedSubgroups();
|
||||
|
||||
@Nullable
|
||||
public abstract ObjectId getRefState();
|
||||
@@ -678,9 +723,13 @@ public class GroupsUpdate {
|
||||
|
||||
abstract Builder setPreviousGroupName(AccountGroup.NameKey previousName);
|
||||
|
||||
abstract Builder setModifiedMembers(Set<Account.Id> modifiedMembers);
|
||||
abstract Builder setAddedMembers(Set<Account.Id> addedMembers);
|
||||
|
||||
abstract Builder setModifiedSubgroups(Set<AccountGroup.UUID> modifiedSubgroups);
|
||||
abstract Builder setDeletedMembers(Set<Account.Id> deletedMembers);
|
||||
|
||||
abstract Builder setAddedSubgroups(Set<AccountGroup.UUID> addedSubgroups);
|
||||
|
||||
abstract Builder setDeletedSubgroups(Set<AccountGroup.UUID> deletedSubgroups);
|
||||
|
||||
public abstract Builder setRefState(ObjectId refState);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user