Skip to content

Commit 0bae1c5

Browse files
authored
concord-server: remove GithubTriggerProcessor interface (#974)
1 parent 0e3c0a8 commit 0bae1c5

File tree

5 files changed

+282
-336
lines changed

5 files changed

+282
-336
lines changed

server/impl/src/main/java/com/walmartlabs/concord/server/events/EventModule.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@
2222

2323
import com.google.inject.Binder;
2424
import com.google.inject.Module;
25+
import com.walmartlabs.concord.server.events.github.GithubTriggerProcessor;
2526
import com.walmartlabs.concord.server.sdk.events.ProcessEventListener;
2627

28+
import static com.google.inject.Scopes.SINGLETON;
2729
import static com.google.inject.multibindings.Multibinder.newSetBinder;
2830

2931
public class EventModule implements Module {
3032

3133
@Override
3234
public void configure(Binder binder) {
3335
newSetBinder(binder, ProcessEventListener.class);
36+
binder.bind(GithubTriggerProcessor.class).in(SINGLETON);
3437
}
3538
}

server/impl/src/main/java/com/walmartlabs/concord/server/events/GithubEventResource.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public class GithubEventResource implements Resource {
8686
private final GithubConfiguration githubCfg;
8787
private final TriggerProcessExecutor executor;
8888
private final AuditLog auditLog;
89-
private final List<GithubTriggerProcessor> processors;
89+
private final GithubTriggerProcessor processor;
9090
private final UserManager userManager;
9191
private final LdapManager ldapManager;
9292
private final TriggerEventInitiatorResolver initiatorResolver;
@@ -96,7 +96,7 @@ public class GithubEventResource implements Resource {
9696
public GithubEventResource(GithubConfiguration githubCfg,
9797
TriggerProcessExecutor executor,
9898
AuditLog auditLog,
99-
List<GithubTriggerProcessor> processors,
99+
GithubTriggerProcessor processor,
100100
UserManager userManager,
101101
LdapManager ldapManager,
102102
TriggerEventInitiatorResolver initiatorResolver,
@@ -105,7 +105,7 @@ public GithubEventResource(GithubConfiguration githubCfg,
105105
this.githubCfg = githubCfg;
106106
this.executor = executor;
107107
this.auditLog = auditLog;
108-
this.processors = processors;
108+
this.processor = processor;
109109
this.userManager = userManager;
110110
this.ldapManager = ldapManager;
111111
this.initiatorResolver = initiatorResolver;
@@ -153,7 +153,7 @@ public String onEvent(Map<String, Object> data,
153153
}
154154

155155
List<GithubTriggerProcessor.Result> results = new ArrayList<>();
156-
processors.forEach(p -> p.process(eventName, payload, uriInfo, results));
156+
processor.process(eventName, payload, uriInfo, results);
157157

158158
Supplier<UserEntry> initiatorSupplier = memo(new GithubEventInitiatorSupplier(userManager, ldapManager, payload));
159159

@@ -202,7 +202,7 @@ public Map<String, Object> resolve(TriggerEntry t) {
202202

203203
GithubTriggerExclusiveMode e = objectMapper.convertValue(exclusive, GithubTriggerExclusiveMode.class);
204204
String groupBy = e.groupByProperty();
205-
if (groupBy== null) {
205+
if (groupBy == null) {
206206
return exclusive;
207207
}
208208

server/impl/src/main/java/com/walmartlabs/concord/server/events/github/GithubTriggerProcessor.java

Lines changed: 269 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* You may obtain a copy of the License at
1212
*
1313
* http://www.apache.org/licenses/LICENSE-2.0
14-
*
14+
*
1515
* Unless required by applicable law or agreed to in writing, software
1616
* distributed under the License is distributed on an "AS IS" BASIS,
1717
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,42 +20,291 @@
2020
* =====
2121
*/
2222

23+
import com.walmartlabs.concord.db.MainDB;
24+
import com.walmartlabs.concord.sdk.Constants.Trigger;
25+
import com.walmartlabs.concord.sdk.MapUtils;
26+
import com.walmartlabs.concord.server.cfg.GithubConfiguration;
27+
import com.walmartlabs.concord.server.events.DefaultEventFilter;
28+
import com.walmartlabs.concord.server.org.project.RepositoryDao;
29+
import com.walmartlabs.concord.server.org.project.RepositoryEntry;
2330
import com.walmartlabs.concord.server.org.triggers.TriggerEntry;
31+
import com.walmartlabs.concord.server.org.triggers.TriggerUtils;
32+
import com.walmartlabs.concord.server.org.triggers.TriggersDao;
33+
import com.walmartlabs.concord.server.sdk.metrics.WithTimer;
34+
import com.walmartlabs.concord.server.security.github.GithubKey;
35+
import org.jooq.Configuration;
36+
import org.jooq.DSLContext;
37+
import org.jooq.impl.DSL;
38+
import org.slf4j.Logger;
39+
import org.slf4j.LoggerFactory;
2440

41+
import javax.inject.Inject;
42+
import javax.inject.Named;
43+
import javax.inject.Singleton;
2544
import javax.ws.rs.core.UriInfo;
26-
import java.util.Collections;
27-
import java.util.List;
28-
import java.util.Map;
45+
import java.util.*;
46+
import java.util.function.Consumer;
47+
import java.util.stream.Collectors;
48+
49+
import static com.walmartlabs.concord.server.events.github.Constants.*;
50+
51+
public class GithubTriggerProcessor {
52+
53+
private static final int VERSION_ID = 2;
54+
private static final Logger log = LoggerFactory.getLogger(GithubTriggerProcessor.class);
55+
56+
private final Dao dao;
57+
private final List<EventEnricher> eventEnrichers;
58+
private final boolean isDisableReposOnDeletedRef;
59+
60+
@Inject
61+
public GithubTriggerProcessor(Dao dao,
62+
List<EventEnricher> eventEnrichers,
63+
GithubConfiguration githubCfg) {
64+
this.dao = dao;
65+
this.eventEnrichers = eventEnrichers;
66+
this.isDisableReposOnDeletedRef = githubCfg.isDisableReposOnDeletedRef();
67+
}
68+
69+
@WithTimer
70+
public void process(String eventName, Payload payload, UriInfo uriInfo, List<Result> result) {
71+
GithubKey githubKey = GithubKey.getCurrent();
72+
UUID projectId = githubKey.getProjectId();
73+
74+
if (isDisableReposOnDeletedRef
75+
&& PUSH_EVENT.equals(payload.eventName())
76+
&& isRefDeleted(payload)) {
77+
78+
List<RepositoryEntry> repositories = dao.findRepos(payload.getFullRepoName());
79+
// disable repos configured with the event branch
80+
repositories.stream()
81+
.filter(r -> !r.isDisabled() && null != r.getBranch())
82+
.filter(r -> r.getBranch().equals(payload.getBranch()))
83+
.forEach(r -> disableRepo(r, payload));
84+
}
85+
86+
List<TriggerEntry> triggers = listTriggers(projectId, payload.getOrg(), payload.getRepo());
87+
for (TriggerEntry t : triggers) {
88+
if (skipTrigger(t, eventName, payload)) {
89+
continue;
90+
}
91+
92+
Map<String, Object> event = buildEvent(eventName, uriInfo, payload);
93+
enrichEventConditions(payload, t, event);
94+
95+
if (DefaultEventFilter.filter(event, t)) {
96+
result.add(new Result(event, t));
97+
}
98+
}
99+
}
100+
101+
static boolean skipTrigger(TriggerEntry t, String eventName, Payload payload) {
102+
// skip empty push events if the trigger's configuration says so
103+
if (GithubUtils.ignoreEmptyPush(t) && GithubUtils.isEmptyPush(eventName, payload)) {
104+
return true;
105+
}
106+
107+
// process is destined to fail if attempted to start from commit in another repo
108+
// on an event from a pull request.
109+
if (TriggerUtils.isUseEventCommitId(t)
110+
&& payload.hasPullRequestEntry()
111+
&& payload.isPullRequestFromDifferentRepo()) {
112+
113+
log.info("Skip start from {} event [{}, {}] -> Commit is in a different repository.",
114+
eventName, payload.getPullRequestBaseUrl(), payload.getPullRequestHeadUrl());
115+
116+
return true;
117+
}
118+
119+
return false;
120+
}
121+
122+
private void enrichEventConditions(Payload payload, TriggerEntry trigger, Map<String, Object> result) {
123+
for (EventEnricher e : eventEnrichers) {
124+
e.enrich(payload, trigger, result);
125+
}
126+
}
127+
128+
private void disableRepo(RepositoryEntry repo, Payload payload) {
129+
log.info("disable repo ['{}', '{}'] -> ref deleted", repo.getId(), payload.getBranch());
130+
dao.disable(repo.getProjectId(), repo.getId());
131+
}
132+
133+
private static boolean isRefDeleted(Payload payload) {
134+
Object val = payload.raw().get("deleted");
135+
136+
if (val == null) {
137+
return false;
138+
}
139+
140+
if (val instanceof String str) {
141+
return Boolean.parseBoolean(str);
142+
}
29143

30-
public interface GithubTriggerProcessor {
144+
return Boolean.TRUE.equals(val);
145+
}
146+
147+
@WithTimer
148+
List<TriggerEntry> listTriggers(UUID projectId, String org, String repo) {
149+
return dao.listTriggers(projectId, org, repo);
150+
}
151+
152+
private Map<String, Object> buildEvent(String eventName, UriInfo uriInfo, Payload payload) {
153+
Map<String, Object> result = new HashMap<>();
154+
155+
result.put(GITHUB_ORG_KEY, payload.getOrg());
156+
result.put(GITHUB_REPO_KEY, payload.getRepo());
157+
result.put(GITHUB_HOST_KEY, payload.getHost());
158+
String branch = payload.getBranch();
159+
if (branch != null) {
160+
result.put(REPO_BRANCH_KEY, payload.getBranch());
161+
}
162+
163+
if (PULL_REQUEST_EVENT.equals(eventName)) {
164+
Map<String, Object> pullRequest = MapUtils.getMap(payload.raw(), PULL_REQUEST_EVENT, Collections.emptyMap());
165+
Map<String, Object> head = MapUtils.getMap(pullRequest, "head", Collections.emptyMap());
166+
String sha = MapUtils.getString(head, "sha");
167+
if (sha != null) {
168+
result.put(COMMIT_ID_KEY, sha);
169+
}
170+
} else if (PUSH_EVENT.equals(eventName)) {
171+
String after = payload.getString("after");
172+
if (after != null) {
173+
result.put(COMMIT_ID_KEY, after);
174+
}
175+
}
176+
177+
result.put(SENDER_KEY, payload.getSender());
178+
result.put(TYPE_KEY, eventName);
179+
result.put(STATUS_KEY, payload.getAction());
180+
result.put(PAYLOAD_KEY, payload.raw());
181+
result.put(QUERY_PARAMS_KEY, new HashMap<>(uriInfo.getQueryParameters()));
182+
183+
// files
184+
Map<String, Set<String>> files = new HashMap<>(payload.getFiles());
185+
// alias for all files (changed/modified/deleted)
186+
files.put("any", files.values().stream()
187+
.flatMap(Set::stream)
188+
.collect(Collectors.toSet()));
189+
result.put(FILES_KEY, files);
190+
191+
// match only with v2 triggers
192+
result.put(VERSION_KEY, VERSION_ID);
193+
194+
return result;
195+
}
196+
197+
public interface EventEnricher {
198+
199+
void enrich(Payload payload, TriggerEntry trigger, Map<String, Object> result);
200+
}
201+
202+
/**
203+
* Adds {@link Trigger#REPOSITORY_INFO} property to the event, but only if
204+
* the trigger's conditions contained the clause with the same key.
205+
*/
206+
@Named
207+
private static class RepositoryInfoEnricher implements EventEnricher {
31208

32-
void process(String eventName, Payload payload, UriInfo uriInfo, List<Result> result);
209+
private final Dao dao;
33210

34-
class Result {
211+
@Inject
212+
public RepositoryInfoEnricher(Dao dao) {
213+
this.dao = dao;
214+
}
215+
216+
@Override
217+
@WithTimer
218+
public void enrich(Payload payload, TriggerEntry trigger, Map<String, Object> result) {
219+
Object projectInfoConditions = trigger.getConditions().get(Trigger.REPOSITORY_INFO);
220+
if (projectInfoConditions == null || payload.getFullRepoName() == null) {
221+
return;
222+
}
223+
224+
List<Map<String, Object>> repositoryInfos = new ArrayList<>();
225+
List<RepositoryEntry> repositories = dao.findRepos(payload.getFullRepoName());
35226

36-
private final Map<String, Object> event;
227+
for (RepositoryEntry r : repositories) {
228+
if (r.isDisabled()) {
229+
continue;
230+
}
37231

38-
private final List<TriggerEntry> triggers;
232+
Map<String, Object> repositoryInfo = new HashMap<>();
233+
repositoryInfo.put(REPO_ID_KEY, r.getId());
234+
repositoryInfo.put(REPO_NAME_KEY, r.getName());
235+
repositoryInfo.put(PROJECT_ID_KEY, r.getProjectId());
236+
if (r.getBranch() != null) {
237+
repositoryInfo.put(REPO_BRANCH_KEY, r.getBranch());
238+
}
239+
repositoryInfo.put(REPO_ENABLED_KEY, !r.isDisabled());
39240

40-
public Result(Map<String, Object> event, List<TriggerEntry> triggers) {
41-
this.event = event;
42-
this.triggers = triggers;
241+
repositoryInfos.add(repositoryInfo);
242+
}
243+
244+
if (!repositoryInfos.isEmpty()) {
245+
result.put(Trigger.REPOSITORY_INFO, repositoryInfos);
246+
}
43247
}
248+
}
44249

45-
public Map<String, Object> event() {
46-
return event;
250+
@Named
251+
@Singleton
252+
public static class Dao {
253+
private final RepositoryDao repoDao;
254+
private final TriggersDao triggersDao;
255+
private final Configuration cfg;
256+
257+
@Inject
258+
public Dao(@MainDB Configuration cfg,
259+
RepositoryDao repoDao,
260+
TriggersDao triggersDao) {
261+
this.cfg = cfg;
262+
this.triggersDao = triggersDao;
263+
this.repoDao = repoDao;
47264
}
48265

49-
public List<TriggerEntry> triggers() {
50-
return triggers;
266+
private List<RepositoryEntry> findRepos(String repoOrgAndName) {
267+
String sshAndHttpPattern = "%[/:]" + repoOrgAndName + "(.git)?/?";
268+
return repoDao.findSimilar(sshAndHttpPattern);
51269
}
52270

53-
static Result from(Map<String, Object> event, TriggerEntry trigger) {
54-
return new Result(event, Collections.singletonList(trigger));
271+
List<TriggerEntry> listTriggers(UUID projectId, String org, String repo) {
272+
Map<String, String> conditions = new HashMap<>();
273+
274+
if (org != null) {
275+
conditions.put(GITHUB_ORG_KEY, org);
276+
}
277+
278+
if (repo != null) {
279+
conditions.put(GITHUB_REPO_KEY, repo);
280+
}
281+
282+
return triggersDao.list(projectId, EVENT_SOURCE, VERSION_ID, conditions);
55283
}
56284

57-
static Result from(Map<String, Object> event, List<TriggerEntry> triggers) {
58-
return new Result(event, triggers);
285+
void disable(UUID projectId, UUID repoId) {
286+
tx(tx -> {
287+
repoDao.disable(tx, repoId);
288+
triggersDao.delete(tx, projectId, repoId);
289+
});
290+
}
291+
292+
private DSLContext dsl() {
293+
return DSL.using(cfg);
294+
}
295+
296+
private void tx(Consumer<DSLContext> c) {
297+
dsl().transaction(localCfg -> {
298+
DSLContext tx = DSL.using(localCfg);
299+
c.accept(tx);
300+
});
301+
}
302+
}
303+
304+
public record Result(Map<String, Object> event, List<TriggerEntry> triggers) {
305+
306+
private Result(Map<String, Object> event, TriggerEntry trigger) {
307+
this(event, List.of(trigger));
59308
}
60309
}
61310
}

0 commit comments

Comments
 (0)