Skip to content

Commit

Permalink
Allow specifying subtypes for events
Browse files Browse the repository at this point in the history
For example one particular check run name, etc

Correct minor generics issue in dict cast.

BUG=203574077
PiperOrigin-RevId: 406205424
Change-Id: If1cb9d1ba807318479a03bde0665ceb7128ebe2b
  • Loading branch information
hsudhof authored and copybara-github committed Oct 28, 2021
1 parent dd49a9c commit beb7d5f
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 24 deletions.
2 changes: 1 addition & 1 deletion docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2544,7 +2544,7 @@ Parameter | Description
--------- | -----------
url | `string`<br><p>Indicates the GitHub repo URL.</p>
checker | `checker` or `NoneType`<br><p>A checker for the GitHub API transport provided by this trigger.</p>
events | `sequence of string`<br><p>Type of events to subscribe. Valid values are: `'ISSUES'`, `'ISSUE_COMMENT'`, `'PULL_REQUEST'`, `'PULL_REQUEST_REVIEW_COMMENT'`, `'PUSH'`, `'STATUS'`, </p>
events | `sequence of string` or `dict of sequence`<br><p>Type of events to subscribe. Valid values are: `'ISSUES'`, `'ISSUE_COMMENT'`, `'PULL_REQUEST'`, `'PULL_REQUEST_REVIEW_COMMENT'`, `'PUSH'`, `'STATUS'`, `'CHECK_RUNS'`</p>
Expand Down
7 changes: 4 additions & 3 deletions java/com/google/copybara/config/SkylarkUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import net.starlark.java.eval.EvalException;
import net.starlark.java.eval.Sequence;
import net.starlark.java.eval.Starlark;
import net.starlark.java.eval.StarlarkList;

/**
* Utilities for dealing with Skylark parameter objects and converting them to Java ones.
Expand Down Expand Up @@ -203,8 +204,8 @@ public static String convertOptionalString(Object x) {
}

/** Casts nested sequence type in Dict */
public static <K, V> Dict<K, V> castOfSequence(
Object x, Class<K> keyType, Class<K> nestedValueType, String what) throws EvalException {
public static <K, V> Dict<K, StarlarkList<V>> castOfSequence(
Object x, Class<K> keyType, Class<V> nestedValueType, String what) throws EvalException {
Preconditions.checkNotNull(x);
if (!(x instanceof Dict)) {
throw Starlark.errorf("got %s for '%s', want dict", Starlark.type(x), what);
Expand All @@ -224,7 +225,7 @@ public static <K, V> Dict<K, V> castOfSequence(
}

@SuppressWarnings("unchecked") // safe
Dict<K, V> res = (Dict<K, V>) x;
Dict<K, StarlarkList<V>> res = (Dict<K, StarlarkList<V>>) x;
return res;
}
}
36 changes: 36 additions & 0 deletions java/com/google/copybara/git/EventTrigger.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2021 Google Inc.
*
* 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.copybara.git;

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
import com.google.copybara.git.github.api.GitHubEventType;

/**
* A simple pair to express GitHub Events with arbitrary subtypes (Status, CheckRun)
*/
@AutoValue
public abstract class EventTrigger {
public abstract GitHubEventType type();

public abstract ImmutableSet<String> subtypes();


public static EventTrigger create(GitHubEventType type, Iterable<String> subtypes) {
return new AutoValue_EventTrigger(type, ImmutableSet.copyOf(subtypes));
}
}
21 changes: 16 additions & 5 deletions java/com/google/copybara/git/GitHubTrigger.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@

package com.google.copybara.git;

import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.copybara.Endpoint;
import com.google.copybara.LazyResourceLoader;
import com.google.copybara.Trigger;
import com.google.copybara.git.github.api.GitHubApi;
import com.google.copybara.git.github.api.GitHubEventType;
import com.google.copybara.git.github.util.GitHubHost;
import com.google.copybara.util.console.Console;
import java.util.stream.Collectors;

/**
* A feedback trigger based on updates on a GitHub PR.
Expand All @@ -37,13 +37,13 @@ public class GitHubTrigger implements Trigger {
private final LazyResourceLoader<GitHubApi> apiSupplier;
private final String url;
private GitHubHost ghHost;
private final ImmutableSet<GitHubEventType> events;
private final ImmutableSet<EventTrigger> events;
private final Console console;

GitHubTrigger(
LazyResourceLoader<GitHubApi> apiSupplier,
String url,
ImmutableSet<GitHubEventType> events,
ImmutableSet<EventTrigger> events,
Console console,
GitHubHost ghHost) {
this.apiSupplier = Preconditions.checkNotNull(apiSupplier);
Expand All @@ -64,14 +64,25 @@ public ImmutableSetMultimap<String, String> describe() {
ImmutableSetMultimap.Builder<String, String> builder = ImmutableSetMultimap.builder();
builder.put("type", "github_trigger");
builder.put("url", url);
builder.putAll("events", Iterables.transform(events, GitHubEventType::toString));
builder.putAll("events",
events.stream().map(s -> s.type().name()).collect(Collectors.toList()));
for (EventTrigger trigger : events) {
if (trigger.subtypes().isEmpty()) {
continue;
}
builder.putAll(
String.format("SUBTYPES_%s", trigger.type().name()),
trigger.subtypes());
}
return builder.build();
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("url", url)
.add("event_types", Joiner.on(",").join(events))
.toString();
}

}
58 changes: 43 additions & 15 deletions java/com/google/copybara/git/GitModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -2024,38 +2025,29 @@ public GerritTrigger gerritTrigger(String url, Object checkerObj, StarlarkThread
name = "events",
allowedTypes = {
@ParamType(type = Sequence.class, generic1 = String.class),
@ParamType(type = Dict.class, generic1 = Sequence.class),
},
named = true,
defaultValue = "[]",
doc =
"Type of events to subscribe. Valid values are: `'ISSUES'`, `'ISSUE_COMMENT'`,"
+ " `'PULL_REQUEST'`, `'PULL_REQUEST_REVIEW_COMMENT'`, `'PUSH'`,"
+ " `'STATUS'`, "),
+ " `'STATUS'`, `'CHECK_RUNS'`"),
},
useStarlarkThread = true)
@UsesFlags(GitHubOptions.class)
public GitHubTrigger gitHubTrigger(
String url,
Object checkerObj,
Sequence<?> events, // <String>
Object events,
StarlarkThread thread)
throws EvalException {
checkNotEmpty(url, "url");
url = fixHttp(url, thread.getCallerLocation());
Checker checker = convertFromNoneable(checkerObj, null);
LinkedHashSet<GitHubEventType> eventBuilder = new LinkedHashSet<>();
for (String e : Sequence.cast(events, String.class, "events")) {
GitHubEventType event = stringToEnum("events", e, GitHubEventType.class);
check(eventBuilder.add(event), "Repeated element %s", e);
check(
WATCHABLE_EVENTS.contains(event),
"%s is not a valid value. Values: %s",
event,
WATCHABLE_EVENTS);
}
check(!eventBuilder.isEmpty(), "events cannot be empty");

ImmutableSet<GitHubEventType> parsedEvents = ImmutableSet.copyOf(eventBuilder);
LinkedHashSet<EventTrigger> eventBuilder = new LinkedHashSet<>();
LinkedHashSet<GitHubEventType> types = new LinkedHashSet<>();
ImmutableSet<EventTrigger> parsedEvents = handleEventTypes(events, eventBuilder, types);
validateEndpointChecker(checker, GITHUB_TRIGGER);
GitHubOptions gitHubOptions = options.get(GitHubOptions.class);
return new GitHubTrigger(
Expand All @@ -2066,6 +2058,42 @@ public GitHubTrigger gitHubTrigger(
GITHUB_COM);
}

private ImmutableSet<EventTrigger> handleEventTypes(
Object events, LinkedHashSet<EventTrigger> eventBuilder,
LinkedHashSet<GitHubEventType> types) throws EvalException {
if (events instanceof Sequence) {
for (String e : Sequence.cast(events, String.class, "events")) {
GitHubEventType event = stringToEnum("events", e, GitHubEventType.class);
check(eventBuilder.add(EventTrigger.create(event, ImmutableSet.of())),
"Repeated element %s", e);
}
} else if (events instanceof Dict) {
Dict<String, StarlarkList<String>> dict = SkylarkUtil.castOfSequence(
events,
String.class,
String.class,
"events");
for (Entry<String, StarlarkList<String>> trigger : dict.entrySet()) {
check(types.add(
stringToEnum("events", trigger.getKey(), GitHubEventType.class)),
"Repeated element %s", trigger);
eventBuilder.add(
EventTrigger.create(
stringToEnum("events", trigger.getKey(), GitHubEventType.class),
ImmutableSet.copyOf(trigger.getValue())));
}
}
for (EventTrigger trigger : eventBuilder) {
check(
WATCHABLE_EVENTS.contains(trigger.type()),
"%s is not a valid value. Values: %s",
trigger.type(),
WATCHABLE_EVENTS);
}
check(!eventBuilder.isEmpty(), "events cannot be empty");
return ImmutableSet.copyOf(eventBuilder);
}

@SuppressWarnings("unused")
@StarlarkMethod(
name = "review_input",
Expand Down
38 changes: 38 additions & 0 deletions javatests/com/google/copybara/git/GitHubTriggerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,44 @@ public void testParsing() throws Exception {
.containsExactly("type", "github_api", "url", "https://github.com/google/example");
}

@Test
public void testParsing_dict() throws Exception {
GitHubTrigger gitHubTrigger =
skylarkTestExecutor.eval(
"e", "e = git.github_trigger("
+ "url = 'https://github.com/google/example',"
+ "events = {'STATUS': [], 'ISSUES': []})");
assertThat(gitHubTrigger.describe()).containsExactly(
"type", "github_trigger",
"url", "https://github.com/google/example",
"events", "STATUS",
"events", "ISSUES").inOrder();

assertThat(gitHubTrigger.getEndpoint().describe())
.containsExactly("type", "github_api", "url", "https://github.com/google/example");
}

@Test
public void testParsing_dictWithSubCategory() throws Exception {
GitHubTrigger gitHubTrigger =
skylarkTestExecutor.eval(
"e", "e = git.github_trigger("
+ "url = 'https://github.com/google/example',"
+ "events = {'STATUS': ['foobar', 'bar'], 'ISSUES': []})");
assertThat(gitHubTrigger.describe()).containsExactly(
"type", "github_trigger",
"url", "https://github.com/google/example",
"events", "STATUS",
"events", "ISSUES",
"SUBTYPES_STATUS", "foobar",
"SUBTYPES_STATUS", "bar"
).inOrder();


assertThat(gitHubTrigger.getEndpoint().describe())
.containsExactly("type", "github_api", "url", "https://github.com/google/example");
}

@Test
public void testInvalidEvent() {
skylarkTestExecutor.evalFails(
Expand Down

0 comments on commit beb7d5f

Please sign in to comment.