hubot-phabs
Version:
A hubot plugin for communication with a Phabricator instance.
424 lines (374 loc) • 13 kB
text/coffeescript
# Description:
# requests Phabricator Conduit api
#
# Dependencies:
#
# Configuration:
# PHABRICATOR_URL
# PHABRICATOR_API_KEY
# PHABRICATOR_BOT_PHID
# PHABRICATOR_TRUSTED_USERS
#
# Author:
# mose
querystring = require 'querystring'
moment = require 'moment'
class Phabricator
statuses: {
'open': 'open',
'opened': 'open',
'resolved': 'resolved',
'resolve': 'resolved',
'closed': 'resolved',
'close': 'resolved',
'wontfix': 'wontfix',
'noway': 'wontfix',
'invalid': 'invalid',
'rejected': 'invalid',
'spite': 'spite',
'lame': 'spite'
}
priorities: {
'unbreak': 100,
'broken': 100,
'need triage': 90,
'none': 90,
'unknown': 90,
'low': 25,
'normal': 50,
'high': 80,
'urgent': 80,
'wish': 0
}
constructor: (, env) ->
= env.PHABRICATOR_URL
= env.PHABRICATOR_API_KEY
storageLoaded = =>
= .brain.data.phabricator ||= {
projects: { },
aliases: { },
bot_phid: env.PHABRICATOR_BOT_PHID
}
.logger.debug 'Phabricator Data Loaded: ' + JSON.stringify(, null, 2)
.brain.on 'loaded', storageLoaded
storageLoaded() # just in case storage was loaded before we got here
ready: ->
.logger.error 'Error: Phabricator url is not specified' if not
.logger.error 'Error: Phabricator api key is not specified' if not
return false unless ( and )
true
phabGet: (query, endpoint, cb) =>
query['api.token'] = process.env.PHABRICATOR_API_KEY
# console.log query
body = querystring.stringify(query)
.http(process.env.PHABRICATOR_URL)
.path("api/#{endpoint}")
.get(body) (err, res, payload) ->
json_body = null
if res?
switch res.statusCode
when 200
if res.headers['content-type'] is 'application/json'
json_body = JSON.parse(payload)
else
json_body = {
result: { },
error_code: 'ENOTJSON',
error_info: 'api did not deliver json'
}
else
json_body = {
result: { },
error_code: res.statusCode,
error_info: "http error #{res.statusCode}"
}
else
json_body = {
result: { },
error_code: err.code,
error_info: err.message
}
cb json_body
withBotPHID: (cb) =>
if .bot_phid
cb .bot_phid
else
'', 'user.whoami', (json_body) =>
.bot_phid = json_body.result.phid
cb .bot_phid
withFeed: (payload, cb) =>
# console.log payload.storyData
if /^PHID-TASK-/.test payload.storyData.objectPHID
query = {
'constraints[phids][0]': payload.storyData.objectPHID,
'attachments[projects]': 1
}
data =
query, 'maniphest.search', (json_body) ->
announces = {
message: payload.storyText
}
announces.rooms = []
if json_body.result.data?
for phid in json_body.result.data[0].attachments.projects.projectPHIDs
for name, project of data.projects
if project.phid? and phid is project.phid
project.feeds ?= [ ]
for room in project.feeds
if announces.rooms.indexOf(room) is -1
announces.rooms.push room
cb announces
else
cb { rooms: [ ] }
withProject: (project, cb) =>
if .projects[project]?
projectData = .projects[project]
projectData.name = project
else
for a, p of .aliases
if a is project and .projects[p]?
projectData = .projects[p]
projectData.name = p
break
aliases = []
if projectData?
for a, p of .aliases
if p is projectData.name
aliases.push a
if projectData.phid?
cb { aliases: aliases, data: projectData }
else
query = { 'names[0]': projectData.name }
query, 'project.query', (json_body) ->
if Object.keys(json_body.result.data).length > 0
projectData.phid = Object.keys(json_body.result.data)[0]
cb { aliases: aliases, data: projectData }
else
cb { error_info: "Sorry, #{project} not found." }
else
data =
query = { 'names[0]': project }
query, 'project.query', (json_body) ->
if json_body.result.data.length > 0 or Object.keys(json_body.result.data).length > 0
phid = Object.keys(json_body.result.data)[0]
data.projects[project] = { phid: phid }
projectData = {
name: json_body.result.data[phid].name
}
cb { aliases: aliases, data: projectData }
else
cb { error_info: "Sorry, #{project} not found." }
withUser: (from, user, cb) =>
if is true
id = user.phid
if id
cb(id)
else
email = user.email_address or user.pagerdutyEmail
unless email
if from.name is user.name
cb {
error_info: "Sorry, I can't figure out your email address :( " +
'Can you tell me with `.phab me as you@yourdomain.com`?'
}
else
cb {
error_info: "Sorry, I can't figure #{user.name} email address. " +
"Can you help me with .phab #{user.name} = <email>"
}
else
query = { 'emails[0]': email }
query, 'user.query', (json_body) ->
unless json_body['result']['0']?
cb {
error_info: "Sorry, I cannot find #{email} :("
}
else
user.phid = json_body['result']['0']['phid']
cb user.phid
withUserByPhid: (phid, cb) =>
if phid?
user = null
for k of .brain.data.users
thisphid = .brain.data.users[k].phid
if thisphid? and thisphid is phid
user = .brain.data.users[k]
break
if user?
cb user
else
query = { 'phids[0]': phid }
query, 'user.query', (json_body) ->
if json_body['result']['0']?
cb { name: json_body['result']['0']['userName'] }
else
cb { name: 'unknown' }
else
cb { name: 'nobody' }
withPermission: (msg, user, group, cb) =>
user = .brain.userForName user.name
if group is 'phuser' and process.env.PHABRICATOR_TRUSTED_USERS is 'y'
isAuthorized = true
else
isAuthorized = .auth?.hasRole(user, [group, 'phadmin']) or
.auth?.isAdmin(user)
if .auth? and not isAuthorized
msg.reply "You don't have permission to do that."
msg.finish()
else
cb()
taskInfo: (id, cb) ->
if is true
query = { 'task_id': id }
query, 'maniphest.info', (json_body) ->
cb json_body
fileInfo: (id, cb) ->
if is true
query = { 'id': id }
query, 'file.info', (json_body) ->
cb json_body
pasteInfo: (id, cb) ->
if is true
query = { 'ids[0]': id }
query, 'paste.query', (json_body) ->
cb json_body
genericInfo: (name, cb) ->
if is true
query = { 'names[]': name }
query, 'phid.lookup', (json_body) ->
cb json_body
searchTask: (phid, terms, cb) ->
if is true
query = {
'constraints[fulltext]': terms,
'constraints[statuses][0]': 'open',
'constraints[projects][0]': phid,
'order': 'newest',
'limit': '3'
}
# console.log query
query, 'maniphest.search', (json_body) ->
cb json_body
createTask: (user, phid, title, description, cb) ->
if is true
adapter = .adapterName
user = .brain.userForName user.name
user, user, (userPhid) =>
if userPhid.error_info?
cb userPhid
else
(bot_phid) =>
query = {
'transactions[0][type]': 'title',
'transactions[0][value]': "#{title}",
'transactions[1][type]': 'comment',
'transactions[1][value]': "(created by #{user.name} on #{adapter})",
'transactions[2][type]': 'subscribers.add',
'transactions[2][value][0]': "#{userPhid}",
'transactions[3][type]': 'subscribers.remove',
'transactions[3][value][0]': "#{bot_phid}",
'transactions[4][type]': 'projects.add',
'transactions[4][value][]': "#{phid}"
}
if description?
query['transactions[5][type]'] = 'description'
query['transactions[5][value]'] = "#{description}"
query, 'maniphest.edit', (json_body) ->
cb json_body
createPaste: (user, title, cb) ->
if is true
bot_phid = .brain.data.phabricator.bot_phid
adapter = .adapterName
user = .brain.userForName user.name
user, user, (userPhid) =>
if userPhid.error_info?
cb userPhid
else
query = {
'transactions[0][type]': 'title',
'transactions[0][value]': "#{title}",
'transactions[1][type]': 'text',
'transactions[1][value]': "(created by #{user.name} on #{adapter})",
'transactions[2][type]': 'subscribers.add',
'transactions[2][value][0]': "#{userPhid}",
'transactions[3][type]': 'subscribers.remove',
'transactions[3][value][0]': "#{bot_phid}"
}
query, 'paste.edit', (json_body) ->
cb json_body
recordPhid: (user, id) ->
user.lastTask = moment().utc()
user.lastPhid = id
retrievePhid: (user) ->
expires_at = moment(user.lastTask).add(5, 'minutes')
if user.lastPhid? and moment().utc().isBefore(expires_at)
user.lastPhid
else
null
addComment: (user, id, comment, cb) ->
if is true
query = {
'objectIdentifier': id,
'transactions[0][type]': 'comment',
'transactions[0][value]': "#{comment} (#{user.name})",
'transactions[1][type]': 'subscribers.remove',
'transactions[1][value][0]': "#{@robot.brain.data.phabricator.bot_phid}"
}
query, 'maniphest.edit', (json_body) ->
cb json_body
updateStatus: (user, id, status, comment, cb) ->
if is true
query = {
'objectIdentifier': id,
'transactions[0][type]': 'status',
'transactions[0][value]': [status],
'transactions[1][type]': 'subscribers.remove',
'transactions[1][value][0]': "#{@robot.brain.data.phabricator.bot_phid}",
'transactions[2][type]': 'owner',
'transactions[2][value]': user.phid,
'transactions[3][type]': 'comment'
}
if comment?
query['transactions[3][value]'] = "#{comment} (#{user.name})"
else
query['transactions[3][value]'] = "status set to #{status} by #{user.name}"
query, 'maniphest.edit', (json_body) ->
cb json_body
updatePriority: (user, id, priority, comment, cb) ->
if is true
query = {
'objectIdentifier': id,
'transactions[0][type]': 'priority',
'transactions[0][value]': [priority],
'transactions[1][type]': 'subscribers.remove',
'transactions[1][value][0]': "#{@robot.brain.data.phabricator.bot_phid}",
'transactions[2][type]': 'owner',
'transactions[2][value]': user.phid,
'transactions[3][type]': 'comment'
}
if comment?
query['transactions[3][value]'] = "#{comment} (#{user.name})"
else
query['transactions[3][value]'] = "priority set to #{priority} by #{user.name}"
query, 'maniphest.edit', (json_body) ->
cb json_body
assignTask: (tid, userphid, cb) ->
if is true
query = {
'objectIdentifier': "T#{tid}",
'transactions[0][type]': 'owner',
'transactions[0][value]': "#{userphid}",
'transactions[1][type]': 'subscribers.remove',
'transactions[1][value][0]': "#{@bot_phid}"
}
query, 'maniphest.edit', (json_body) ->
cb json_body
listTasks: (projphid, cb) ->
if is true
query = {
'projectPHIDs[0]': "#{projphid}",
'status': 'status-open'
}
query, 'maniphest.query', (json_body) ->
cb json_body
module.exports = Phabricator