wuffle 0.74.0 → 0.76.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.yml +1 -0
- package/lib/apps/auth-routes/AuthRoutes.js +16 -14
- package/lib/apps/automatic-dev-flow/AutomaticDevFlow.js +7 -8
- package/lib/apps/background-sync/BackgroundSync.js +30 -5
- package/lib/apps/board-api-routes/board-api-routes.js +33 -29
- package/lib/apps/dump-store/s3/S3.js +12 -0
- package/lib/apps/events-sync/EventsSync.js +20 -3
- package/lib/apps/github-app/GithubApp.js +31 -3
- package/lib/apps/github-checks/GithubChecks.js +2 -0
- package/lib/apps/github-comments/GithubComments.js +9 -78
- package/lib/apps/github-comments/GithubCommentsBackend.js +132 -0
- package/lib/apps/github-comments/index.js +3 -1
- package/lib/apps/github-issues/GithubIssues.js +161 -9
- package/lib/apps/github-reviews/GithubReviews.js +2 -0
- package/lib/apps/github-statuses/GithubStatuses.js +2 -0
- package/lib/apps/issue-filter/IssueFilter.js +5 -3
- package/lib/apps/search/Search.js +9 -5
- package/lib/apps/webhook-events/WebhookEvents.js +12 -5
- package/lib/events.js +2 -2
- package/lib/filters.js +4 -2
- package/lib/probot/CustomProbot.js +2 -1
- package/lib/util/links.js +16 -0
- package/lib/util/search.js +1 -1
- package/package.json +7 -7
- package/public/bundle.js +1 -1
- package/public/bundle.js.map +1 -1
package/app.yml
CHANGED
|
@@ -60,7 +60,7 @@ export default function AuthRoutes(logger, router, securityContext) {
|
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
const params = new URLSearchParams();
|
|
63
|
-
params.append('client_id', process.env.GITHUB_CLIENT_ID);
|
|
63
|
+
params.append('client_id', /** @type {string} */ (process.env.GITHUB_CLIENT_ID));
|
|
64
64
|
params.append('state', state);
|
|
65
65
|
params.append('redirect_uri', appUrl('/wuffle/login/callback'));
|
|
66
66
|
|
|
@@ -81,10 +81,9 @@ export default function AuthRoutes(logger, router, securityContext) {
|
|
|
81
81
|
|
|
82
82
|
log.debug({ session_id }, 'logging out');
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
})
|
|
87
|
-
|
|
84
|
+
req.session.destroy(function(err) {
|
|
85
|
+
res.redirect(redirectTo);
|
|
86
|
+
});
|
|
88
87
|
});
|
|
89
88
|
|
|
90
89
|
|
|
@@ -128,8 +127,8 @@ export default function AuthRoutes(logger, router, securityContext) {
|
|
|
128
127
|
const params = new URLSearchParams();
|
|
129
128
|
params.append('code', /** @type {string} */ (code));
|
|
130
129
|
params.append('state', /** @type {string} */ (state));
|
|
131
|
-
params.append('client_id', process.env.GITHUB_CLIENT_ID);
|
|
132
|
-
params.append('client_secret', process.env.GITHUB_CLIENT_SECRET);
|
|
130
|
+
params.append('client_id', /** @type {string} */ (process.env.GITHUB_CLIENT_ID));
|
|
131
|
+
params.append('client_secret', /** @type {string} */ (process.env.GITHUB_CLIENT_SECRET));
|
|
133
132
|
params.append('redirect_uri', appUrl('/wuffle/login/callback'));
|
|
134
133
|
|
|
135
134
|
const {
|
|
@@ -185,7 +184,9 @@ export default function AuthRoutes(logger, router, securityContext) {
|
|
|
185
184
|
} = session;
|
|
186
185
|
|
|
187
186
|
if (!githubUser) {
|
|
188
|
-
|
|
187
|
+
res.type('json').json(null);
|
|
188
|
+
|
|
189
|
+
return;
|
|
189
190
|
}
|
|
190
191
|
|
|
191
192
|
const {
|
|
@@ -216,17 +217,18 @@ export default function AuthRoutes(logger, router, securityContext) {
|
|
|
216
217
|
}, 'failed to check GitHub authentication');
|
|
217
218
|
|
|
218
219
|
// access is not granted anymore, clear current session
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
})
|
|
220
|
+
req.session.destroy(function(err) {
|
|
221
|
+
res.type('json').json(null);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return;
|
|
222
225
|
}
|
|
223
226
|
}
|
|
224
227
|
|
|
225
|
-
|
|
228
|
+
res.type('json').json({
|
|
226
229
|
login,
|
|
227
230
|
avatar_url
|
|
228
|
-
})
|
|
229
|
-
|
|
231
|
+
});
|
|
230
232
|
});
|
|
231
233
|
|
|
232
234
|
|
|
@@ -31,14 +31,13 @@ export default function(webhookEvents, githubIssues, columns, issueFilter, logge
|
|
|
31
31
|
'pull_request.closed'
|
|
32
32
|
], ifEnabled(async (context) => {
|
|
33
33
|
|
|
34
|
-
const {
|
|
35
|
-
pull_request,
|
|
36
|
-
issue
|
|
37
|
-
} = context.payload;
|
|
38
|
-
|
|
39
34
|
const column = columns.getByState(DONE);
|
|
40
35
|
|
|
41
|
-
|
|
36
|
+
const issueOrPull = 'issue' in context.payload
|
|
37
|
+
? context.payload.issue
|
|
38
|
+
: context.payload.pull_request;
|
|
39
|
+
|
|
40
|
+
await githubIssues.moveIssue(context, issueOrPull, column);
|
|
42
41
|
}));
|
|
43
42
|
|
|
44
43
|
webhookEvents.on('pull_request.converted_to_draft', ifEnabled(async (context) => {
|
|
@@ -107,7 +106,7 @@ export default function(webhookEvents, githubIssues, columns, issueFilter, logge
|
|
|
107
106
|
const newAssignee = (
|
|
108
107
|
process.env.AUTO_ASSIGN_PULLS && !external &&
|
|
109
108
|
author && author.type === 'User' && author.login
|
|
110
|
-
);
|
|
109
|
+
) || null;
|
|
111
110
|
|
|
112
111
|
await Promise.all([
|
|
113
112
|
githubIssues.moveIssue(context, pull_request, column, newAssignee),
|
|
@@ -172,7 +171,7 @@ export default function(webhookEvents, githubIssues, columns, issueFilter, logge
|
|
|
172
171
|
return;
|
|
173
172
|
}
|
|
174
173
|
|
|
175
|
-
const issue_number = match[1];
|
|
174
|
+
const issue_number = parseInt(match[1], 10);
|
|
176
175
|
|
|
177
176
|
const column = columns.getByState(IN_PROGRESS);
|
|
178
177
|
|
|
@@ -9,6 +9,17 @@ function isInternalError(error) {
|
|
|
9
9
|
/**
|
|
10
10
|
* This component performs a periodic background sync of a project.
|
|
11
11
|
*
|
|
12
|
+
* Unless disabled via `process.env.DISABLE_BACKGROUND_SYNC` it will
|
|
13
|
+
* register a recurring check.
|
|
14
|
+
*
|
|
15
|
+
* Background check performs various optimizations to ensure only relevant
|
|
16
|
+
* data is stored on the board:
|
|
17
|
+
*
|
|
18
|
+
* * Closed issues/PRs on the board will be thrashed
|
|
19
|
+
* * Closed issues/PRs wiil not be synchronized from GitHub
|
|
20
|
+
* * Open but stale issues/PRs will not be synchronized to the board
|
|
21
|
+
* * Open issue/PR details will only be synchronized for recent issues
|
|
22
|
+
*
|
|
12
23
|
* @constructor
|
|
13
24
|
*
|
|
14
25
|
* @param {Object} config
|
|
@@ -21,25 +32,25 @@ export default function BackgroundSync(config, logger, store, events, background
|
|
|
21
32
|
|
|
22
33
|
// 30 days
|
|
23
34
|
const syncClosedLookback = (
|
|
24
|
-
parseInt(process.env.BACKGROUND_SYNC_SYNC_CLOSED_LOOKBACK, 10) ||
|
|
35
|
+
parseInt(process.env.BACKGROUND_SYNC_SYNC_CLOSED_LOOKBACK || '', 10) ||
|
|
25
36
|
1000 * 60 * 60 * 24 * 30
|
|
26
37
|
);
|
|
27
38
|
|
|
28
39
|
// 4 hours
|
|
29
40
|
const syncClosedDetailsLookback = (
|
|
30
|
-
parseInt(process.env.BACKGROUND_SYNC_SYNC_CLOSED_DETAILS_LOOKBACK, 10) ||
|
|
41
|
+
parseInt(process.env.BACKGROUND_SYNC_SYNC_CLOSED_DETAILS_LOOKBACK || '', 10) ||
|
|
31
42
|
1000 * 60 * 60 * 4
|
|
32
43
|
);
|
|
33
44
|
|
|
34
45
|
// 1 day
|
|
35
46
|
const syncOpenDetailsLookback = (
|
|
36
|
-
parseInt(process.env.BACKGROUND_SYNC_SYNC_OPEN_DETAILS_LOOKBACK, 10) ||
|
|
47
|
+
parseInt(process.env.BACKGROUND_SYNC_SYNC_OPEN_DETAILS_LOOKBACK || '', 10) ||
|
|
37
48
|
1000 * 60 * 60 * 24
|
|
38
49
|
);
|
|
39
50
|
|
|
40
51
|
// 60 days
|
|
41
52
|
const removeClosedLookback = (
|
|
42
|
-
parseInt(process.env.BACKGROUND_SYNC_REMOVE_CLOSED_LOOKBACK, 10) ||
|
|
53
|
+
parseInt(process.env.BACKGROUND_SYNC_REMOVE_CLOSED_LOOKBACK || '', 10) ||
|
|
43
54
|
1000 * 60 * 60 * 24 * 60
|
|
44
55
|
);
|
|
45
56
|
|
|
@@ -393,6 +404,13 @@ We automatically synchronize all repositories you granted us access to via the G
|
|
|
393
404
|
return Promise.all(jobs);
|
|
394
405
|
}
|
|
395
406
|
|
|
407
|
+
/**
|
|
408
|
+
* Trigger background synchronization for all connected repositories.
|
|
409
|
+
*
|
|
410
|
+
* This ensures that data out-of-sync with the board is fetched from remote.
|
|
411
|
+
*
|
|
412
|
+
* @return {Promise<void>}
|
|
413
|
+
*/
|
|
396
414
|
async function backgroundSync() {
|
|
397
415
|
|
|
398
416
|
log.info('start');
|
|
@@ -410,7 +428,7 @@ We automatically synchronize all repositories you granted us access to via the G
|
|
|
410
428
|
}
|
|
411
429
|
|
|
412
430
|
const syncInterval = (
|
|
413
|
-
parseInt(process.env.BACKGROUND_SYNC_SYNC_INTERVAL, 10) || (
|
|
431
|
+
parseInt(process.env.BACKGROUND_SYNC_SYNC_INTERVAL || '', 10) || (
|
|
414
432
|
process.env.NODE_ENV !== 'production'
|
|
415
433
|
|
|
416
434
|
// five minutes
|
|
@@ -442,6 +460,13 @@ We automatically synchronize all repositories you granted us access to via the G
|
|
|
442
460
|
|
|
443
461
|
// api ///////////////////
|
|
444
462
|
|
|
463
|
+
/**
|
|
464
|
+
* Trigger background synchronization for all connected repositories.
|
|
465
|
+
*
|
|
466
|
+
* This ensures that data out-of-sync with the board is fetched from remote.
|
|
467
|
+
*
|
|
468
|
+
* @return {Promise<void>}
|
|
469
|
+
*/
|
|
445
470
|
this.backgroundSync = backgroundSync;
|
|
446
471
|
|
|
447
472
|
|
|
@@ -174,17 +174,17 @@ export default async function BoardApiRoutes(
|
|
|
174
174
|
|
|
175
175
|
return filterBoardItems(req, items).then(filteredItems => {
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
res.type('json').json({
|
|
178
178
|
items: filteredItems,
|
|
179
179
|
cursor
|
|
180
|
-
})
|
|
180
|
+
});
|
|
181
181
|
}).catch(err => {
|
|
182
182
|
log.error({
|
|
183
183
|
err,
|
|
184
184
|
cursor
|
|
185
185
|
}, 'failed to retrieve cards');
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
res.status(500).json({ error : true });
|
|
188
188
|
});
|
|
189
189
|
});
|
|
190
190
|
|
|
@@ -196,7 +196,7 @@ export default async function BoardApiRoutes(
|
|
|
196
196
|
name
|
|
197
197
|
} = config;
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
res.type('json').json({
|
|
200
200
|
columns: columns.map(c => {
|
|
201
201
|
const { name, collapsed } = c;
|
|
202
202
|
|
|
@@ -206,7 +206,7 @@ export default async function BoardApiRoutes(
|
|
|
206
206
|
};
|
|
207
207
|
}),
|
|
208
208
|
name: name || 'Wuffle Board'
|
|
209
|
-
})
|
|
209
|
+
});
|
|
210
210
|
|
|
211
211
|
});
|
|
212
212
|
|
|
@@ -216,18 +216,16 @@ export default async function BoardApiRoutes(
|
|
|
216
216
|
|
|
217
217
|
const updates = cursor ? store.getUpdates(cursor) : [];
|
|
218
218
|
|
|
219
|
-
return (
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
})
|
|
230
|
-
);
|
|
219
|
+
return filterUpdates(req, updates).then(filteredUpdates => {
|
|
220
|
+
res.type('json').json(filteredUpdates);
|
|
221
|
+
}).catch(err => {
|
|
222
|
+
log.error({
|
|
223
|
+
err,
|
|
224
|
+
cursor
|
|
225
|
+
}, 'failed to retrieve card updates');
|
|
226
|
+
|
|
227
|
+
res.status(500).json({ error : true });
|
|
228
|
+
});
|
|
231
229
|
});
|
|
232
230
|
|
|
233
231
|
|
|
@@ -236,7 +234,9 @@ export default async function BoardApiRoutes(
|
|
|
236
234
|
const user = authRoutes.getGitHubUser(req);
|
|
237
235
|
|
|
238
236
|
if (!user) {
|
|
239
|
-
|
|
237
|
+
res.status(401).json({});
|
|
238
|
+
|
|
239
|
+
return;
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
const body = JSON.parse(req.body);
|
|
@@ -251,13 +251,17 @@ export default async function BoardApiRoutes(
|
|
|
251
251
|
const issue = await store.getIssueById(id);
|
|
252
252
|
|
|
253
253
|
if (!issue) {
|
|
254
|
-
|
|
254
|
+
res.status(404).json({});
|
|
255
|
+
|
|
256
|
+
return;
|
|
255
257
|
}
|
|
256
258
|
|
|
257
259
|
const column = columns.getByName(columnName);
|
|
258
260
|
|
|
259
261
|
if (!column) {
|
|
260
|
-
|
|
262
|
+
res.status(404).json({});
|
|
263
|
+
|
|
264
|
+
return;
|
|
261
265
|
}
|
|
262
266
|
|
|
263
267
|
const repo = repoAndOwner(issue);
|
|
@@ -265,7 +269,9 @@ export default async function BoardApiRoutes(
|
|
|
265
269
|
const canWrite = await userAccess.canWrite(user, repo);
|
|
266
270
|
|
|
267
271
|
if (!canWrite) {
|
|
268
|
-
|
|
272
|
+
res.status(403).json({});
|
|
273
|
+
|
|
274
|
+
return;
|
|
269
275
|
}
|
|
270
276
|
|
|
271
277
|
const octokit = await githubClient.getUserScoped(user);
|
|
@@ -280,15 +286,13 @@ export default async function BoardApiRoutes(
|
|
|
280
286
|
}
|
|
281
287
|
};
|
|
282
288
|
|
|
283
|
-
return (
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
log.error(err, 'failed to move issue');
|
|
289
|
+
return moveIssue(context, issue, column, { before, after }).then(() => {
|
|
290
|
+
res.type('json').json({});
|
|
291
|
+
}).catch(err => {
|
|
292
|
+
log.error(err, 'failed to move issue');
|
|
288
293
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
) && null;
|
|
294
|
+
res.status(500).json({ error : true });
|
|
295
|
+
});
|
|
292
296
|
|
|
293
297
|
});
|
|
294
298
|
|
|
@@ -16,6 +16,18 @@ export default function S3() {
|
|
|
16
16
|
S3_ENDPOINT: endpoint
|
|
17
17
|
} = process.env;
|
|
18
18
|
|
|
19
|
+
if (!accessKeyId) {
|
|
20
|
+
throw new Error('process.env.AWS_ACCESS_KEY_ID required');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!secretAccessKey) {
|
|
24
|
+
throw new Error('process.env.AWS_SECRET_ACCESS_KEY required');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!bucket) {
|
|
28
|
+
throw new Error('process.env.S3_BUCKET required');
|
|
29
|
+
}
|
|
30
|
+
|
|
19
31
|
const s3client = new S3Client({
|
|
20
32
|
region,
|
|
21
33
|
endpoint,
|
|
@@ -227,12 +227,29 @@ export default function EventsSync(webhookEvents, store, logger) {
|
|
|
227
227
|
});
|
|
228
228
|
|
|
229
229
|
|
|
230
|
-
// issues
|
|
230
|
+
// sub-issues /////////////////////
|
|
231
231
|
|
|
232
|
-
// https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/webhook-events-and-payloads#issues
|
|
233
232
|
|
|
234
|
-
//
|
|
233
|
+
// https://docs.github.com/en/webhooks/webhook-events-and-payloads#sub_issues
|
|
234
|
+
//
|
|
235
|
+
// update sub-issues on change - updating parent <> child relationship
|
|
236
|
+
webhookEvents.on(
|
|
237
|
+
/** @type {any} */ ([ 'sub_issues.sub_issue_added', 'sub_issues.sub_issue_removed' ]),
|
|
238
|
+
async ({ payload }) => {
|
|
239
|
+
|
|
240
|
+
const {
|
|
241
|
+
sub_issue,
|
|
242
|
+
repository
|
|
243
|
+
} = /** @type {any} */ (payload);
|
|
244
|
+
|
|
245
|
+
return store.updateIssue(filterIssue(sub_issue, repository));
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
// https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/webhook-events-and-payloads#issues
|
|
235
251
|
//
|
|
252
|
+
// issue transfer is mapped to the following GitHub events:
|
|
236
253
|
// -> issues.opened (new issue is being opened by GitHub)
|
|
237
254
|
// -> issues.transferred (old issue was deleted by GitHub)
|
|
238
255
|
//
|
|
@@ -19,11 +19,13 @@ const RequiredEvents = [
|
|
|
19
19
|
'issues',
|
|
20
20
|
'issue_comment',
|
|
21
21
|
'label',
|
|
22
|
+
'member',
|
|
22
23
|
'milestone',
|
|
23
24
|
'pull_request',
|
|
24
25
|
'pull_request_review',
|
|
25
26
|
'repository',
|
|
26
|
-
'status'
|
|
27
|
+
'status',
|
|
28
|
+
'sub_issues'
|
|
27
29
|
];
|
|
28
30
|
|
|
29
31
|
/**
|
|
@@ -41,8 +43,9 @@ const RequiredEvents = [
|
|
|
41
43
|
* @param {import('../../types.js').ProbotApp} app
|
|
42
44
|
* @param {import('../../types.js').Logger} logger
|
|
43
45
|
* @param {import('../../types.js').Injector} injector
|
|
46
|
+
* @param {import('../../events.js').default} events
|
|
44
47
|
*/
|
|
45
|
-
export default function GithubApp(config, app, logger, injector) {
|
|
48
|
+
export default function GithubApp(config, app, logger, injector, events) {
|
|
46
49
|
|
|
47
50
|
const log = logger.child({
|
|
48
51
|
name: 'wuffle:github-app'
|
|
@@ -266,6 +269,31 @@ export default function GithubApp(config, app, logger, injector) {
|
|
|
266
269
|
log.debug('validated installations');
|
|
267
270
|
}
|
|
268
271
|
|
|
272
|
+
async function validateApp() {
|
|
273
|
+
const octokit = await getAppScopedClient();
|
|
274
|
+
const { data: app } = await octokit.rest.apps.getAuthenticated();
|
|
275
|
+
|
|
276
|
+
// app may not be configured yet
|
|
277
|
+
if (!app) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const missingEvents = RequiredEvents.filter(
|
|
282
|
+
event => !app.events.includes(event)
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
if (missingEvents.length) {
|
|
286
|
+
log.error({
|
|
287
|
+
missingEvents,
|
|
288
|
+
events: app.events
|
|
289
|
+
}, 'app is missing required event subscriptions; update app settings on GitHub');
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
events.once('wuffle.start', async function() {
|
|
294
|
+
await validateApp().catch(err => log.warn({ err }, 'failed to validate app configuration'));
|
|
295
|
+
});
|
|
296
|
+
|
|
269
297
|
/**
|
|
270
298
|
* Fetch active installations.
|
|
271
299
|
*
|
|
@@ -312,7 +340,7 @@ export default function GithubApp(config, app, logger, injector) {
|
|
|
312
340
|
/**
|
|
313
341
|
* Get an installation for the given id.
|
|
314
342
|
*
|
|
315
|
-
* @param {string} id
|
|
343
|
+
* @param {string|number} id
|
|
316
344
|
*
|
|
317
345
|
* @return {Promise<Installation | undefined>}
|
|
318
346
|
*/
|
|
@@ -3,6 +3,8 @@ import { repoAndOwner, Cache } from '../../util/index.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* This component updates the stored issues based on GitHub events.
|
|
5
5
|
*
|
|
6
|
+
* It also hooks into `BackgroundSync` to fetch checks for a PR.
|
|
7
|
+
*
|
|
6
8
|
* @constructor
|
|
7
9
|
*
|
|
8
10
|
* @param {import('../webhook-events/WebhookEvents.js').default} webhookEvents
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { repoAndOwner } from '../../util/index.js';
|
|
2
1
|
import { filterUser, filterIssue } from '../../filters.js';
|
|
3
|
-
import gql from 'fake-tag';
|
|
4
2
|
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
5
|
* This component updates the stored issues based on GitHub events.
|
|
8
6
|
*
|
|
7
|
+
* It also hooks into `BackgroundSync` to fetch comments for issues/PRs.
|
|
8
|
+
*
|
|
9
9
|
* @constructor
|
|
10
10
|
*
|
|
11
11
|
* @param {import('../webhook-events/WebhookEvents.js').default} webhookEvents
|
|
12
12
|
* @param {import('../../events.js').default} events
|
|
13
|
-
* @param {import('../github-client/GithubClient.js').default} githubClient
|
|
14
13
|
* @param {import('../../store.js').default} store
|
|
15
14
|
* @param {import('../issue-filter/IssueFilter.js').default} issueFilter
|
|
16
15
|
* @param {import('../../types.js').Logger } logger
|
|
16
|
+
* @param {import('./GithubCommentsBackend.js').default} githubCommentsBackend
|
|
17
17
|
*/
|
|
18
|
-
export default function GithubComments(webhookEvents, events,
|
|
18
|
+
export default function GithubComments(webhookEvents, events, store, issueFilter, logger, githubCommentsBackend) {
|
|
19
19
|
|
|
20
20
|
const log = logger.child({
|
|
21
21
|
name: 'wuffle:github-comments'
|
|
@@ -32,81 +32,10 @@ export default function GithubComments(webhookEvents, events, githubClient, stor
|
|
|
32
32
|
} = event;
|
|
33
33
|
|
|
34
34
|
const {
|
|
35
|
-
id
|
|
36
|
-
number
|
|
35
|
+
id
|
|
37
36
|
} = issue;
|
|
38
37
|
|
|
39
|
-
const
|
|
40
|
-
repo,
|
|
41
|
-
owner
|
|
42
|
-
} = repoAndOwner(issue);
|
|
43
|
-
|
|
44
|
-
const github = await githubClient.getOrgScoped(owner);
|
|
45
|
-
|
|
46
|
-
const result = await github.graphql(gql`
|
|
47
|
-
|
|
48
|
-
fragment CommentInfo on IssueComment {
|
|
49
|
-
id: databaseId
|
|
50
|
-
node_id: id
|
|
51
|
-
body: bodyText
|
|
52
|
-
created_at: publishedAt
|
|
53
|
-
authorAssociation,
|
|
54
|
-
html_url: url,
|
|
55
|
-
user: author {
|
|
56
|
-
login
|
|
57
|
-
avatar_url: avatarUrl,
|
|
58
|
-
html_url: url
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
query FetchComments(
|
|
63
|
-
$repo: String!,
|
|
64
|
-
$owner: String!,
|
|
65
|
-
$issue_number: Int!,
|
|
66
|
-
$after: String
|
|
67
|
-
) {
|
|
68
|
-
repository(name: $repo, owner: $owner) {
|
|
69
|
-
issueOrPullRequest(number: $issue_number) {
|
|
70
|
-
...on Issue {
|
|
71
|
-
comments(first: 100, after: $after) {
|
|
72
|
-
edges {
|
|
73
|
-
node {
|
|
74
|
-
...CommentInfo
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
pageInfo {
|
|
78
|
-
endCursor
|
|
79
|
-
hasNextPage
|
|
80
|
-
}
|
|
81
|
-
totalCount
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
...on PullRequest {
|
|
85
|
-
comments(first: 100, after: $after) {
|
|
86
|
-
edges {
|
|
87
|
-
node {
|
|
88
|
-
...CommentInfo
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
pageInfo {
|
|
92
|
-
endCursor
|
|
93
|
-
hasNextPage
|
|
94
|
-
}
|
|
95
|
-
totalCount
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}`,
|
|
101
|
-
{
|
|
102
|
-
owner,
|
|
103
|
-
repo,
|
|
104
|
-
issue_number: number
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const comments = (
|
|
108
|
-
result.repository.issueOrPullRequest.comments.edges.map(e => e.node)
|
|
109
|
-
);
|
|
38
|
+
const comments = await githubCommentsBackend.getIssueComments(issue);
|
|
110
39
|
|
|
111
40
|
await store.queueUpdate({
|
|
112
41
|
id,
|
|
@@ -115,7 +44,9 @@ export default function GithubComments(webhookEvents, events, githubClient, stor
|
|
|
115
44
|
});
|
|
116
45
|
|
|
117
46
|
webhookEvents.on([
|
|
118
|
-
'issue_comment'
|
|
47
|
+
'issue_comment.created',
|
|
48
|
+
'issue_comment.edited',
|
|
49
|
+
'issue_comment.deleted'
|
|
119
50
|
], ifEnabled(async ({ payload }) => {
|
|
120
51
|
const {
|
|
121
52
|
action,
|