UNPKG

6.85 kBtext/coffeescriptView Raw
1_ = require "underscore"
2Fuse = require "fuse.js"
3fetch = require "node-fetch"
4cache = require "memory-cache"
5
6Config = require "./config"
7StatsD = require('node-dogstatsd').StatsD
8
9if Config.stats.host and Config.stats.port and Config.stats.prefix
10 c = new StatsD Config.stats.host, Config.stats.port
11
12class Utils
13 @robot: null
14
15 @fetch: (url, opts) ->
16 options =
17 headers:
18 "X-Atlassian-Token": "no-check"
19 "Content-Type": "application/json"
20 "Authorization": 'Basic ' + new Buffer("#{Config.jira.username}:#{Config.jira.password}").toString('base64')
21 options = _(options).extend opts
22
23 Utils.robot.logger.debug "Fetching: #{url}"
24 fetch(url,options).then (response) ->
25 if response.status >= 200 and response.status < 300
26 return response
27 else
28 error = new Error "#{response.statusText}: #{response.url.split("?")[0]}"
29 error.response = response
30 throw error
31 .then (response) ->
32 length = response.headers.get 'content-length'
33 response.json() unless length is "0" or length is 0
34 .catch (error) ->
35 Utils.robot.logger.error error
36 Utils.robot.logger.error error.stack
37 try
38 error.response.json().then (json) ->
39 Utils.robot.logger.error JSON.stringify json
40 message = "\n`#{error}`"
41 message += "\n`#{v}`" for k,v of json.errors
42 throw message
43 catch e
44 throw error
45
46 @lookupRoomsForProject: (project) ->
47 results = _(Config.maps.projects).pick (p) -> p is project
48 _(results).keys()
49
50 @lookupChatUser: (username) ->
51 users = Utils.JiraBot.adapter.getUsers()
52 return users[username] if users[username] #Note: if get the user id instead of username
53
54 result = (users[user] for user of users when users[user].name is username)
55 if result?.length is 1
56 return result[0]
57 return null
58
59 @lookupUserWithJira: (jira, fallback=no) ->
60 users = Utils.JiraBot.adapter.getUsers()
61 result = (users[user] for user of users when users[user].profile.email is jira.emailAddress) if jira
62 if result?.length is 1
63 return if fallback then result[0].name else "<@#{result[0].id}>"
64 else if jira
65 return jira.displayName
66 else
67 return "Unassigned"
68
69 @lookupChatUsersWithJira: (jiraUsers, message) ->
70 jiraUsers = [ jiraUsers ] unless _(jiraUsers).isArray()
71 chatUsers = []
72 for jiraUser in jiraUsers when jiraUser
73 user = Utils.lookupChatUserWithJira jiraUser
74 chatUsers.push user if user
75 return chatUsers
76
77 @lookupChatUserWithJira: (jira) ->
78 users = Utils.JiraBot.adapter.getUsers()
79 result = (users[user] for user of users when users[user].profile.email is jira.emailAddress) if jira
80 return result[0] if result?.length is 1
81 return null
82
83 @detectPossibleDuplicate: (summary, tickets) ->
84 t = new Fuse tickets,
85 keys: ['fields.summary']
86 shouldSort: yes
87 verbose: no
88 threshold: 0.6
89
90 return _(t.search summary).first()
91
92 @lookupUserWithGithub: (github) ->
93 return Promise.resolve() unless github
94
95 findMatch = (user) ->
96 name = user.name or user.login
97 return unless name
98 users = Utils.JiraBot.adapter.getUsers()
99 users = _(users).keys().map (id) ->
100 u = users[id]
101 id: u.id
102 name: u.name
103 real_name: u.real_name
104
105 f = new Fuse users,
106 keys: ['real_name']
107 shouldSort: yes
108 verbose: no
109 threshold: 0.55
110
111 results = f.search name
112 result = if results? and results.length >=1 then results[0] else undefined
113 return Promise.resolve result
114
115 if github.fetch?
116 github.fetch().then findMatch
117 else
118 findMatch github
119
120 @buildQueryString: (params) ->
121 "?#{("#{encodeURIComponent k}=#{encodeURIComponent v}" for k,v of params when v).join "&"}"
122
123 @fuzzyFind: (term, arr, keys, opts) ->
124 f = new Fuse arr, _(keys: keys, shouldSort: yes, threshold: 0.3).extend opts
125 results = f.search term
126 result = if results? and results.length >=1 then results[0]
127
128 @cache:
129 put: (key, value, time=Config.cache.default.expiry) -> cache.put key, value, time
130 get: cache.get
131
132 @Stats:
133 increment: (label, tags) ->
134 try
135 label = label
136 .replace( /[\/\(\)-]/g, '.' ) #Convert slashes, brackets and dashes to dots
137 .replace( /[:\?]/g, '' ) #Remove any colon or question mark
138 .replace( /\.+/g, '.' ) #Compress multiple periods into one
139 .replace( /\.$/, '' ) #Remove any trailing period
140
141 console.log "#{Config.stats.prefix}.#{label}", tags if Config.debug
142 c.increment "#{Config.stats.prefix}.#{label}", tags if c
143 catch e
144 console.error e
145
146 @extract:
147 all: (summary) ->
148 [summary, description] = Utils.extract.description summary
149 [summary, toState] = Utils.extract.transition summary
150 [summary, assignee] = Utils.extract.mention summary
151 [summary, labels] = Utils.extract.labels summary
152 [summary, priority] = Utils.extract.priority summary
153 summary = summary.trim()
154 { summary, description, toState, assignee, labels, priority }
155
156 description: (summary) ->
157 description = summary.match(Config.quote.regex)[1] if Config.quote.regex.test(summary)
158 summary = summary.replace(Config.quote.regex, "") if description
159 return [summary, description]
160
161 transition: (summary) ->
162 if Config.maps.transitions
163 if Config.transitions.shouldRegex.test(summary)
164 [ __, toState] = summary.match Config.transitions.shouldRegex
165 summary = summary.replace(Config.transitions.shouldRegex, "") if toState
166 return [summary, toState]
167
168 mention: (summary) ->
169 if Config.mention.regex.test summary
170 assignee = summary.match(Config.mention.regex)[1]
171 summary = summary.replace Config.mention.regex, ""
172 return [summary, assignee]
173
174 labels: (summary) ->
175 labels = []
176 if Config.labels.regex.test summary
177 labels = (summary.match(Config.labels.regex).map((label) -> label.replace('#', '').trim())).concat(labels)
178 summary = summary.replace Config.labels.regex, ""
179 if Config.labels.slackChannelRegexGlobal.test summary
180 labels = (summary.match(Config.labels.slackChannelRegexGlobal).map((label) -> label.replace(Config.labels.slackChannelRegex, "$1"))).concat(labels)
181 summary = summary.replace Config.labels.slackChannelRegexGlobal, ""
182 return [summary, labels]
183
184 priority: (summary) ->
185 if Config.maps.priorities and Config.priority.regex.test summary
186 priority = summary.match(Config.priority.regex)[1]
187 priority = Config.maps.priorities.find (p) -> p.name.toLowerCase() is priority.toLowerCase()
188 summary = summary.replace Config.priority.regex, ""
189 [summary, priority]
190
191module.exports = Utils