UNPKG

12.3 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright © 2020 Atomist, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17Object.defineProperty(exports, "__esModule", { value: true });
18const slack_messages_1 = require("@atomist/slack-messages");
19const pubsub_1 = require("@google-cloud/pubsub");
20const log_1 = require("./log");
21const payload_1 = require("./payload");
22const util_1 = require("./util");
23const cloneDeep = require("lodash.clonedeep");
24/** Valid MessageClient types. */
25exports.MessageMimeTypes = {
26 SLACK_JSON: "application/x-atomist-slack+json",
27 SLACK_FILE_JSON: "application/x-atomist-slack-file+json",
28 PLAIN_TEXT: "text/plain",
29 APPLICATION_JSON: "application/json",
30};
31class MessageClientSupport {
32 respond(msg, options) {
33 return this.doSend(msg, { users: [], channels: [] }, options);
34 }
35 send(msg, destinations, options) {
36 return this.doSend(msg, destinations, options);
37 }
38}
39exports.MessageClientSupport = MessageClientSupport;
40class AbstractMessageClient extends MessageClientSupport {
41 constructor(request, correlationId, team, source, graphClient) {
42 super();
43 this.request = request;
44 this.correlationId = correlationId;
45 this.team = team;
46 this.source = source;
47 this.graphClient = graphClient;
48 }
49 async delete(destinations, options) {
50 return this.doSend(undefined, destinations, Object.assign(Object.assign({}, options), { delete: true }));
51 }
52 // tslint:disable-next-line:cyclomatic-complexity
53 async doSend(msg, destinations, options = {}) {
54 var _a, _b, _c, _d, _e;
55 if (!!msg && msg.content_type === "application/x-atomist-continuation+json") {
56 return this.sendResponse(msg).then(() => msg);
57 }
58 const ts = this.ts(options);
59 const responseDestinations = [];
60 let threadTs;
61 if (options.thread === true && !!this.source) {
62 threadTs = (_b = (_a = this.source) === null || _a === void 0 ? void 0 : _a.slack) === null || _b === void 0 ? void 0 : _b.message.ts;
63 }
64 else if (typeof options.thread === "string") {
65 threadTs = options.thread;
66 }
67 const teamId = await this.getTeamId((_e = (_d = (_c = this.source) === null || _c === void 0 ? void 0 : _c.slack) === null || _d === void 0 ? void 0 : _d.team) === null || _e === void 0 ? void 0 : _e.id, this.graphClient);
68 util_1.toArray(destinations.users || []).forEach(d => {
69 responseDestinations.push({
70 user_agent: "slack",
71 slack: {
72 team: {
73 id: teamId,
74 },
75 user: {
76 name: d,
77 },
78 thread_ts: threadTs,
79 },
80 });
81 });
82 util_1.toArray(destinations.channels || []).forEach(d => {
83 responseDestinations.push({
84 user_agent: "slack",
85 slack: {
86 team: {
87 id: teamId,
88 },
89 channel: {
90 name: d,
91 },
92 thread_ts: threadTs,
93 },
94 });
95 });
96 if (responseDestinations.length === 0 && this.source) {
97 const responseDestination = cloneDeep(this.source);
98 if (responseDestination.slack) {
99 delete responseDestination.slack.user;
100 if (threadTs) {
101 responseDestination.slack.thread_ts = threadTs;
102 }
103 }
104 responseDestinations.push(responseDestination);
105 }
106 const response = {
107 api_version: "1",
108 correlation_id: this.correlationId,
109 team: this.team,
110 source: this.source ? this.source : undefined,
111 command: payload_1.isCommandIncoming(this.request) ? this.request.command : undefined,
112 event: payload_1.isEventIncoming(this.request) ? this.request.extensions.operationName : undefined,
113 destinations: responseDestinations,
114 id: options.id ? options.id : undefined,
115 timestamp: ts,
116 ttl: ts && options.ttl ? options.ttl : undefined,
117 post_mode: options.post === "update_only" ? "update_only" : (options.post === "always" ? "always" : "ttl"),
118 skill: this.request.skill,
119 };
120 if (isSlackMessage(msg)) {
121 const msgClone = cloneDeep(msg);
122 const actions = mapActions(msgClone);
123 response.content_type = exports.MessageMimeTypes.SLACK_JSON;
124 response.body = slack_messages_1.render(msgClone, false);
125 response.actions = actions;
126 }
127 else if (isFileMessage(msg)) {
128 response.content_type = exports.MessageMimeTypes.SLACK_FILE_JSON;
129 response.body = JSON.stringify({
130 content: msg.content,
131 filename: msg.fileName,
132 filetype: msg.fileType,
133 title: msg.title,
134 initial_comment: msg.comment,
135 });
136 }
137 else if (typeof msg === "string") {
138 response.content_type = exports.MessageMimeTypes.PLAIN_TEXT;
139 response.body = msg;
140 }
141 else if (options.delete) {
142 response.content_type = "application/x-atomist-delete";
143 response.body = undefined;
144 }
145 return this.sendResponse(response).then(() => response);
146 }
147 ts(options) {
148 if (options.id) {
149 if (options.ts) {
150 return options.ts;
151 }
152 else {
153 return Date.now();
154 }
155 }
156 else {
157 return undefined;
158 }
159 }
160 async getTeamId(teamId, graphClient) {
161 var _a;
162 if (teamId) {
163 return teamId;
164 }
165 else {
166 const query = `query ChatTeam { ChatTeam { id } }`;
167 const result = await graphClient.query(query);
168 return (_a = result === null || result === void 0 ? void 0 : result.ChatTeam[0]) === null || _a === void 0 ? void 0 : _a.id;
169 }
170 }
171}
172exports.AbstractMessageClient = AbstractMessageClient;
173function mapActions(msg) {
174 const actions = [];
175 let counter = 0;
176 if (msg.attachments) {
177 msg.attachments.filter(attachment => attachment.actions).forEach(attachment => {
178 attachment.actions.forEach(a => {
179 if (!!a && !!a.command) {
180 const cra = a;
181 const id = counter++;
182 cra.command.id = `${cra.command.id}-${id}`;
183 a.name = `${a.name}-${id}`;
184 const action = {
185 id: cra.command.id,
186 parameter_name: cra.command.parameterName,
187 command: cra.command.name,
188 parameters: mapParameters(cra.command.parameters),
189 };
190 actions.push(action);
191 // Lastly we need to delete our extension from the slack action
192 cra.command = undefined;
193 }
194 });
195 });
196 }
197 return actions;
198}
199exports.mapActions = mapActions;
200function mapParameters(data) {
201 const parameters = [];
202 for (const key in data) {
203 const value = data[key];
204 if (value) {
205 parameters.push({
206 name: key,
207 value: value.toString(),
208 });
209 }
210 else {
211 // logger.debug(`Parameter value for '${key}' is null`);
212 }
213 }
214 return parameters;
215}
216function isSlackMessage(object) {
217 return !!object && (object.text || object.attachments || object.blocks) && !object.content;
218}
219exports.isSlackMessage = isSlackMessage;
220function isFileMessage(object) {
221 return !!object && !object.length && object.content;
222}
223exports.isFileMessage = isFileMessage;
224class AbstractPubSubMessageClient extends AbstractMessageClient {
225 constructor(request, correlationId, team, source, graphClient) {
226 super(request, correlationId, team, source, graphClient);
227 this.request = request;
228 this.correlationId = correlationId;
229 this.team = team;
230 this.source = source;
231 this.graphClient = graphClient;
232 this.pubsub = new pubsub_1.PubSub();
233 }
234 async sendResponse(message) {
235 const topicName = process.env.ATOMIST_TOPIC || process.env.TOPIC;
236 try {
237 log_1.debug(`Sending message: ${JSON.stringify(message, util_1.replacer)}`);
238 if (topicName) {
239 const topic = this.pubsub.topic(topicName);
240 const messageBuffer = Buffer.from(JSON.stringify(message), "utf8");
241 await topic.publish(messageBuffer);
242 }
243 }
244 catch (err) {
245 log_1.error(`Error occurred sending message: ${err.message}`);
246 }
247 }
248}
249class PubSubCommandMessageClient extends AbstractPubSubMessageClient {
250 constructor(request, graphClient) {
251 super(request, request.correlation_id, request.team, request.source, graphClient);
252 this.request = request;
253 this.graphClient = graphClient;
254 }
255 async doSend(msg, destinations, options = {}) {
256 return super.doSend(msg, destinations, options);
257 }
258 async publish(status) {
259 const source = cloneDeep(this.request.source);
260 if (source && source.slack) {
261 delete source.slack.user;
262 }
263 const response = {
264 api_version: "1",
265 correlation_id: this.request.correlation_id,
266 team: this.request.team,
267 command: this.request.command,
268 source: this.request.source,
269 destinations: [source],
270 status,
271 skill: this.request.skill,
272 };
273 return this.sendResponse(response);
274 }
275}
276exports.PubSubCommandMessageClient = PubSubCommandMessageClient;
277class PubSubEventMessageClient extends AbstractPubSubMessageClient {
278 constructor(request, graphClient) {
279 super(request, request.extensions.correlation_id, { id: request.extensions.team_id, name: request.extensions.team_name }, undefined, graphClient);
280 this.request = request;
281 this.graphClient = graphClient;
282 }
283 async doSend(msg, destinations, options = {}) {
284 return super.doSend(msg, destinations, options);
285 }
286 async publish(status) {
287 const response = {
288 api_version: "1",
289 correlation_id: this.request.extensions.correlation_id,
290 team: {
291 id: this.request.extensions.team_id,
292 name: this.request.extensions.team_name,
293 },
294 event: this.request.extensions.operationName,
295 status,
296 skill: this.request.skill,
297 };
298 return this.sendResponse(response);
299 }
300}
301exports.PubSubEventMessageClient = PubSubEventMessageClient;
302function prepareStatus(status, context) {
303 if (status instanceof Error) {
304 return {
305 code: 1,
306 reason: `Error invoking ${context.skill.namespace}/${context.skill.name}`,
307 };
308 }
309 else {
310 const reason = `${(status === null || status === void 0 ? void 0 : status.code) === 0 ? "Successfully" : "Unsuccessfully"} invoked ${context.skill.namespace}/${context.skill.name}`;
311 return {
312 visibility: status === null || status === void 0 ? void 0 : status.visibility,
313 code: (status === null || status === void 0 ? void 0 : status.code) || 0,
314 reason: (status === null || status === void 0 ? void 0 : status.reason) || reason,
315 };
316 }
317}
318exports.prepareStatus = prepareStatus;
319//# sourceMappingURL=message.js.map
\No newline at end of file