-
Notifications
You must be signed in to change notification settings - Fork 193
/
Copy pathjira-client-util.ts
149 lines (122 loc) · 4.45 KB
/
jira-client-util.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import { envVars } from "config/env";
import { getLogger } from "config/logger";
import { JiraIssue } from "interfaces/jira";
import { jiraIssueInSquareBracketsRegex } from "utils/jira-utils";
const logger = getLogger("jira.util");
export const getJiraUtil = (jiraClient) => {
const containsReferenceLink = (line: string) => {
// reference text links should have 2 parts only
if (line.split(" ").length === 2) {
const hasSquareBrackets = line.charAt(0) === "[" && line.includes("]:");
const hasUrl = line.includes("http://") || line.includes("https://");
return hasSquareBrackets && hasUrl;
}
return false;
};
// Parse our existing issue text, pulling out any existing reference links.
// if reference links exist, returns array of issue keys. For example, the following
// reference links would return [ 'TEST-2019', 'TEST-2020' ]
// [TEST-2019]: http://example.com/browse/TEST-2019
// [TEST-2020]: https://example.com/browse/TEST-2020
// if no issue keys exist, return []
const checkForReferenceText = (text: string) => {
const splitTextByNewLine = text.split("\n");
return splitTextByNewLine
.filter((line) => containsReferenceLink(line))
.map((referenceLink) => referenceLink.slice(1, referenceLink.indexOf("]")));
};
const addJiraIssueLinks = (text: string, issues: JiraIssue[]): string => {
const referenceRegex = jiraIssueInSquareBracketsRegex();
const issueMap = issues.reduce((acc, issue) => ({
...acc,
[issue.key]: issue
}), {});
const links: string[] = [];
const keys = checkForReferenceText(text);
// Parse the text up to a maximum amount of characters.
while (referenceRegex.lastIndex < 1000) {
const match = referenceRegex.exec(text);
if (!match) {
break;
}
const [, , key] = match;
// If we already have a reference link, or the issue is not valid, skip it.
if (keys.includes(key) || !issueMap[key]) {
continue;
}
const issueTrackingParam = envVars.JIRA_LINK_TRACKING_ID ? `?atlOrigin=${envVars.JIRA_LINK_TRACKING_ID}` : "";
const link = `${jiraClient.baseURL}/browse/${key}${issueTrackingParam}`;
const reference = `[${key}]: ${link}`;
if (text.includes(reference)) {
continue;
}
links.push(reference);
}
return links.length ? [text, links.join("\n")].join("\n\n") : text;
};
const unfurl = async (text: string): Promise<string | undefined> => {
try {
const issues = jiraClient.issues.parse(text);
if (!issues) return undefined;
const validIssues = await jiraClient.issues.getAll(issues);
if (!validIssues.length) return undefined;
const linkifiedBody = addJiraIssueLinks(text, validIssues);
if (linkifiedBody === text) return undefined;
return linkifiedBody;
} catch (err) {
logger.warn({ err, issueText: text }, "Error getting all JIRA issues");
return undefined;
}
};
type Command = {
kind: string;
name: string;
text: string;
time: number;
issueKeys: string[]
};
const runJiraCommands = async (commands: Command[]) => {
return Promise.all(commands.map(command => {
if (command.kind === "comment") {
return Promise.all(command.issueKeys.map(issueKey => jiraClient.issues.comments.addForIssue(issueKey, {
body: command.text
})));
}
if (command.kind === "worklog") {
return Promise.all(command.issueKeys.map(issueKey => jiraClient.issues.worklogs.addForIssue(issueKey, {
timeSpentSeconds: command.time,
comment: command.text
})));
}
if (command.kind === "transition") {
return Promise.all(command.issueKeys.map(async issueKey => {
const transitions = (await jiraClient.issues.transitions.getForIssue(issueKey))
.data
.transitions
.map((transition: {id: string, name: string}) => ({
id: transition.id,
name: transition.name.replace(" ", "-").toLowerCase()
}))
.filter((transition: {id: string, name: string}) => transition.name.startsWith(command.name));
// We only want to run a transition if we match only one. If we don't match a transition
// or if we match two transitions, we should resolve rather than transitioning.
if (transitions.length !== 1) {
return Promise.resolve();
}
if (command.text) {
await jiraClient.issues.comments.addForIssue(issueKey, {
body: command.text
});
}
return jiraClient.issues.transitions.updateForIssue(issueKey, transitions[0].id);
}));
}
return Promise.resolve();
}));
};
return {
addJiraIssueLinks,
runJiraCommands,
unfurl
};
};