wuffle
Version:
A multi-repository task board for GitHub issues
277 lines (211 loc) • 5.94 kB
JavaScript
import {
filterIssue,
filterPull,
filterIssueOrPull,
filterRepository,
getIdentifier,
getKey
} from '../filters.js';
/**
* This component updates the stored issues based on GitHub events.
*
* @constructor
*
* @param {import('./webhook-events/WebhookEvents.js').default} webhookEvents
* @param {import('../store.js').default} store
* @param {import('../types.js').Logger} logger
*/
export default function EventsSync(webhookEvents, store, logger) {
const log = logger.child({
name: 'wuffle:user-access'
});
// issues /////////////////////
webhookEvents.on([
'issues.opened',
'issues.reopened',
'issues.labeled',
'issues.unlabeled',
'issues.assigned',
'issues.unassigned',
'issues.edited',
'issues.closed'
], async ({ payload }) => {
const {
issue,
repository
} = payload;
return store.updateIssue(filterIssue(issue, repository));
});
// available for issues only, we must manually
// fetch the related pull request
webhookEvents.on([
'issues.milestoned',
'issues.demilestoned'
], async ({ payload }) => {
const {
issue,
repository
} = payload;
return store.updateIssue(filterIssueOrPull(issue, repository));
});
webhookEvents.on([
'issues.deleted'
], async ({ payload }) => {
const {
issue,
repository
} = payload;
const id = getIdentifier(issue, repository);
return store.removeIssueById(id);
});
// issue_comment ///////////////////////
// https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/webhook-events-and-payloads#issue_comment
webhookEvents.on([
'issue_comment.created',
'issue_comment.edited'
], async ({ payload }) => {
const {
issue,
repository
} = payload;
// necro bump issue on comment
return store.updateIssue(filterIssueOrPull(issue, repository));
});
// pull requests //////////////////
webhookEvents.on([
'pull_request.opened',
'pull_request.reopened',
'pull_request.labeled',
'pull_request.unlabeled',
'pull_request.edited',
'pull_request.ready_for_review',
'pull_request.converted_to_draft',
'pull_request.assigned',
'pull_request.unassigned',
'pull_request.synchronize',
'pull_request.closed',
'pull_request.review_requested',
'pull_request.review_request_removed'
], async ({ payload }) => {
const {
pull_request,
repository
} = payload;
return store.updateIssue(filterPull(pull_request, repository));
});
// milestones /////////////////////
webhookEvents.on([
'milestone.edited'
], async ({ payload }) => {
const {
milestone
} = payload;
await store.updateIssues(issue => {
if (issue.milestone && issue.milestone.id === milestone.id) {
return {
milestone
};
}
});
});
// labels ///////////////////////
webhookEvents.on([
'label.edited'
], async ({ payload }) => {
const {
label
} = payload;
await store.updateIssues(issue => {
const issueLabels = issue.labels || [];
const existingIndex = issueLabels.findIndex(l => l.id === label.id);
if (existingIndex !== -1) {
return {
labels: [
...issueLabels.slice(0, existingIndex),
label,
...issueLabels.slice(existingIndex + 1)
]
};
}
});
});
webhookEvents.on([
'label.deleted'
], async ({ payload }) => {
const {
label
} = payload;
await store.updateIssues(issue => {
const issueLabels = issue.labels || [];
const existingIndex = issueLabels.findIndex(l => l.id === label.id);
if (existingIndex !== -1) {
return {
labels: [
...issueLabels.slice(0, existingIndex),
...issueLabels.slice(existingIndex + 1)
]
};
}
});
});
// repository ///////////////////////
// https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/webhook-events-and-payloads#repository
webhookEvents.on([
'repository.renamed'
], async ({ payload }) => {
const {
repository
} = payload;
await store.updateIssues(issue => {
if (issue.repository.id === repository.id) {
return {
key: getKey(issue, repository),
repository: filterRepository(repository)
};
}
});
});
webhookEvents.on([
'repository.transferred'
], async ({ payload }) => {
// rename issues in repository
});
// issues ///////////////////////////////
// https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/webhook-events-and-payloads#issues
// issue transfer is mapped to the following GitHub events
//
// -> issues.opened (new issue is being opened by GitHub)
// -> issues.transferred (old issue was deleted by GitHub)
//
// Labels are not taken over during the transfer, thus we cannot retain
// the column mapping. We do, however retain the order.
//
webhookEvents.on([
'issues.transferred'
], async ({ payload }) => {
const {
issue,
repository,
changes
} = payload;
const {
new_issue: newIssue,
new_repository: newRepository
} = changes;
const storedIssue = store.getIssueById(getIdentifier(issue, repository));
if (!storedIssue) {
log.warn({ issue: issue.id }, 'stored original issue not found');
return;
}
const newStoredIssue = store.getIssueById(getIdentifier(newIssue, newRepository));
if (newStoredIssue) {
await store.updateIssue({
id: newStoredIssue.id,
order: storedIssue.order
});
} else {
log.warn({ issue: newIssue.id }, 'stored transferred issue not found');
}
return store.removeIssueById(storedIssue.id);
});
}