Create a version in the ITS based on an event property value
create-version-from-property is a new type of action named ProjectAction.
This type of actions are triggered on the same events but don't require a matching issue
to be executed.
A ProjectAction requires an association to be set between a Gerrit project and
its ITS counterpart in order to know on which ITS project the action
must be performed.
create-version-from-property can be used to create a version in the ITS project
when a Tag is created in the Gerrit project.
Change-Id: Ifcd75e1665df8090ce8e458afc192275265a61c3
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/ItsHookModule.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/ItsHookModule.java
index de80229..6c68777 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/ItsHookModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/ItsHookModule.java
@@ -35,6 +35,7 @@
import com.googlesource.gerrit.plugins.its.base.workflow.AddSoyComment;
import com.googlesource.gerrit.plugins.its.base.workflow.AddStandardComment;
import com.googlesource.gerrit.plugins.its.base.workflow.Condition;
+import com.googlesource.gerrit.plugins.its.base.workflow.CreateVersionFromProperty;
import com.googlesource.gerrit.plugins.its.base.workflow.ItsRulesProjectCacheImpl;
import com.googlesource.gerrit.plugins.its.base.workflow.LogEvent;
import com.googlesource.gerrit.plugins.its.base.workflow.Rule;
@@ -70,6 +71,7 @@
factory(AddComment.Factory.class);
factory(AddSoyComment.Factory.class);
factory(AddStandardComment.Factory.class);
+ factory(CreateVersionFromProperty.Factory.class);
factory(LogEvent.Factory.class);
factory(AddPropertyToField.Factory.class);
install(ItsRulesProjectCacheImpl.module());
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfig.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfig.java
index fe25bb7..a004f6f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfig.java
@@ -141,6 +141,16 @@
return RefPatternMatcher.getMatcher(refPattern).match(refName, null);
}
+ // Project association
+ public Optional<String> getItsProjectName(Project.NameKey projectNK) {
+ ProjectState projectState = projectCache.get(projectNK);
+ if (projectState == null) {
+ return Optional.empty();
+ }
+ return Optional.ofNullable(
+ pluginCfgFactory.getFromProjectConfig(projectState, pluginName).getString("its-project"));
+ }
+
// Issue association --------------------------------------------------------
/**
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacade.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacade.java
index 4cd7bc2..db2857b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacade.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacade.java
@@ -38,6 +38,10 @@
public void performAction(String issueId, String actionName) throws IOException;
+ default void createVersion(String itsProject, String version) throws IOException {
+ throw new UnsupportedOperationException("create-version is not implemented by " + getClass());
+ }
+
public boolean exists(final String issueId) throws IOException;
public String createLinkForWebui(String url, String text);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/NoopItsFacade.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/NoopItsFacade.java
index 85d806e..ecf3a6f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/NoopItsFacade.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/NoopItsFacade.java
@@ -62,6 +62,13 @@
}
@Override
+ public void createVersion(String itsProject, String version) {
+ if (log.isDebugEnabled()) {
+ log.debug("createVersion({},{})", itsProject, version);
+ }
+ }
+
+ @Override
public String healthCheck(Check check) throws IOException {
if (log.isDebugEnabled()) {
log.debug("healthCheck()");
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/util/ItsProjectExtractor.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/util/ItsProjectExtractor.java
new file mode 100644
index 0000000..e872f8d
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/util/ItsProjectExtractor.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.base.util;
+
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.its.base.its.ItsConfig;
+import java.util.Optional;
+
+public class ItsProjectExtractor {
+
+ private final ItsConfig itsConfig;
+
+ @Inject
+ ItsProjectExtractor(ItsConfig itsConfig) {
+ this.itsConfig = itsConfig;
+ }
+
+ public Optional<String> getItsProject(String gerritProjectName) {
+ return itsConfig.getItsProjectName(new Project.NameKey(gerritProjectName));
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractor.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractor.java
index 5fde1ab..a59de25 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractor.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractor.java
@@ -31,6 +31,7 @@
import com.google.gerrit.server.events.RefEvent;
import com.google.gerrit.server.events.RefUpdatedEvent;
import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.its.base.workflow.RefEventProperties;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -41,16 +42,19 @@
/** Extractor to translate an {@link ChangeEvent} to a map of properties}. */
public class PropertyExtractor {
- private IssueExtractor issueExtractor;
- private PropertyAttributeExtractor propertyAttributeExtractor;
+ private final ItsProjectExtractor itsProjectExtractor;
+ private final IssueExtractor issueExtractor;
+ private final PropertyAttributeExtractor propertyAttributeExtractor;
private final String pluginName;
@Inject
PropertyExtractor(
IssueExtractor issueExtractor,
+ ItsProjectExtractor itsProjectExtractor,
PropertyAttributeExtractor propertyAttributeExtractor,
@PluginName String pluginName) {
this.issueExtractor = issueExtractor;
+ this.itsProjectExtractor = itsProjectExtractor;
this.propertyAttributeExtractor = propertyAttributeExtractor;
this.pluginName = pluginName;
}
@@ -167,12 +171,17 @@
* @param event The event to extract property maps from.
* @return set of property maps extracted from the event.
*/
- public Set<Map<String, String>> extractFrom(RefEvent event) {
+ public RefEventProperties extractFrom(RefEvent event) {
Map<String, Set<String>> associations = null;
Map<String, String> common = new HashMap<>();
common.put("event", event.getClass().getName());
+ String project = event.getProjectNameKey().get();
common.put("event-type", event.type);
- common.put("project", event.getProjectNameKey().get());
+ common.put("project", project);
+
+ itsProjectExtractor
+ .getItsProject(project)
+ .ifPresent(itsProject -> common.put("its-project", itsProject));
common.put("ref", event.getRefName());
common.put("itsName", pluginName);
@@ -200,6 +209,6 @@
ret.add(properties);
}
}
- return ret;
+ return new RefEventProperties(common, ret);
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Action.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Action.java
index 0f46ce6..338ad81 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Action.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Action.java
@@ -21,15 +21,18 @@
/** Interface for actions on an issue tracking system */
interface Action {
+ /** @return The type of this action */
+ ActionType getType();
+
/**
* Execute this action.
*
* @param its The facade interface to execute actions.
- * @param issue The issue to execute on.
+ * @param target The target to execute on. Its kind will depend on the action type.
* @param actionRequest The request to execute.
* @param properties The properties for the execution.
*/
void execute(
- ItsFacade its, String issue, ActionRequest actionRequest, Map<String, String> properties)
+ ItsFacade its, String target, ActionRequest actionRequest, Map<String, String> properties)
throws IOException;
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionController.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionController.java
index 3329610..4af8f1b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionController.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionController.java
@@ -23,6 +23,8 @@
import java.util.Collection;
import java.util.Map;
import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Controller that takes actions according to {@code ChangeEvents@}.
@@ -31,6 +33,9 @@
* issue's status).
*/
public class ActionController implements EventListener {
+
+ private static final Logger log = LoggerFactory.getLogger(ActionController.class);
+
private final PropertyExtractor propertyExtractor;
private final RuleBase ruleBase;
private final ActionExecutor actionExecutor;
@@ -59,12 +64,39 @@
}
private void handleEvent(RefEvent refEvent) {
- Set<Map<String, String>> properties = propertyExtractor.extractFrom(refEvent);
- for (Map<String, String> propertiesMap : properties) {
- Collection<ActionRequest> actions = ruleBase.actionRequestsFor(propertiesMap);
+ RefEventProperties refEventProperties = propertyExtractor.extractFrom(refEvent);
+
+ handleIssuesEvent(refEventProperties.getIssuesProperties());
+ handleProjectEvent(refEventProperties.getProjectProperties());
+ }
+
+ private void handleIssuesEvent(Set<Map<String, String>> issuesProperties) {
+ for (Map<String, String> issueProperties : issuesProperties) {
+ Collection<ActionRequest> actions = ruleBase.actionRequestsFor(issueProperties);
if (!actions.isEmpty()) {
- actionExecutor.execute(actions, propertiesMap);
+ actionExecutor.executeOnIssue(actions, issueProperties);
}
}
}
+
+ private void handleProjectEvent(Map<String, String> projectProperties) {
+ if (projectProperties.isEmpty()) {
+ return;
+ }
+
+ Collection<ActionRequest> projectActions = ruleBase.actionRequestsFor(projectProperties);
+ if (projectActions.isEmpty()) {
+ return;
+ }
+ if (!projectProperties.containsKey("its-project")) {
+ String project = projectProperties.get("project");
+ log.error(
+ "Could not process project event. No its-project associated with project {}. "
+ + "Did you forget to configure the ITS project association in project.config?",
+ project);
+ return;
+ }
+
+ actionExecutor.executeOnProject(projectActions, projectProperties);
+ }
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutor.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutor.java
index e209daf..bd42552 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutor.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutor.java
@@ -33,6 +33,7 @@
private final AddSoyComment.Factory addSoyCommentFactory;
private final LogEvent.Factory logEventFactory;
private final AddPropertyToField.Factory addPropertyToFieldFactory;
+ private final CreateVersionFromProperty.Factory createVersionFromPropertyFactory;
@Inject
public ActionExecutor(
@@ -41,13 +42,15 @@
AddStandardComment.Factory addStandardCommentFactory,
AddSoyComment.Factory addSoyCommentFactory,
LogEvent.Factory logEventFactory,
- AddPropertyToField.Factory addPropertyToFieldFactory) {
+ AddPropertyToField.Factory addPropertyToFieldFactory,
+ CreateVersionFromProperty.Factory createVersionFromPropertyFactory) {
this.itsFactory = itsFactory;
this.addCommentFactory = addCommentFactory;
this.addStandardCommentFactory = addStandardCommentFactory;
this.addSoyCommentFactory = addSoyCommentFactory;
this.logEventFactory = logEventFactory;
this.addPropertyToFieldFactory = addPropertyToFieldFactory;
+ this.createVersionFromPropertyFactory = createVersionFromPropertyFactory;
}
private Action getAction(String actionName) {
@@ -62,18 +65,21 @@
return logEventFactory.create();
case "add-property-to-field":
return addPropertyToFieldFactory.create();
+ case "create-version-from-property":
+ return createVersionFromPropertyFactory.create();
default:
return null;
}
}
- private void execute(String issue, ActionRequest actionRequest, Map<String, String> properties) {
+ private void executeOnIssue(
+ String issue, ActionRequest actionRequest, Map<String, String> properties) {
ItsFacade its = itsFactory.getFacade(new Project.NameKey(properties.get("project")));
try {
Action action = getAction(actionRequest.getName());
if (action == null) {
its.performAction(issue, actionRequest.getUnparsed());
- } else {
+ } else if (action.getType() == ActionType.ISSUE) {
action.execute(its, issue, actionRequest, properties);
}
} catch (IOException e) {
@@ -81,9 +87,34 @@
}
}
- public void execute(Iterable<ActionRequest> actions, Map<String, String> properties) {
+ public void executeOnIssue(Iterable<ActionRequest> actions, Map<String, String> properties) {
for (ActionRequest actionRequest : actions) {
- execute(properties.get("issue"), actionRequest, properties);
+ executeOnIssue(properties.get("issue"), actionRequest, properties);
+ }
+ }
+
+ private void executeOnProject(
+ String itsProject, ActionRequest actionRequest, Map<String, String> properties) {
+ try {
+ String actionName = actionRequest.getName();
+ Action action = getAction(actionName);
+ if (action == null) {
+ log.debug("No action found for name {}", actionName);
+ return;
+ }
+ if (action.getType() != ActionType.PROJECT) {
+ return;
+ }
+ ItsFacade its = itsFactory.getFacade(new Project.NameKey(properties.get("project")));
+ action.execute(its, itsProject, actionRequest, properties);
+ } catch (IOException e) {
+ log.error("Error while executing action " + actionRequest, e);
+ }
+ }
+
+ public void executeOnProject(Iterable<ActionRequest> actions, Map<String, String> properties) {
+ for (ActionRequest actionRequest : actions) {
+ executeOnProject(properties.get("its-project"), actionRequest, properties);
}
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionType.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionType.java
new file mode 100644
index 0000000..8d18cce
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionType.java
@@ -0,0 +1,22 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.base.workflow;
+
+public enum ActionType {
+ /** Actions that will be executed on ITS issues */
+ ISSUE,
+ /** Actions that will be executed on ITS projects */
+ PROJECT
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddComment.java
index 82d1701..2624613 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddComment.java
@@ -24,7 +24,7 @@
*
* <p>The action requests parameters get concatenated and get added to the issue.
*/
-public class AddComment implements Action {
+public class AddComment extends IssueAction {
public interface Factory {
AddComment create();
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToField.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToField.java
index 1979183..c19aa73 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToField.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddPropertyToField.java
@@ -20,7 +20,7 @@
import java.util.Map;
import java.util.Optional;
-public class AddPropertyToField implements Action {
+public class AddPropertyToField extends IssueAction {
public interface Factory {
AddPropertyToField create();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddSoyComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddSoyComment.java
index 035a6da..c29d622 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddSoyComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddSoyComment.java
@@ -39,7 +39,7 @@
*
* <p>Comments are added for merging, abandoning, restoring of changes and adding of patch sets.
*/
-public class AddSoyComment implements Action {
+public class AddSoyComment extends IssueAction {
private static final Logger log = LoggerFactory.getLogger(AddSoyComment.class);
public interface Factory {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardComment.java
index c2b95a6..d289024 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardComment.java
@@ -24,7 +24,7 @@
*
* <p>Comments are added for merging, abandoning, restoring of changes and adding of patch sets.
*/
-public class AddStandardComment implements Action {
+public class AddStandardComment extends IssueAction {
public interface Factory {
AddStandardComment create();
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromProperty.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromProperty.java
new file mode 100644
index 0000000..91cd34d
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromProperty.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.base.workflow;
+
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+
+/** Creates a version in the ITS. The value of the version is extracted from an event property. */
+public class CreateVersionFromProperty extends ProjectAction {
+
+ public interface Factory {
+ CreateVersionFromProperty create();
+ }
+
+ private final CreateVersionFromPropertyParametersExtractor parametersExtractor;
+
+ @Inject
+ public CreateVersionFromProperty(
+ CreateVersionFromPropertyParametersExtractor parametersExtractor) {
+ this.parametersExtractor = parametersExtractor;
+ }
+
+ @Override
+ public void execute(
+ ItsFacade its, String itsProject, ActionRequest actionRequest, Map<String, String> properties)
+ throws IOException {
+ Optional<CreateVersionFromPropertyParameters> parameters =
+ parametersExtractor.extract(actionRequest, properties);
+ if (!parameters.isPresent()) {
+ return;
+ }
+
+ its.createVersion(itsProject, parameters.get().getPropertyValue());
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParameters.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParameters.java
new file mode 100644
index 0000000..2ddc245
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParameters.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.base.workflow;
+
+/** Parameters needed by {@link CreateVersionFromProperty} action */
+public class CreateVersionFromPropertyParameters {
+
+ private final String propertyValue;
+
+ public CreateVersionFromPropertyParameters(String propertyValue) {
+ this.propertyValue = propertyValue;
+ }
+
+ /** @return The extracted property value that will be used as the version value */
+ public String getPropertyValue() {
+ return propertyValue;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParametersExtractor.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParametersExtractor.java
new file mode 100644
index 0000000..1eb01ce
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParametersExtractor.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.base.workflow;
+
+import com.google.common.base.Strings;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Inject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class CreateVersionFromPropertyParametersExtractor {
+
+ private static final Logger log =
+ LoggerFactory.getLogger(CreateVersionFromPropertyParametersExtractor.class);
+
+ @Inject
+ public CreateVersionFromPropertyParametersExtractor() {}
+
+ public Optional<CreateVersionFromPropertyParameters> extract(
+ ActionRequest actionRequest, Map<String, String> properties) {
+ String[] parameters = actionRequest.getParameters();
+ if (parameters.length != 1) {
+ log.error(
+ "Wrong number of received parameters. Received parameters are {}. Only one parameter is expected, the property id.",
+ Arrays.toString(parameters));
+ return Optional.empty();
+ }
+
+ String propertyId = parameters[0];
+ if (Strings.isNullOrEmpty(propertyId)) {
+ log.error("Received property id is blank");
+ return Optional.empty();
+ }
+
+ if (!properties.containsKey(propertyId)) {
+ log.error("No event property found for id {}", propertyId);
+ return Optional.empty();
+ }
+
+ String propertyValue = properties.get(propertyId);
+ return Optional.of(new CreateVersionFromPropertyParameters(propertyValue));
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/IssueAction.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/IssueAction.java
new file mode 100644
index 0000000..230517f
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/IssueAction.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.base.workflow;
+
+/** Abstraction for actions on ITS issues */
+public abstract class IssueAction implements Action {
+
+ @Override
+ public final ActionType getType() {
+ return ActionType.ISSUE;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEvent.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEvent.java
index 1f12f19..6eec9a4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEvent.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEvent.java
@@ -27,7 +27,7 @@
*
* <p>This event helps when developing rules as available properties become visible.
*/
-public class LogEvent implements Action {
+public class LogEvent extends IssueAction {
private static final Logger log = LoggerFactory.getLogger(LogEvent.class);
private enum Level {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ProjectAction.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ProjectAction.java
new file mode 100644
index 0000000..4c3c55b
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ProjectAction.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.base.workflow;
+
+/** Abstraction for actions on ITS projects */
+public abstract class ProjectAction implements Action {
+
+ @Override
+ public final ActionType getType() {
+ return ActionType.PROJECT;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RefEventProperties.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RefEventProperties.java
new file mode 100644
index 0000000..30940bb
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/RefEventProperties.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.base.workflow;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/** The properties extracted from a {@link com.google.gerrit.server.events.RefEvent} */
+public class RefEventProperties {
+
+ private final Map<String, String> projectProperties;
+ private final Set<Map<String, String>> issuesProperties;
+
+ /**
+ * @param projectProperties Properties of the ref event
+ * @param issuesProperties Properties of the ref event added of the properties specific to the
+ * issues. There will be as many set of properties as number of issues
+ */
+ public RefEventProperties(
+ Map<String, String> projectProperties, Set<Map<String, String>> issuesProperties) {
+ this.projectProperties = Collections.unmodifiableMap(projectProperties);
+ this.issuesProperties = Collections.unmodifiableSet(issuesProperties);
+ }
+
+ /** @return Properties of the ref event */
+ public Map<String, String> getProjectProperties() {
+ return projectProperties;
+ }
+
+ /**
+ * @return Properties of the ref event added of the properties specific to the issues. There will
+ * be as many set of properties as number of issues
+ */
+ public Set<Map<String, String>> getIssuesProperties() {
+ return issuesProperties;
+ }
+}
diff --git a/src/main/resources/Documentation/config-common.md b/src/main/resources/Documentation/config-common.md
index 9532510..72d5e24 100644
--- a/src/main/resources/Documentation/config-common.md
+++ b/src/main/resources/Documentation/config-common.md
@@ -122,6 +122,23 @@
branch = ^refs/heads/stable-.*
```
+[associating-its-project]: #associating-its-project
+<a name="associating-its-project">Associating a Gerrit project with its ITS
+project counterpart</a>
+---------------------------------------------------------------
+
+To be able to make use of actions acting at the ITS project level, you must
+associate a Gerrit project to its ITS project counterpart.
+
+It must be configured per project and per plugin. To configure the association
+for a project mapping to an ITS project named `manhattan-project`, the project
+must have the following entry in its `project.config` file in the
+`refs/meta/config` branch:
+
+```
+ [plugin "@PLUGIN@"]
+ its-project = manhattan-project
+```
[configure-rules]: #configure-rules
diff --git a/src/main/resources/Documentation/config-rulebase-common.md b/src/main/resources/Documentation/config-rulebase-common.md
index 65c18b1..64059bf 100644
--- a/src/main/resources/Documentation/config-rulebase-common.md
+++ b/src/main/resources/Documentation/config-rulebase-common.md
@@ -564,6 +564,9 @@
[`add-soy-comment`][action-add-soy-comment]
: adds a rendered Closure Template (soy) template as issue comment
+[`create-version-from-property`][action-create-version-from-property]
+: creates a version based on an event's property value
+
[`log-event`][action-log-event]
: appends the event's properties to Gerrit's log
@@ -648,6 +651,22 @@
action = add-property-to-field branch labels
```
+[action-create-version-from-property]: #create-version-from-property
+### <a name="create-version-from-property">Action: create-version-from-property</a>
+
+The `create-version-from-property` action creates a version in the ITS project
+by using an event property value as the version value.
+
+This is useful when you want to create a new version in the ITS when a tag is
+created in the Gerrit project.
+
+Example with the event property `ref`:
+
+```
+ action = create-version-from-property ref
+```
+
+
[action-log-event]: #action-log-event
### <a name="action-log-event">Action: log-event</a>
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfigTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfigTest.java
index 9664542..1a147c7 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfigTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/its/ItsConfigTest.java
@@ -14,6 +14,7 @@
package com.googlesource.gerrit.plugins.its.base.its;
+import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import com.google.common.base.Suppliers;
@@ -38,6 +39,7 @@
import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
import com.googlesource.gerrit.plugins.its.base.validation.ItsAssociationPolicy;
import java.util.Arrays;
+import java.util.Optional;
import org.eclipse.jgit.lib.Config;
public class ItsConfigTest extends LoggingMockingTestCase {
@@ -47,7 +49,8 @@
private PluginConfigFactory pluginConfigFactory;
private Config serverConfig;
- public void setupIsEnabled(String enabled, String parentEnabled, String[] branches) {
+ public void setupIsEnabled(
+ String enabled, String itsProject, String parentEnabled, String[] branches) {
ProjectState projectState = createMock(ProjectState.class);
expect(projectCache.get(new Project.NameKey("testProject"))).andReturn(projectState).anyTimes();
@@ -81,7 +84,7 @@
parents = Arrays.asList(parentProjectState, projectState);
}
- expect(projectState.treeInOrder()).andReturn(parents);
+ expect(projectState.treeInOrder()).andReturn(parents).anyTimes();
PluginConfig pluginConfig = createMock(PluginConfig.class);
@@ -90,6 +93,7 @@
.anyTimes();
expect(pluginConfig.getString("enabled", "false")).andReturn(enabled).anyTimes();
+ expect(pluginConfig.getString(eq("its-project"))).andReturn(itsProject).anyTimes();
PluginConfig pluginConfigWI = createMock(PluginConfig.class);
@@ -104,7 +108,7 @@
public void testIsEnabledRefNoParentNoBranchEnabled() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -116,7 +120,7 @@
public void BROKEN_testIsEnabledRefNoParentNoBranchDisabled() {
String[] branches = {};
- setupIsEnabled("false", null, branches);
+ setupIsEnabled("false", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -128,7 +132,7 @@
public void testIsEnabledRefNoParentNoBranchEnforced() {
String[] branches = {};
- setupIsEnabled("enforced", null, branches);
+ setupIsEnabled("enforced", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -140,7 +144,7 @@
public void testIsEnabledRefNoParentMatchingBranchEnabled() {
String[] branches = {"^refs/heads/test.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -152,7 +156,7 @@
public void BROKEN_testIsEnabledRefNoParentMatchingBranchDisabled() {
String[] branches = {"^refs/heads/test.*"};
- setupIsEnabled("false", null, branches);
+ setupIsEnabled("false", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -164,7 +168,7 @@
public void testIsEnabledRefNoParentMatchingBranchEnforced() {
String[] branches = {"^refs/heads/test.*"};
- setupIsEnabled("enforced", null, branches);
+ setupIsEnabled("enforced", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -176,7 +180,7 @@
public void BROKEN_testIsEnabledRefNoParentNonMatchingBranchEnabled() {
String[] branches = {"^refs/heads/foo.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -188,7 +192,7 @@
public void BROKEN_testIsEnabledRefNoParentNonMatchingBranchDisabled() {
String[] branches = {"^refs/heads/foo.*"};
- setupIsEnabled("false", null, branches);
+ setupIsEnabled("false", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -200,7 +204,7 @@
public void BROKEN_testIsEnabledRefNoParentNonMatchingBranchEnforced() {
String[] branches = {"^refs/heads/foo.*"};
- setupIsEnabled("enforced", null, branches);
+ setupIsEnabled("enforced", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -212,7 +216,7 @@
public void testIsEnabledRefNoParentMatchingBranchMiddleEnabled() {
String[] branches = {"^refs/heads/foo.*", "^refs/heads/test.*", "^refs/heads/baz.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -224,7 +228,7 @@
public void BROKEN_testIsEnabledRefNoParentMatchingBranchMiddleDisabled() {
String[] branches = {"^refs/heads/foo.*", "^refs/heads/test.*", "^refs/heads/baz.*"};
- setupIsEnabled("false", null, branches);
+ setupIsEnabled("false", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -236,7 +240,7 @@
public void testIsEnabledRefNoParentMatchingBranchMiddleEnforced() {
String[] branches = {"^refs/heads/foo.*", "^refs/heads/test.*", "^refs/heads/baz.*"};
- setupIsEnabled("enforced", null, branches);
+ setupIsEnabled("enforced", null, null, branches);
ItsConfig itsConfig = createItsConfig();
@@ -248,7 +252,7 @@
public void BROKEN_testIsEnabledRefParentNoBranchEnabled() {
String[] branches = {};
- setupIsEnabled("false", "true", branches);
+ setupIsEnabled("false", null, "true", branches);
ItsConfig itsConfig = createItsConfig();
@@ -260,7 +264,7 @@
public void BROKEN_testIsEnabledRefParentNoBranchDisabled() {
String[] branches = {};
- setupIsEnabled("false", "false", branches);
+ setupIsEnabled("false", null, "false", branches);
ItsConfig itsConfig = createItsConfig();
@@ -272,7 +276,7 @@
public void testIsEnabledRefParentNoBranchEnforced() {
String[] branches = {};
- setupIsEnabled("false", "enforced", branches);
+ setupIsEnabled("false", null, "enforced", branches);
ItsConfig itsConfig = createItsConfig();
@@ -284,7 +288,7 @@
public void testIsEnabledEventNoBranches() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -297,7 +301,7 @@
public void testIsEnabledEventSingleBranchExact() {
String[] branches = {"refs/heads/testBranch"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -310,7 +314,7 @@
public void testIsEnabledEventSingleBranchRegExp() {
String[] branches = {"^refs/heads/test.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -323,7 +327,7 @@
public void BROKEN_testIsEnabledEventSingleBranchNonMatchingRegExp() {
String[] branches = {"^refs/heads/foo.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -336,7 +340,7 @@
public void testIsEnabledEventMultiBranchExact() {
String[] branches = {"refs/heads/foo", "refs/heads/testBranch"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -349,7 +353,7 @@
public void testIsEnabledEventMultiBranchRegExp() {
String[] branches = {"^refs/heads/foo.*", "^refs/heads/test.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -362,7 +366,7 @@
public void testIsEnabledEventMultiBranchMixedMatchExact() {
String[] branches = {"refs/heads/testBranch", "refs/heads/foo.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -375,7 +379,7 @@
public void testIsEnabledEventMultiBranchMixedMatchRegExp() {
String[] branches = {"refs/heads/foo", "^refs/heads/test.*"};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -388,7 +392,7 @@
public void BROKEN_testIsEnabledEventDisabled() {
String[] branches = {"^refs/heads/testBranch"};
- setupIsEnabled("false", null, branches);
+ setupIsEnabled("false", null, null, branches);
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
@@ -401,7 +405,7 @@
public void testIsEnabledCommentAddedEvent() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
CommentAddedEvent event = new CommentAddedEvent(testChange("testProject", "testBranch"));
@@ -414,7 +418,7 @@
public void testIsEnabledChangeMergedEvent() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ChangeMergedEvent event = new ChangeMergedEvent(testChange("testProject", "testBranch"));
@@ -427,7 +431,7 @@
public void testIsEnabledChangeAbandonedEvent() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ChangeAbandonedEvent event = new ChangeAbandonedEvent(testChange("testProject", "testBranch"));
@@ -440,7 +444,7 @@
public void testIsEnabledChangeRestoredEvent() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
ChangeRestoredEvent event = new ChangeRestoredEvent(testChange("testProject", "testBranch"));
@@ -453,7 +457,7 @@
public void testIsEnabledRefUpdatedEvent() {
String[] branches = {};
- setupIsEnabled("true", null, branches);
+ setupIsEnabled("true", null, null, branches);
RefUpdatedEvent event = new RefUpdatedEvent();
RefUpdateAttribute refUpdateAttribute = new RefUpdateAttribute();
@@ -479,6 +483,31 @@
assertLogMessageContains("not recognised and ignored");
}
+ public void testGetItsProjectNull() {
+ String[] branches = {};
+ setupIsEnabled("true", null, null, branches);
+
+ ItsConfig itsConfig = createItsConfig();
+
+ replayMocks();
+
+ assertFalse(itsConfig.getItsProjectName(new Project.NameKey("testProject")).isPresent());
+ }
+
+ public void testGetItsProjectConfigured() {
+ String[] branches = {};
+ setupIsEnabled("true", "itsProject", null, branches);
+
+ ItsConfig itsConfig = createItsConfig();
+
+ replayMocks();
+
+ Optional<String> itsProjectName =
+ itsConfig.getItsProjectName(new Project.NameKey("testProject"));
+ assertTrue(itsProjectName.isPresent());
+ assertEquals("itsProject", itsProjectName.get());
+ }
+
public void testGetIssuePatternNullMatch() {
ItsConfig itsConfig = createItsConfig();
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/util/ItsProjectExtractorTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/util/ItsProjectExtractorTest.java
new file mode 100644
index 0000000..0aecd05
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/util/ItsProjectExtractorTest.java
@@ -0,0 +1,62 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.base.util;
+
+import static org.easymock.EasyMock.expect;
+
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.its.ItsConfig;
+import com.googlesource.gerrit.plugins.its.base.testutil.MockingTestCase;
+import java.util.Optional;
+
+public class ItsProjectExtractorTest extends MockingTestCase {
+
+ private static final String PROJECT = "project";
+ private static final String ITS_PROJECT = "itsProject";
+
+ private Injector injector;
+ private ItsConfig itsConfig;
+
+ public void test() {
+ ItsProjectExtractor projectExtractor = injector.getInstance(ItsProjectExtractor.class);
+
+ expect(itsConfig.getItsProjectName(new Project.NameKey(PROJECT)))
+ .andReturn(Optional.of(ITS_PROJECT))
+ .once();
+
+ replayMocks();
+
+ String ret = projectExtractor.getItsProject(PROJECT).orElse(null);
+ assertEquals(ret, ITS_PROJECT);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ injector = Guice.createInjector(new TestModule());
+ }
+
+ private class TestModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ itsConfig = createMock(ItsConfig.class);
+ bind(ItsConfig.class).toInstance(itsConfig);
+ }
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractorTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractorTest.java
index 249abcd..d2a7480 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractorTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/util/PropertyExtractorTest.java
@@ -43,20 +43,25 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
public class PropertyExtractorTest extends LoggingMockingTestCase {
private Injector injector;
+ private ItsProjectExtractor itsProjectExtractor;
private IssueExtractor issueExtractor;
private PropertyAttributeExtractor propertyAttributeExtractor;
public void testDummyChangeEvent() {
PropertyExtractor propertyExtractor = injector.getInstance(PropertyExtractor.class);
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
replayMocks();
- Set<Map<String, String>> actual = propertyExtractor.extractFrom(new DummyEvent());
+ Set<Map<String, String>> actual =
+ propertyExtractor.extractFrom(new DummyEvent()).getIssuesProperties();
Set<Map<String, String>> expected = new HashSet<>();
assertEquals("Properties do not match", expected, actual);
@@ -65,6 +70,8 @@
public void testChangeAbandonedEvent() {
ChangeAbandonedEvent event = new ChangeAbandonedEvent(testChange("testProject", "testBranch"));
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
ChangeAttribute changeAttribute = createMock(ChangeAttribute.class);
event.change = Suppliers.ofInstance(changeAttribute);
Map<String, String> changeProperties =
@@ -104,6 +111,8 @@
public void testChangeMergedEvent() {
ChangeMergedEvent event = new ChangeMergedEvent(testChange("testProject", "testBranch"));
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
ChangeAttribute changeAttribute = createMock(ChangeAttribute.class);
event.change = Suppliers.ofInstance(changeAttribute);
Map<String, String> changeProperties =
@@ -141,6 +150,8 @@
public void testChangeRestoredEvent() {
ChangeRestoredEvent event = new ChangeRestoredEvent(testChange("testProject", "testBranch"));
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
ChangeAttribute changeAttribute = createMock(ChangeAttribute.class);
event.change = Suppliers.ofInstance(changeAttribute);
Map<String, String> changeProperties =
@@ -179,6 +190,8 @@
public void testCommentAddedEventWOApprovals() {
CommentAddedEvent event = new CommentAddedEvent(testChange("testProject", "testBranch"));
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
ChangeAttribute changeAttribute = createMock(ChangeAttribute.class);
event.change = Suppliers.ofInstance(changeAttribute);
Map<String, String> changeProperties =
@@ -218,6 +231,8 @@
public void testCommentAddedEventWApprovals() {
CommentAddedEvent event = new CommentAddedEvent(testChange("testProject", "testBranch"));
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
ChangeAttribute changeAttribute = createMock(ChangeAttribute.class);
event.change = Suppliers.ofInstance(changeAttribute);
Map<String, String> changeProperties =
@@ -270,6 +285,8 @@
public void testPatchSetCreatedEvent() {
PatchSetCreatedEvent event = new PatchSetCreatedEvent(testChange("testProject", "testBranch"));
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
ChangeAttribute changeAttribute = createMock(ChangeAttribute.class);
event.change = Suppliers.ofInstance(changeAttribute);
Map<String, String> changeProperties =
@@ -325,6 +342,8 @@
refUpdateAttribute.oldRev = "oldRevision";
refUpdateAttribute.refName = "testBranch";
+ expect(itsProjectExtractor.getItsProject("testProject")).andReturn(Optional.empty());
+
Map<String, String> common =
ImmutableMap.<String, String>builder()
.putAll(accountProperties)
@@ -357,7 +376,7 @@
replayMocks();
- Set<Map<String, String>> actual = propertyExtractor.extractFrom(event);
+ Set<Map<String, String>> actual = propertyExtractor.extractFrom(event).getIssuesProperties();
Map<String, String> propertiesIssue4711 =
ImmutableMap.<String, String>builder()
@@ -395,6 +414,9 @@
protected void configure() {
bind(String.class).annotatedWith(PluginName.class).toInstance("ItsTestName");
+ itsProjectExtractor = createMock(ItsProjectExtractor.class);
+ bind(ItsProjectExtractor.class).toInstance(itsProjectExtractor);
+
issueExtractor = createMock(IssueExtractor.class);
bind(IssueExtractor.class).toInstance(issueExtractor);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionControllerTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionControllerTest.java
index 8c8a7de..bd32089 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionControllerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionControllerTest.java
@@ -47,7 +47,9 @@
ChangeEvent event = createMock(ChangeEvent.class);
Set<Map<String, String>> propertySets = new HashSet<>();
- expect(propertyExtractor.extractFrom(event)).andReturn(propertySets).anyTimes();
+ expect(propertyExtractor.extractFrom(event))
+ .andReturn(new RefEventProperties(Collections.emptyMap(), propertySets))
+ .anyTimes();
replayMocks();
@@ -63,34 +65,49 @@
Map<String, String> properties = ImmutableMap.of("fake", "property");
propertySets.add(properties);
- expect(propertyExtractor.extractFrom(event)).andReturn(propertySets).anyTimes();
+ expect(propertyExtractor.extractFrom(event))
+ .andReturn(new RefEventProperties(properties, propertySets))
+ .anyTimes();
// When no issues are found in the commit message, the list of actions is empty
// as there are no matchs with an empty map of properties.
Collection<ActionRequest> actions = Collections.emptySet();
- expect(ruleBase.actionRequestsFor(properties)).andReturn(actions).once();
+ expect(ruleBase.actionRequestsFor(properties)).andReturn(actions).times(2);
replayMocks();
actionController.onEvent(event);
}
- public void testSinglePropertyMapSingleActionSingleIssue() {
+ public void testSinglePropertyMapSingleIssueActionSingleProjectAction() {
ActionController actionController = createActionController();
ChangeEvent event = createMock(ChangeEvent.class);
- Map<String, String> properties = ImmutableMap.of("issue", "testIssue");
+ Map<String, String> projectProperties = ImmutableMap.of("its-project", "itsProject");
- Set<Map<String, String>> propertySets = ImmutableSet.of(properties);
+ Map<String, String> issueProperties =
+ ImmutableMap.<String, String>builder()
+ .putAll(projectProperties)
+ .put("issue", "testIssue")
+ .build();
- expect(propertyExtractor.extractFrom(event)).andReturn(propertySets).anyTimes();
+ Set<Map<String, String>> propertySets = ImmutableSet.of(issueProperties);
- ActionRequest actionRequest1 = createMock(ActionRequest.class);
- Collection<ActionRequest> actionRequests = ImmutableList.of(actionRequest1);
- expect(ruleBase.actionRequestsFor(properties)).andReturn(actionRequests).once();
+ expect(propertyExtractor.extractFrom(event))
+ .andReturn(new RefEventProperties(projectProperties, propertySets))
+ .anyTimes();
- actionExecutor.execute(actionRequests, properties);
+ ActionRequest issueActionRequest1 = createMock(ActionRequest.class);
+ Collection<ActionRequest> issueActionRequests = ImmutableList.of(issueActionRequest1);
+ expect(ruleBase.actionRequestsFor(issueProperties)).andReturn(issueActionRequests).once();
+
+ ActionRequest projectActionRequest1 = createMock(ActionRequest.class);
+ Collection<ActionRequest> projectActionRequests = ImmutableList.of(projectActionRequest1);
+ expect(ruleBase.actionRequestsFor(projectProperties)).andReturn(projectActionRequests).once();
+
+ actionExecutor.executeOnIssue(issueActionRequests, issueProperties);
+ actionExecutor.executeOnProject(projectActionRequests, projectProperties);
replayMocks();
@@ -107,7 +124,9 @@
Set<Map<String, String>> propertySets = ImmutableSet.of(properties1, properties2);
- expect(propertyExtractor.extractFrom(event)).andReturn(propertySets).anyTimes();
+ expect(propertyExtractor.extractFrom(event))
+ .andReturn(new RefEventProperties(Collections.emptyMap(), propertySets))
+ .anyTimes();
ActionRequest actionRequest1 = createMock(ActionRequest.class);
Collection<ActionRequest> actionRequests1 = ImmutableList.of(actionRequest1);
@@ -119,8 +138,8 @@
expect(ruleBase.actionRequestsFor(properties1)).andReturn(actionRequests1).once();
expect(ruleBase.actionRequestsFor(properties2)).andReturn(actionRequests2).once();
- actionExecutor.execute(actionRequests1, properties1);
- actionExecutor.execute(actionRequests2, properties2);
+ actionExecutor.executeOnIssue(actionRequests1, properties1);
+ actionExecutor.executeOnIssue(actionRequests2, properties2);
replayMocks();
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutorTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutorTest.java
index 3864cdf..88cfd1a 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutorTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutorTest.java
@@ -26,6 +26,7 @@
import com.googlesource.gerrit.plugins.its.base.its.ItsFacadeFactory;
import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
import java.io.IOException;
+import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -39,9 +40,15 @@
private AddSoyComment.Factory addSoyCommentFactory;
private LogEvent.Factory logEventFactory;
private AddPropertyToField.Factory addPropertyToFieldFactory;
+ private CreateVersionFromProperty.Factory createVersionFromPropertyFactory;
private Map<String, String> properties =
ImmutableMap.of("issue", "4711", "project", "testProject");
+ private Map<String, String> projectProperties =
+ ImmutableMap.<String, String>builder()
+ .putAll(properties)
+ .put("its-project", "itsTestProject")
+ .build();
public void testExecuteItem() throws IOException {
ActionRequest actionRequest = createMock(ActionRequest.class);
@@ -57,7 +64,7 @@
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute(actionRequests, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
}
public void testExecuteItemException() throws IOException {
@@ -75,7 +82,7 @@
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute(actionRequests, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
assertLogThrowableMessageContains("injected exception 1");
}
@@ -100,7 +107,7 @@
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute(actionRequests, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
}
public void testExecuteIterableExceptions() throws IOException {
@@ -131,7 +138,7 @@
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute(actionRequests, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
assertLogThrowableMessageContains("injected exception 1");
assertLogThrowableMessageContains("injected exception 3");
@@ -153,7 +160,7 @@
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute(actionRequests, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
}
public void testAddSoyCommentDelegation() throws IOException {
@@ -172,7 +179,7 @@
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute(actionRequests, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
}
public void testAddStandardCommentDelegation() throws IOException {
@@ -191,7 +198,7 @@
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute(actionRequests, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
}
public void testLogEventDelegation() throws IOException {
@@ -210,7 +217,25 @@
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute(actionRequests, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
+ }
+
+ public void testCreateVersionFromPropertyDelegation() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getName()).andReturn("create-version-from-property");
+
+ CreateVersionFromProperty createVersionFromProperty =
+ createMock(CreateVersionFromProperty.class);
+ expect(createVersionFromPropertyFactory.create()).andReturn(createVersionFromProperty);
+ expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+ .andReturn(its);
+
+ createVersionFromProperty.execute(its, "itsTestProject", actionRequest, projectProperties);
+
+ replayMocks();
+
+ ActionExecutor actionExecutor = createActionExecutor();
+ actionExecutor.executeOnProject(Collections.singleton(actionRequest), projectProperties);
}
public void testAddPropertyToFieldDelegation() throws IOException {
@@ -229,7 +254,7 @@
replayMocks();
ActionExecutor actionExecutor = createActionExecutor();
- actionExecutor.execute(actionRequests, properties);
+ actionExecutor.executeOnIssue(actionRequests, properties);
}
private ActionExecutor createActionExecutor() {
@@ -265,6 +290,9 @@
addPropertyToFieldFactory = createMock(AddPropertyToField.Factory.class);
bind(AddPropertyToField.Factory.class).toInstance(addPropertyToFieldFactory);
+
+ createVersionFromPropertyFactory = createMock(CreateVersionFromProperty.Factory.class);
+ bind(CreateVersionFromProperty.Factory.class).toInstance(createVersionFromPropertyFactory);
}
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParametersExtractorTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParametersExtractorTest.java
new file mode 100644
index 0000000..82b33e7
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyParametersExtractorTest.java
@@ -0,0 +1,83 @@
+package com.googlesource.gerrit.plugins.its.base.workflow;
+
+import static org.easymock.EasyMock.expect;
+
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.testutil.MockingTestCase;
+import java.util.Collections;
+import java.util.Optional;
+
+public class CreateVersionFromPropertyParametersExtractorTest extends MockingTestCase {
+
+ private static final String ITS_PROJECT = "test-project";
+ private static final String PROPERTY_ID = "propertyId";
+ private static final String PROPERTY_VALUE = "propertyValue";
+
+ private CreateVersionFromPropertyParametersExtractor extractor;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ Injector injector = Guice.createInjector(new TestModule());
+ extractor = injector.getInstance(CreateVersionFromPropertyParametersExtractor.class);
+ }
+
+ private class TestModule extends FactoryModule {}
+
+ public void testNoParameter() {
+ testWrongNumberOfReceivedParameters(new String[] {});
+ }
+
+ public void testTwoParameters() {
+ testWrongNumberOfReceivedParameters(new String[] {PROPERTY_ID, PROPERTY_ID});
+ }
+
+ private void testWrongNumberOfReceivedParameters(String[] parameters) {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(parameters);
+
+ replayMocks();
+
+ Optional<CreateVersionFromPropertyParameters> extractedParameters =
+ extractor.extract(actionRequest, Collections.emptyMap());
+ assertFalse(extractedParameters.isPresent());
+ }
+
+ public void testBlankPropertyId() {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(new String[] {""});
+
+ replayMocks();
+
+ Optional<CreateVersionFromPropertyParameters> extractedParameters =
+ extractor.extract(actionRequest, Collections.emptyMap());
+ assertFalse(extractedParameters.isPresent());
+ }
+
+ public void testUnknownPropertyId() {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(new String[] {PROPERTY_ID});
+
+ replayMocks();
+
+ Optional<CreateVersionFromPropertyParameters> extractedParameters =
+ extractor.extract(actionRequest, Collections.emptyMap());
+ assertFalse(extractedParameters.isPresent());
+ }
+
+ public void testHappyPath() {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+ expect(actionRequest.getParameters()).andReturn(new String[] {PROPERTY_ID});
+
+ replayMocks();
+
+ Optional<CreateVersionFromPropertyParameters> extractedParameters =
+ extractor.extract(actionRequest, Collections.singletonMap(PROPERTY_ID, PROPERTY_VALUE));
+ if (!extractedParameters.isPresent()) {
+ fail();
+ }
+ assertEquals(PROPERTY_VALUE, extractedParameters.get().getPropertyValue());
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyTest.java
new file mode 100644
index 0000000..a89509c
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/CreateVersionFromPropertyTest.java
@@ -0,0 +1,76 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.base.workflow;
+
+import static org.easymock.EasyMock.expect;
+
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.base.testutil.MockingTestCase;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import org.easymock.EasyMock;
+
+public class CreateVersionFromPropertyTest extends MockingTestCase {
+
+ private static final String ITS_PROJECT = "test-project";
+ private static final String PROPERTY_ID = "propertyId";
+ private static final String PROPERTY_VALUE = "propertyValue";
+
+ private Injector injector;
+ private ItsFacade its;
+ private CreateVersionFromPropertyParametersExtractor parametersExtractor;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ injector = Guice.createInjector(new TestModule());
+ }
+
+ private class TestModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ its = createMock(ItsFacade.class);
+ bind(ItsFacade.class).toInstance(its);
+
+ parametersExtractor = createMock(CreateVersionFromPropertyParametersExtractor.class);
+ bind(CreateVersionFromPropertyParametersExtractor.class).toInstance(parametersExtractor);
+ }
+ }
+
+ public void testHappyPath() throws IOException {
+ ActionRequest actionRequest = createMock(ActionRequest.class);
+
+ Map<String, String> properties = Collections.emptyMap();
+ expect(parametersExtractor.extract(actionRequest, properties))
+ .andReturn(Optional.of(new CreateVersionFromPropertyParameters(PROPERTY_VALUE)));
+
+ its.createVersion(ITS_PROJECT, PROPERTY_VALUE);
+ EasyMock.expectLastCall().once();
+
+ replayMocks();
+
+ CreateVersionFromProperty createVersionFromProperty = createCreateVersionFromProperty();
+ createVersionFromProperty.execute(its, ITS_PROJECT, actionRequest, properties);
+ }
+
+ private CreateVersionFromProperty createCreateVersionFromProperty() {
+ return injector.getInstance(CreateVersionFromProperty.class);
+ }
+}