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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bjira",
3
- "version": "0.0.21",
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
- let value = await Ask.askList(`${fieldData.name}:`,
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. Default ${DEFAULT_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;