-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathgithub-labels.js
124 lines (103 loc) · 3.6 KB
/
github-labels.js
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
import GithubAPI from "../api/github-api.js";
/**
* Backend for performing operations with labels on GitHub repositories.
* @category GitHub
*/
export default class GithubLabels extends GithubAPI {
static urls = [
{hostname: "(api.)?github.com", pathname: "(/repos)?/:owner/:repo/labels"},
{hostname: "(api.)?github.com", pathname: "(/repos)?/:owner/:repo/issues/:issue_number(.+)?/labels"},
{hostname: "(api.)?github.com", pathname: "(/repos)?/:owner/:repo/milestones/:milestone_number(.+)?/labels"},
];
static capabilities = { auth: true, put: true };
static phrases = {
no_labels: "There are no labels to work with. Pass an empty array if you were to delete all existing labels in one go.",
failure: (action) => `Labels failed to ${action}:`,
};
/**
* Performs operations (create, update, delete) with labels.
* @param {Array<any>} data Labels to create, update, or delete.
* @returns {Promise<Object | null>} Promise that is resolved with an object based on the results of performed operations.
*/
async put (data, {ref = this.ref, allow = "create update delete"} = {}) {
if (!data) {
// Nothing to work with
console.warn(this.constructor.phrase("no_labels"));
return null;
}
if (!this.data) {
// Existing labels are not yet fetched. Fetching...
try {
await this.load(this.source);
}
catch (err) {
if (err.status === 404) {
return null;
}
}
}
// Allowed operations
allow = new Set(allow.trim().split(/\s+/));
// Label properties to monitor—label should be updated if any of these have changed
const props = ["name", "color", "default", "description"];
// Operations to be performed
let to = {};
to.create = !allow.has("create") ? [] : data.filter(label => !label.id || !this.data.find(l => l.id === label.id));
to.update = !allow.has("update") ? [] : data.filter(label => this.data.find(l => l.id === label.id && props.some(prop => l[prop] !== label[prop])));
to.delete = !allow.has("delete") ? [] : this.data.filter(l => !data.find(label => label.id === l.id));
// Results of performed operations
let ret = {};
for (let action in to) {
if (to[action].length) {
ret[`${action}d`] = await this.#write(action, to[action], ref);
}
}
return ret;
}
async #write (type, labels, ref) {
const methods = {update: "PATCH", delete: "DELETE"};
let method = methods[type] ?? "POST";
let result = await Promise.allSettled(labels.map(label => {
let apiCall = label.url, data = label, req;
if (type === "create") {
apiCall = ref.endpoint;
}
else if (type === "delete") {
data = null;
req = {responseType: "text"};
}
return this.request(apiCall, data, method, req);
}));
let success = [], failure = [];
for (let [index, promise] of result.entries()) {
let label = labels[index];
if (promise.status === "fulfilled") {
if (type === "delete") {
// For deleted labels there is no additional information returned
label = { name: label.name };
}
else {
label = promise.value;
}
success.push(label);
}
else {
failure.push({label, reason: promise.reason});
}
}
if (failure.length) {
console.warn(`${this.constructor.phrase("failure", type)} ${failure.map(({label}) => label.name).join(", ")}.`);
}
return { success, failure };
}
static parseURL (source) {
let ret = super.parseURL(source);
// Some fix-ups for URLs with host === "github.com"
// `endpoint` and `apiCall` should start with `repos/`
if (!ret.endpoint.startsWith("repos/")) {
ret.endpoint = `repos/${ret.endpoint}`;
ret.apiCall = `repos/${ret.apiCall}`;
}
return ret;
}
}