bjira 0.0.21 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/ask.js +31 -0
- package/src/field.js +29 -8
- package/src/index.js +2 -0
- package/src/label.js +103 -0
- package/src/run.js +1 -1
- package/src/set.js +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bjira",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
4
4
|
"description": "A simple jira CLI tool",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"author": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"fuzzy": "^0.1.3",
|
|
30
30
|
"inquirer": "^8.2.0",
|
|
31
31
|
"inquirer-autocomplete-prompt": "^1.4.0",
|
|
32
|
+
"inquirer-checkbox-plus-prompt": "^1.0.1",
|
|
32
33
|
"jira-client": "^6.22.0",
|
|
33
34
|
"ora": "^6.0.1",
|
|
34
35
|
"temp": "^0.9.4"
|
package/src/ask.js
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
6
|
import inquirerAutocompletePrompt from 'inquirer-autocomplete-prompt';
|
|
7
|
+
import inquirerCheckboxPlusPrompt from 'inquirer-checkbox-plus-prompt';
|
|
7
8
|
import fuzzy from 'fuzzy';
|
|
8
9
|
|
|
9
10
|
inquirer.registerPrompt('autocomplete', inquirerAutocompletePrompt);
|
|
11
|
+
inquirer.registerPrompt('checkbox-plus', inquirerCheckboxPlusPrompt);
|
|
10
12
|
|
|
11
13
|
class Ask {
|
|
12
14
|
static async askString(message, defaultValue = undefined) {
|
|
@@ -61,6 +63,35 @@ class Ask {
|
|
|
61
63
|
}]);
|
|
62
64
|
return answer.value;
|
|
63
65
|
}
|
|
66
|
+
|
|
67
|
+
static async askCallback(message, callback) {
|
|
68
|
+
const answer = await inquirer.prompt([{
|
|
69
|
+
type: 'autocomplete',
|
|
70
|
+
name: 'value',
|
|
71
|
+
message,
|
|
72
|
+
source: (answers, input) => callback(input),
|
|
73
|
+
}]);
|
|
74
|
+
return answer.value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static async askMultiList(message, list, defaultValue = null) {
|
|
78
|
+
const answer = await inquirer.prompt([{
|
|
79
|
+
type: 'checkbox-plus',
|
|
80
|
+
name: 'value',
|
|
81
|
+
message,
|
|
82
|
+
default: defaultValue,
|
|
83
|
+
searchable: true,
|
|
84
|
+
source: (answers, input) => {
|
|
85
|
+
return new Promise(resolve => {
|
|
86
|
+
resolve(fuzzy.filter(input || '', list, {
|
|
87
|
+
extract: el => el.name
|
|
88
|
+
}).map(element => element.original));
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
}]);
|
|
92
|
+
return answer.value;
|
|
93
|
+
}
|
|
94
|
+
|
|
64
95
|
};
|
|
65
96
|
|
|
66
97
|
export default Ask;
|
package/src/field.js
CHANGED
|
@@ -145,10 +145,14 @@ class Field extends Command {
|
|
|
145
145
|
return field;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
if (Array.isArray(field)) {
|
|
149
|
+
return field.map(f => this.fieldValue(f, fieldData)).join(", ");
|
|
150
|
+
}
|
|
151
|
+
|
|
148
152
|
return field.name;
|
|
149
153
|
}
|
|
150
154
|
|
|
151
|
-
static async fetchAndAskFieldIfSupported(jira, field) {
|
|
155
|
+
static async fetchAndAskFieldIfSupported(jira, field, defaultValue = null) {
|
|
152
156
|
const meta = await Project.metadata(jira, field.projectName, field.issueTypeName);
|
|
153
157
|
const fields = meta.projects.find(p => p.key === field.projectName)
|
|
154
158
|
.issuetypes.find(i => i.name === field.issueTypeName).fields;
|
|
@@ -156,14 +160,14 @@ class Field extends Command {
|
|
|
156
160
|
for (const name in fields) {
|
|
157
161
|
const fieldObj = fields[name];
|
|
158
162
|
if (fieldObj.name === field.fieldName) {
|
|
159
|
-
return Field.askFieldIfSupported(fieldObj);
|
|
163
|
+
return Field.askFieldIfSupported(fieldObj, defaultValue);
|
|
160
164
|
}
|
|
161
165
|
}
|
|
162
166
|
|
|
163
167
|
return null;
|
|
164
168
|
}
|
|
165
169
|
|
|
166
|
-
static async askFieldIfSupported(fieldData) {
|
|
170
|
+
static async askFieldIfSupported(fieldData, defaultValue = null) {
|
|
167
171
|
if (!Field.isSupported(fieldData)) {
|
|
168
172
|
console.log("Unsupported field");
|
|
169
173
|
return null;
|
|
@@ -179,9 +183,30 @@ class Field extends Command {
|
|
|
179
183
|
return {
|
|
180
184
|
value: await Ask.askString(`${fieldData.name}:`), key: fieldData.key
|
|
181
185
|
};
|
|
186
|
+
|
|
187
|
+
case 'array':
|
|
188
|
+
if (Array.isArray(defaultValue)) {
|
|
189
|
+
defaultValue = defaultValue.map(a => ({
|
|
190
|
+
id: a.id
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const value = await Ask.askMultiList(`${fieldData.name}:`,
|
|
195
|
+
fieldData.allowedValues.map(value => ({
|
|
196
|
+
name: value.name,
|
|
197
|
+
value: {
|
|
198
|
+
id: value.id
|
|
199
|
+
}
|
|
200
|
+
})),
|
|
201
|
+
defaultValue);
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
key: fieldData.key,
|
|
205
|
+
value: value,
|
|
206
|
+
};
|
|
182
207
|
}
|
|
183
208
|
|
|
184
|
-
|
|
209
|
+
const value = await Ask.askList(`${fieldData.name}:`,
|
|
185
210
|
fieldData.allowedValues.map(value => ({
|
|
186
211
|
name: value.name,
|
|
187
212
|
value: {
|
|
@@ -189,10 +214,6 @@ class Field extends Command {
|
|
|
189
214
|
}
|
|
190
215
|
})));
|
|
191
216
|
|
|
192
|
-
if (fieldData.schema.type === 'array') {
|
|
193
|
-
value = [value];
|
|
194
|
-
}
|
|
195
|
-
|
|
196
217
|
return {
|
|
197
218
|
key: fieldData.key,
|
|
198
219
|
value,
|
package/src/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import Create from './create.js';
|
|
|
15
15
|
import Field from './field.js';
|
|
16
16
|
import Init from './init.js';
|
|
17
17
|
import Issue from './issue.js';
|
|
18
|
+
import Label from './label.js';
|
|
18
19
|
import Preset from './preset.js';
|
|
19
20
|
import Project from './project.js';
|
|
20
21
|
import Query from './query.js';
|
|
@@ -31,6 +32,7 @@ const commands = [
|
|
|
31
32
|
new Field(),
|
|
32
33
|
new Init(),
|
|
33
34
|
new Issue(),
|
|
35
|
+
new Label(),
|
|
34
36
|
new Preset(),
|
|
35
37
|
new Project(),
|
|
36
38
|
new Query(),
|
package/src/label.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2021-2022 Andrea Marchesini <baku@bnode.dev>
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
import color from 'chalk';
|
|
6
|
+
|
|
7
|
+
import Ask from './ask.js';
|
|
8
|
+
import Command from './command.js';
|
|
9
|
+
import Field from './field.js';
|
|
10
|
+
import Issue from './issue.js';
|
|
11
|
+
import Jira from './jira.js';
|
|
12
|
+
import Project from './project.js';
|
|
13
|
+
import Table from './table.js';
|
|
14
|
+
import User from './user.js';
|
|
15
|
+
|
|
16
|
+
class Label extends Command {
|
|
17
|
+
addOptions(program) {
|
|
18
|
+
const labelCmd = program.command('label')
|
|
19
|
+
.description('Play with labels');
|
|
20
|
+
labelCmd.command('add')
|
|
21
|
+
.description('Add a label to an issue')
|
|
22
|
+
.argument('<id>', 'The issue ID')
|
|
23
|
+
.action(async id => {
|
|
24
|
+
const jira = new Jira(program);
|
|
25
|
+
const resultFields = await Field.listFields(jira);
|
|
26
|
+
const result = await jira.spin('Running query...', jira.api.findIssue(id));
|
|
27
|
+
const issue = Issue.replaceFields(result, resultFields);
|
|
28
|
+
|
|
29
|
+
const meta = await Project.metadata(jira, issue.fields['Project'].key, issue.fields['Issue Type'].name);
|
|
30
|
+
const field = meta.projects.find(p => p.key === issue.fields['Project'].key)
|
|
31
|
+
.issuetypes.find(i => i.name === issue.fields['Issue Type'].name).fields['labels'];
|
|
32
|
+
if (!field) {
|
|
33
|
+
console.log("This type does not support labels");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const label = await Ask.askCallback("Add label:", async input => {
|
|
38
|
+
if (!input) return [];
|
|
39
|
+
const data = await jira.api.doRequest({
|
|
40
|
+
url: field.autoCompleteUrl + input
|
|
41
|
+
})
|
|
42
|
+
return JSON.parse(data).suggestions.map(label => label.label);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const labels = issue.fields['Labels'] || [];
|
|
46
|
+
if (labels.includes(label)) {
|
|
47
|
+
console.log("Duplicate label");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
labels.push(label);
|
|
52
|
+
|
|
53
|
+
const data = {};
|
|
54
|
+
data[field.key] = labels;
|
|
55
|
+
|
|
56
|
+
await jira.spin(`Updating issue ${id}...`, jira.api.updateIssue(id, {
|
|
57
|
+
fields: {
|
|
58
|
+
...data
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
labelCmd.command('remove')
|
|
64
|
+
.description('Remove a label from an issue')
|
|
65
|
+
.argument('<id>', 'The issue ID')
|
|
66
|
+
.action(async id => {
|
|
67
|
+
const jira = new Jira(program);
|
|
68
|
+
const resultFields = await Field.listFields(jira);
|
|
69
|
+
const result = await jira.spin('Running query...', jira.api.findIssue(id));
|
|
70
|
+
const issue = Issue.replaceFields(result, resultFields);
|
|
71
|
+
|
|
72
|
+
const meta = await Project.metadata(jira, issue.fields['Project'].key, issue.fields['Issue Type'].name);
|
|
73
|
+
const field = meta.projects.find(p => p.key === issue.fields['Project'].key)
|
|
74
|
+
.issuetypes.find(i => i.name === issue.fields['Issue Type'].name).fields['labels'];
|
|
75
|
+
if (!field) {
|
|
76
|
+
console.log("This type does not support labels");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let labels = issue.fields['Labels'] || [];
|
|
81
|
+
if (labels.length === 0) {
|
|
82
|
+
console.log("No labels");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let label = await Ask.askList("Remove label:", labels.map(label => ({
|
|
87
|
+
name: label,
|
|
88
|
+
value: label
|
|
89
|
+
})));
|
|
90
|
+
|
|
91
|
+
const data = {};
|
|
92
|
+
data[field.key] = labels.filter(l => label != l)
|
|
93
|
+
|
|
94
|
+
await jira.spin(`Updating issue ${id}...`, jira.api.updateIssue(id, {
|
|
95
|
+
fields: {
|
|
96
|
+
...data
|
|
97
|
+
}
|
|
98
|
+
}));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export default Label;
|
package/src/run.js
CHANGED
|
@@ -17,7 +17,7 @@ class Run extends Command {
|
|
|
17
17
|
.argument('<name>', 'The preset name')
|
|
18
18
|
.option('-q, --query <query...>')
|
|
19
19
|
.option('-l, --limit <limit>',
|
|
20
|
-
`Set the query limit
|
|
20
|
+
`Set the query limit`,
|
|
21
21
|
DEFAULT_QUERY_LIMIT)
|
|
22
22
|
.option('-g, --grouped', 'Group the issues by parenting')
|
|
23
23
|
.action(async name => {
|
package/src/set.js
CHANGED
|
@@ -66,7 +66,7 @@ class Set extends Command {
|
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
await Set.setCustomField(jira, customField, id);
|
|
69
|
+
await Set.setCustomField(jira, customField, id, issue.fields[customField.fieldName]);
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -90,8 +90,8 @@ class Set extends Command {
|
|
|
90
90
|
await jira.spin(`Updating issue ${id}...`, jira.api.updateIssue(id, issue));
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
static async setCustomField(jira, customField, id) {
|
|
94
|
-
const field = await Field.fetchAndAskFieldIfSupported(jira, customField);
|
|
93
|
+
static async setCustomField(jira, customField, id, defaultValue = null) {
|
|
94
|
+
const field = await Field.fetchAndAskFieldIfSupported(jira, customField, defaultValue);
|
|
95
95
|
if (field === null) {
|
|
96
96
|
console.log("Unsupported field type");
|
|
97
97
|
return;
|