Simplify Slack-GitHub cache

This commit is contained in:
Pedro Pombeiro 2018-02-15 00:57:46 +01:00
parent 4513976cb9
commit a9eb8d117a
No known key found for this signature in database
GPG Key ID: A65DEB11E4BBC647
5 changed files with 173 additions and 165 deletions

View File

@ -18,20 +18,20 @@ const slackHelper = require('../lib/slack')
const botName = 'bounty-awaiting-approval-slack-ping'
module.exports = (robot, getSlackMentionFromGitHubId) => {
registerForNewBounties(robot, getSlackMentionFromGitHubId)
module.exports = (robot) => {
registerForNewBounties(robot)
}
function registerForNewBounties (robot, getSlackMentionFromGitHubId) {
function registerForNewBounties (robot) {
robot.on('issues.labeled', async context => {
// Make sure we don't listen to our own messages
if (context.isBot) return null
await notifyCollaborators(context, robot, getSlackMentionFromGitHubId)
await notifyCollaborators(context, robot)
})
}
async function notifyCollaborators (context, robot, getSlackMentionFromGitHubId) {
async function notifyCollaborators (context, robot) {
const { github, payload } = context
const ownerName = payload.repository.owner.login
const repoName = payload.repository.name
@ -57,12 +57,12 @@ async function notifyCollaborators (context, robot, getSlackMentionFromGitHubId)
robot.log(`${botName} - issue #${payload.issue.number} on ${ownerName}/${repoName} was labeled as a bounty awaiting approval. Pinging slack...`)
const slackCollaborators = await getSlackCollaborators(ownerName, repoName, github, robot, gitHubTeamConfig, getSlackMentionFromGitHubId)
const slackCollaborators = await getSlackCollaborators(ownerName, repoName, github, robot, gitHubTeamConfig)
// Mention the project board owner as well, if configured
const bountyProjectBoardOwner = bountyProjectBoardConfig['owner']
if (bountyProjectBoardOwner) {
const slackUserMention = getSlackMentionFromGitHubId(bountyProjectBoardOwner)
const slackUserMention = robot.gitHubIdMapper.getSlackMentionFromGitHubId(bountyProjectBoardOwner)
if (slackUserMention) {
slackCollaborators.push(slackUserMention)
}
@ -82,7 +82,7 @@ function randomInt (low, high) {
}
// Get the Slack IDs of the collaborators of this repo.
async function getSlackCollaborators (ownerName, repoName, github, robot, gitHubTeamConfig, getSlackMentionFromGitHubId) {
async function getSlackCollaborators (ownerName, repoName, github, robot, gitHubTeamConfig) {
const teamSlug = gitHubTeamConfig['slug']
if (!teamSlug) {
robot.log.debug(`${botName} - GitHub team slug not configured in repo ${ownerName}/${repoName}, ignoring`)
@ -100,12 +100,21 @@ async function getSlackCollaborators (ownerName, repoName, github, robot, gitHub
const teamMembers = await github.paginate(github.orgs.getTeamMembers({id: team.id, per_page: 100}), res => res.data)
// Create an array of Slack usernames from GitHub usernames
const slackUsers = teamMembers.map(u => u.login).map(getSlackMentionFromGitHubId).filter(id => id)
const gitHubUsers = teamMembers.map(u => u.login)
const slackUsers = new HashSet()
for (const gitHubUser of gitHubUsers) {
const id = await robot.gitHubIdMapper.getSlackMentionFromGitHubId(gitHubUser)
if (id) {
slackUsers.add(id)
}
}
// Select 2 random Slack team members
const randomTeamMemberLimit = 2
const selectedSlackUsers = new HashSet()
while (selectedSlackUsers.length < randomTeamMemberLimit || selectedSlackUsers.length < slackUsers.length) {
const slackUser = slackUsers[randomInt(0, slackUsers.length)]
while (selectedSlackUsers.size() < randomTeamMemberLimit || selectedSlackUsers.size() < slackUsers.size()) {
const slackUser = slackUsers[randomInt(0, slackUsers.size())]
selectedSlackUsers.add(slackUser)
}

View File

@ -11,24 +11,24 @@ const slackHelper = require('../lib/slack')
const botName = 'notify-reviewers-via-slack'
module.exports = (robot, getSlackIdFromGitHubId) => {
module.exports = (robot) => {
robot.log(`${botName} - Starting...`)
registerForNewReviewRequests(robot, getSlackIdFromGitHubId)
registerForNewReviewRequests(robot)
}
function registerForNewReviewRequests (robot, getSlackIdFromGitHubId) {
function registerForNewReviewRequests (robot) {
robot.on('pull_request.review_requested', async context => {
// Make sure we don't listen to our own messages
if (context.isBot) return null
await notifyReviewer(context, robot, getSlackIdFromGitHubId)
await notifyReviewer(context, robot)
})
}
async function notifyReviewer (context, robot, getSlackIdFromGitHubId) {
async function notifyReviewer (context, robot) {
const { payload } = context
const reviewer = payload.requested_reviewer
const userID = getSlackIdFromGitHubId(reviewer.login)
const userID = await robot.gitHubIdMapper.getSlackIdFromGitHubId(reviewer.login)
if (!userID) {
robot.log.warn('Could not find Slack ID for GitHub user', reviewer.login)

View File

@ -1,6 +1,3 @@
const MemCache = require('mem-cache')
const slackGitHubCache = new MemCache({ timeoutDisabled: true })
const SlackGitHubCacheBuilder = require('./lib/retrieve-slack-github-users')
const Slack = require('./lib/slack')
module.exports = async (robot) => {
@ -8,7 +5,7 @@ module.exports = async (robot) => {
Slack(robot, slack => {})
await new Promise((resolve, reject) => {
await new Promise(resolve => {
robot.on('slack.connected', event => {
robot.log.info(`Connected to Slack`)
@ -19,20 +16,15 @@ module.exports = async (robot) => {
})
})
const slackCachePromise = SlackGitHubCacheBuilder.build(robot, slackGitHubCache)
robot['gitHubIdMapper'] = require('./lib/github-id-mapper')(robot)
require('./bot_scripts/assign-new-pr-to-review')(robot)
require('./bot_scripts/assign-approved-pr-to-test')(robot)
require('./bot_scripts/assign-to-bounty-awaiting-for-approval')(robot)
require('./bot_scripts/greet-new-contributor')(robot)
require('./bot_scripts/trigger-automation-test-build')(robot)
await slackCachePromise
robot.log.info('Slack user ID cache populated, loading remainder of scripts')
// Add scripts which require using the Slack/GitHub cache after this comment
require('./bot_scripts/bounty-awaiting-approval-slack-ping')(robot, getSlackMentionFromGitHubId)
require('./bot_scripts/notify-reviewers-via-slack')(robot, getSlackIdFromGitHubId)
require('./bot_scripts/bounty-awaiting-approval-slack-ping')(robot)
require('./bot_scripts/notify-reviewers-via-slack')(robot)
// For more information on building apps:
// https://probot.github.io/docs/
@ -40,15 +32,3 @@ module.exports = async (robot) => {
// To get your app running against GitHub, see:
// https://probot.github.io/docs/development/
}
function getSlackMentionFromGitHubId (gitHubId) {
const id = SlackGitHubCacheBuilder.getSlackIdFromGitHubId(gitHubId, slackGitHubCache)
if (!id) {
return null
}
return `<@${id}>`
}
function getSlackIdFromGitHubId (gitHubId) {
return SlackGitHubCacheBuilder.getSlackIdFromGitHubId(gitHubId, slackGitHubCache)
}

View File

@ -1,33 +1,52 @@
// Description:
// Configuration-related functionality
// GitHub ID mapping to other connected systems (e.g. Slack)
//
// Dependencies:
// "mem-cache": "0.0.5"
// mem-cache: "0.0.5"
// @slack/client: "^3.16.0"
//
// Author:
// PombeirP
const MemCache = require('mem-cache')
const { WebClient } = require('@slack/client')
const token = process.env.SLACK_USER_TOKEN || ''
module.exports.build = async (robot, cache) => {
const web = new WebClient(token)
await populateCache(robot, web, cache)
module.exports = (robot) => new GitHubSlackIdMapper(robot)
class GitHubSlackIdMapper {
constructor (robot) {
this.robot = robot
this.cache = new MemCache({ timeoutDisabled: true })
this.buildPromise = new Promise((resolve, reject) => internalBuild(this.robot, this.cache).then(resolve).catch(reject))
}
module.exports.getGitHubIdFromSlackId = (slackUserId, cache) => {
async getGitHubIdFromSlackId (slackUserId, cache) {
await this.buildPromise
return cache.get(getSlackId2GitHubIdCacheKeyName(slackUserId))
}
module.exports.getSlackIdFromGitHubId = (gitHubId, cache) => {
return cache.get(getGitHub2SlackIdCacheKeyName(gitHubId))
async getSlackIdFromGitHubId (gitHubId) {
await this.buildPromise
return this.cache.get(getGitHub2SlackIdCacheKeyName(gitHubId))
}
async function populateCache (robot, web, cache) {
async getSlackMentionFromGitHubId (gitHubId) {
const id = await this.getSlackIdFromGitHubId(gitHubId)
if (!id) {
return null
}
return `<@${id}>`
}
}
async function internalBuild (robot, cache) {
robot.log.info('Populating Slack user ID cache...')
try {
const usersList = await web.users.list() // TODO: This call should be paginated to avoid hitting limits (memory, API): https://api.slack.com/docs/pagination#cursors
const slackWeb = new WebClient(token) // We need to use a different token because users.profile API is not available to bot users
const usersList = await slackWeb.users.list() // TODO: This call should be paginated to avoid hitting limits (memory, API): https://api.slack.com/docs/pagination#cursors
const activeUsersList = usersList.members.filter(u => !u.deleted && !u.is_bot && u.id !== 'USLACKBOT')
let gitHubFieldId = null
@ -41,7 +60,7 @@ async function populateCache (robot, web, cache) {
try {
++profileFetchBatchCount
const { profile } = await web.users.profile.get({ user: user.id, include_labels: !gitHubFieldId })
const { profile } = await slackWeb.users.profile.get({ user: user.id, include_labels: !gitHubFieldId })
const username = profile.display_name_normalized || profile.real_name_normalized
if (!gitHubFieldId) {

View File