blob: 16519debfe2dfdb478054ee739cb275b87db0cbf [file] [log] [blame]
// Copyright (C) 2013 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.googlesource.gerrit.plugins.its.base.util;
import com.google.common.collect.Sets;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.data.ApprovalAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute;
import com.google.gerrit.server.events.ChangeAbandonedEvent;
import com.google.gerrit.server.events.ChangeEvent;
import com.google.gerrit.server.events.ChangeMergedEvent;
import com.google.gerrit.server.events.ChangeRestoredEvent;
import com.google.gerrit.server.events.CommentAddedEvent;
import com.google.gerrit.server.events.DraftPublishedEvent;
import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.PatchSetCreatedEvent;
import com.google.gerrit.server.events.PatchSetEvent;
import com.google.gerrit.server.events.RefUpdatedEvent;
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.its.base.workflow.Property;
import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.lib.ObjectId;
/** Extractor to translate an {@link ChangeEvent} to {@link Property Properties}. */
public class PropertyExtractor {
private IssueExtractor issueExtractor;
private Property.Factory propertyFactory;
private PropertyAttributeExtractor propertyAttributeExtractor;
private final String pluginName;
@Inject
PropertyExtractor(
IssueExtractor issueExtractor,
Property.Factory propertyFactory,
PropertyAttributeExtractor propertyAttributeExtractor,
@PluginName String pluginName) {
this.issueExtractor = issueExtractor;
this.propertyFactory = propertyFactory;
this.propertyAttributeExtractor = propertyAttributeExtractor;
this.pluginName = pluginName;
}
/**
* creates a patch id for change id string and patchset id string.
*
* @param changeId String representation of the patch sets {@code Change.Id@}
* @param patchId String representation of the patch sets {@code Patchset.Id@}
* @return PatchSet.Id for the specified patch set. If the String to int conversion fails for any
* of the parameters, null is returned.
*/
private PatchSet.Id newPatchSetId(String changeId, String patchId) {
try {
return new PatchSet.Id(new Change.Id(Integer.parseInt(changeId)), Integer.parseInt(patchId));
} catch (NumberFormatException e) {
return null;
}
}
private Map<String, Set<String>> extractFrom(PatchSetEvent event, Set<Property> common) {
common.add(propertyFactory.create("event-type", event.type));
ChangeAttribute change = event.change.get();
PatchSetAttribute patchSet = event.patchSet.get();
common.addAll(propertyAttributeExtractor.extractFrom(change));
common.addAll(propertyAttributeExtractor.extractFrom(patchSet));
PatchSet.Id patchSetId =
newPatchSetId(Integer.toString(change.number), Integer.toString(patchSet.number));
return issueExtractor.getIssueIds(change.project, patchSet.revision, patchSetId);
}
private Map<String, Set<String>> extractFrom(ChangeAbandonedEvent event, Set<Property> common) {
common.addAll(propertyAttributeExtractor.extractFrom(event.abandoner.get(), "abandoner"));
common.add(propertyFactory.create("reason", event.reason));
return extractFrom((PatchSetEvent) event, common);
}
private Map<String, Set<String>> extractFrom(ChangeMergedEvent event, Set<Property> common) {
common.addAll(propertyAttributeExtractor.extractFrom(event.submitter.get(), "submitter"));
return extractFrom((PatchSetEvent) event, common);
}
private Map<String, Set<String>> extractFrom(ChangeRestoredEvent event, Set<Property> common) {
common.addAll(propertyAttributeExtractor.extractFrom(event.restorer.get(), "restorer"));
common.add(propertyFactory.create("reason", event.reason));
return extractFrom((PatchSetEvent) event, common);
}
private Map<String, Set<String>> extractFrom(DraftPublishedEvent event, Set<Property> common) {
common.addAll(propertyAttributeExtractor.extractFrom(event.uploader.get(), "uploader"));
return extractFrom((PatchSetEvent) event, common);
}
private Map<String, Set<String>> extractFrom(RefUpdatedEvent event, Set<Property> common) {
common.add(propertyFactory.create("event-type", event.type));
if (event.submitter != null) {
common.addAll(propertyAttributeExtractor.extractFrom(event.submitter.get(), "submitter"));
}
common.addAll(propertyAttributeExtractor.extractFrom(event.refUpdate.get()));
RefUpdateAttribute refUpdateEvent = event.refUpdate.get();
String commitId =
(refUpdateEvent.newRev.equals(ObjectId.zeroId().name())
? refUpdateEvent.oldRev
: refUpdateEvent.newRev);
return issueExtractor.getIssueIds(event.getProjectNameKey().get(), commitId);
}
private Map<String, Set<String>> extractFrom(PatchSetCreatedEvent event, Set<Property> common) {
common.addAll(propertyAttributeExtractor.extractFrom(event.uploader.get(), "uploader"));
return extractFrom((PatchSetEvent) event, common);
}
private Map<String, Set<String>> extractFrom(CommentAddedEvent event, Set<Property> common) {
common.addAll(propertyAttributeExtractor.extractFrom(event.author.get(), "commenter"));
if (event.approvals != null) {
for (ApprovalAttribute approvalAttribute : event.approvals.get()) {
common.addAll(propertyAttributeExtractor.extractFrom(approvalAttribute));
}
}
common.add(propertyFactory.create("comment", event.comment));
return extractFrom((PatchSetEvent) event, common);
}
/**
* A set of property sets extracted from an event.
*
* <p>As events may relate to more that a single issue, and properties sets are should be tied to
* a single issue, returning {@code Set<Property>} is not sufficient, and we need to return {@code
* Set<Set<Property>>}. Using this approach, a PatchSetCreatedEvent for a patch set with commit
* message:
*
* <pre>
* (bug 4711) Fix treatment of special characters in title
*
* This commit mitigates the effects of bug 42, but does not fix them.
*
* Change-Id: I1234567891123456789212345678931234567894
* </pre>
*
* may return both
*
* <pre>
* issue: 4711
* association: subject
* event: patchset-created
* </pre>
*
* and
*
* <pre>
* issue: 42
* association: body
* event: patchset-created
* </pre>
*
* Thereby, sites can choose to cause different actions for different issues associated to the
* same event. So in the above example, a comment "mentioned in change 123" may be added for issue
* 42, and a comment "fixed by change 123” may be added for issue 4711.
*
* @param event The event to extract property sets from.
* @return sets of property sets extracted from the event.
*/
public Set<Set<Property>> extractFrom(Event event) {
Map<String, Set<String>> associations = null;
Set<Set<Property>> ret = Sets.newHashSet();
Set<Property> common = Sets.newHashSet();
common.add(propertyFactory.create("event", event.getClass().getName()));
if (event instanceof ChangeAbandonedEvent) {
associations = extractFrom((ChangeAbandonedEvent) event, common);
} else if (event instanceof ChangeMergedEvent) {
associations = extractFrom((ChangeMergedEvent) event, common);
} else if (event instanceof ChangeRestoredEvent) {
associations = extractFrom((ChangeRestoredEvent) event, common);
} else if (event instanceof CommentAddedEvent) {
associations = extractFrom((CommentAddedEvent) event, common);
} else if (event instanceof DraftPublishedEvent) {
associations = extractFrom((DraftPublishedEvent) event, common);
} else if (event instanceof PatchSetCreatedEvent) {
associations = extractFrom((PatchSetCreatedEvent) event, common);
} else if (event instanceof RefUpdatedEvent) {
associations = extractFrom((RefUpdatedEvent) event, common);
}
if (associations != null) {
for (String issue : associations.keySet()) {
Set<Property> properties = Sets.newHashSet();
Property property = propertyFactory.create("issue", issue);
properties.add(property);
property = propertyFactory.create("its-name", pluginName);
properties.add(property);
for (String occurrence : associations.get(issue)) {
property = propertyFactory.create("association", occurrence);
properties.add(property);
}
properties.addAll(common);
ret.add(properties);
}
}
return ret;
}
}