Line data Source code
1 : // Copyright (C) 2018 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.group.db; 16 : 17 : import com.google.common.collect.ImmutableSet; 18 : import com.google.common.collect.Sets; 19 : import com.google.common.collect.Streams; 20 : import com.google.gerrit.entities.InternalGroup; 21 : import java.util.Optional; 22 : import java.util.Set; 23 : import java.util.StringJoiner; 24 : import java.util.function.BiFunction; 25 : import java.util.function.Function; 26 : import java.util.stream.Stream; 27 : import org.eclipse.jgit.revwalk.FooterKey; 28 : 29 : /** 30 : * A parsable commit message for a NoteDb commit of a group. 31 : * 32 : * <p>For group creations, it's sufficient to simply call the constructor of this class. For 33 : * updates, {@link #setOriginalGroup(InternalGroup)} has to be called as well. 34 : */ 35 : class GroupConfigCommitMessage { 36 152 : static final FooterKey FOOTER_ADD_MEMBER = new FooterKey("Add"); 37 152 : static final FooterKey FOOTER_REMOVE_MEMBER = new FooterKey("Remove"); 38 152 : static final FooterKey FOOTER_ADD_GROUP = new FooterKey("Add-group"); 39 152 : static final FooterKey FOOTER_REMOVE_GROUP = new FooterKey("Remove-group"); 40 : 41 : private final AuditLogFormatter auditLogFormatter; 42 : private final InternalGroup updatedGroup; 43 152 : private Optional<InternalGroup> originalGroup = Optional.empty(); 44 : 45 152 : GroupConfigCommitMessage(AuditLogFormatter auditLogFormatter, InternalGroup updatedGroup) { 46 152 : this.auditLogFormatter = auditLogFormatter; 47 152 : this.updatedGroup = updatedGroup; 48 152 : } 49 : 50 : public void setOriginalGroup(InternalGroup originalGroup) { 51 152 : this.originalGroup = Optional.of(originalGroup); 52 152 : } 53 : 54 : public String create() { 55 152 : String summaryLine = originalGroup.isPresent() ? "Update group" : "Create group"; 56 : 57 152 : StringJoiner footerJoiner = new StringJoiner("\n", "\n\n", ""); 58 152 : footerJoiner.setEmptyValue(""); 59 152 : Streams.concat( 60 152 : Streams.stream(getFooterForRename()), 61 152 : getFootersForMemberModifications(), 62 152 : getFootersForSubgroupModifications()) 63 152 : .sorted() 64 152 : .forEach(footerJoiner::add); 65 152 : String footer = footerJoiner.toString(); 66 : 67 152 : return summaryLine + footer; 68 : } 69 : 70 : private Optional<String> getFooterForRename() { 71 152 : if (!originalGroup.isPresent()) { 72 152 : return Optional.empty(); 73 : } 74 : 75 152 : String originalName = originalGroup.get().getName(); 76 152 : String newName = updatedGroup.getName(); 77 152 : if (originalName.equals(newName)) { 78 152 : return Optional.empty(); 79 : } 80 4 : return Optional.of("Rename from " + originalName + " to " + newName); 81 : } 82 : 83 : private Stream<String> getFootersForMemberModifications() { 84 152 : return getFooters( 85 : InternalGroup::getMembers, 86 : AuditLogFormatter::getParsableAccount, 87 : FOOTER_ADD_MEMBER, 88 : FOOTER_REMOVE_MEMBER); 89 : } 90 : 91 : private Stream<String> getFootersForSubgroupModifications() { 92 152 : return getFooters( 93 : InternalGroup::getSubgroups, 94 : AuditLogFormatter::getParsableGroup, 95 : FOOTER_ADD_GROUP, 96 : FOOTER_REMOVE_GROUP); 97 : } 98 : 99 : private <T> Stream<String> getFooters( 100 : Function<InternalGroup, Set<T>> getElements, 101 : BiFunction<AuditLogFormatter, T, String> toParsableString, 102 : FooterKey additionFooterKey, 103 : FooterKey removalFooterKey) { 104 152 : Set<T> oldElements = originalGroup.map(getElements).orElseGet(ImmutableSet::of); 105 152 : Set<T> newElements = getElements.apply(updatedGroup); 106 : 107 152 : Function<T, String> toString = element -> toParsableString.apply(auditLogFormatter, element); 108 : 109 152 : Stream<String> removedElements = 110 152 : Sets.difference(oldElements, newElements).stream() 111 152 : .map(toString) 112 152 : .map((removalFooterKey.getName() + ": ")::concat); 113 152 : Stream<String> addedElements = 114 152 : Sets.difference(newElements, oldElements).stream() 115 152 : .map(toString) 116 152 : .map((additionFooterKey.getName() + ": ")::concat); 117 152 : return Stream.concat(removedElements, addedElements); 118 : } 119 : }