@wanadev/mcp-gitlab 1.0.5 → 1.1.0
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/dist/client.d.ts +5 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +331 -171
- package/dist/client.js.map +1 -1
- package/dist/graphql.d.ts +56 -0
- package/dist/graphql.d.ts.map +1 -0
- package/dist/graphql.js +615 -0
- package/dist/graphql.js.map +1 -0
- package/dist/index.js +75 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -1,27 +1,33 @@
|
|
|
1
|
+
import { toGid, Q_CURRENT_USER, Q_GROUPS, Q_EPICS, Q_EPIC, Q_EPIC_ISSUES, Q_EPIC_NOTES, Q_GROUP_ISSUES, Q_ISSUE, Q_ISSUE_NOTES, Q_MILESTONES, Q_MILESTONE, Q_MERGE_REQUESTS, Q_MERGE_REQUEST, Q_ITERATIONS, Q_PROJECTS, Q_MEMBERS, Q_LABELS, Q_BOARDS, Q_PROJECT_PATH, M_CREATE_EPIC, M_UPDATE_EPIC, M_CREATE_MILESTONE, M_UPDATE_MILESTONE, M_CREATE_ISSUE, M_UPDATE_ISSUE, M_CREATE_NOTE, M_EPIC_ADD_ISSUE, mapUser, mapEpic, mapIssue, mapMilestone, mapMergeRequest, mapGroup, mapProject, mapMember, mapLabel, mapNote, mapBoard, mapIteration, } from "./graphql.js";
|
|
1
2
|
export class GitLabClient {
|
|
2
3
|
baseUrl;
|
|
3
4
|
token;
|
|
4
5
|
readOnly;
|
|
6
|
+
projectPathCache = new Map();
|
|
5
7
|
constructor(config) {
|
|
6
8
|
this.baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
7
9
|
this.token = config.token;
|
|
8
10
|
this.readOnly = config.readOnly;
|
|
9
11
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Core GraphQL methods
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
+
async graphql(query, variables) {
|
|
17
|
+
if (this.readOnly && query.trimStart().startsWith("mutation")) {
|
|
18
|
+
throw new Error("Mode lecture seule actif (GITLAB_READ_ONLY=true). Impossible d'effectuer une mutation.");
|
|
13
19
|
}
|
|
14
|
-
const url =
|
|
20
|
+
const url = `${this.baseUrl}/api/graphql`;
|
|
15
21
|
let lastError = null;
|
|
16
22
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
17
23
|
try {
|
|
18
|
-
const response = await fetch(url
|
|
19
|
-
method,
|
|
24
|
+
const response = await fetch(url, {
|
|
25
|
+
method: "POST",
|
|
20
26
|
headers: {
|
|
21
27
|
"PRIVATE-TOKEN": this.token,
|
|
22
28
|
"Content-Type": "application/json",
|
|
23
29
|
},
|
|
24
|
-
body:
|
|
30
|
+
body: JSON.stringify({ query, variables }),
|
|
25
31
|
signal: AbortSignal.timeout(15_000),
|
|
26
32
|
});
|
|
27
33
|
if (response.status === 429) {
|
|
@@ -34,13 +40,15 @@ export class GitLabClient {
|
|
|
34
40
|
}
|
|
35
41
|
if (!response.ok) {
|
|
36
42
|
const text = await response.text().catch(() => "");
|
|
37
|
-
|
|
38
|
-
throw new Error(message);
|
|
43
|
+
throw new Error(this.formatHttpError(response.status, text));
|
|
39
44
|
}
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
46
|
+
const json = (await response.json());
|
|
47
|
+
if (json.errors?.length) {
|
|
48
|
+
const messages = json.errors.map((e) => e.message).join("; ");
|
|
49
|
+
throw new Error(`GraphQL error: ${messages}`);
|
|
42
50
|
}
|
|
43
|
-
return
|
|
51
|
+
return json.data;
|
|
44
52
|
}
|
|
45
53
|
catch (error) {
|
|
46
54
|
if (error instanceof Error &&
|
|
@@ -48,241 +56,393 @@ export class GitLabClient {
|
|
|
48
56
|
throw error;
|
|
49
57
|
}
|
|
50
58
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
51
|
-
if (error instanceof DOMException &&
|
|
52
|
-
error.name === "TimeoutError") {
|
|
59
|
+
if (error instanceof DOMException && error.name === "TimeoutError") {
|
|
53
60
|
if (attempt < 2)
|
|
54
61
|
continue;
|
|
55
62
|
}
|
|
56
|
-
else
|
|
63
|
+
else {
|
|
57
64
|
throw lastError;
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
}
|
|
61
68
|
throw lastError ?? new Error("Echec apres 3 tentatives");
|
|
62
69
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
async graphqlPaginate(query, variables, extract, mapper, maxItems = 500) {
|
|
71
|
+
const results = [];
|
|
72
|
+
let after = null;
|
|
73
|
+
while (results.length < maxItems) {
|
|
74
|
+
const data = await this.graphql(query, { ...variables, after });
|
|
75
|
+
const connection = extract(data);
|
|
76
|
+
if (!connection || !connection.nodes)
|
|
77
|
+
break;
|
|
78
|
+
for (const node of connection.nodes) {
|
|
79
|
+
results.push(mapper(node));
|
|
80
|
+
}
|
|
81
|
+
if (!connection.pageInfo.hasNextPage || !connection.pageInfo.endCursor)
|
|
82
|
+
break;
|
|
83
|
+
after = connection.pageInfo.endCursor;
|
|
68
84
|
}
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
return results.slice(0, maxItems);
|
|
86
|
+
}
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
88
|
+
async mutate(query, input, resultKey) {
|
|
89
|
+
const data = await this.graphql(query, { input });
|
|
90
|
+
const result = data[resultKey];
|
|
91
|
+
if (result?.errors?.length) {
|
|
92
|
+
throw new Error(`GitLab mutation error: ${result.errors.join("; ")}`);
|
|
71
93
|
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
formatHttpError(status, body) {
|
|
72
97
|
switch (status) {
|
|
73
98
|
case 401:
|
|
74
99
|
return "Authentification echouee. Verifiez votre GITLAB_TOKEN.";
|
|
75
100
|
case 403:
|
|
76
|
-
return
|
|
101
|
+
return "Acces refuse (403). Verifiez les permissions du token et le niveau GitLab (Premium/Ultimate pour les epics).";
|
|
77
102
|
case 404:
|
|
78
|
-
return
|
|
103
|
+
return "Endpoint GraphQL introuvable (404). Verifiez GITLAB_BASE_URL.";
|
|
79
104
|
default:
|
|
80
|
-
return `Erreur GitLab ${status}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
async paginate(path, params) {
|
|
84
|
-
const results = [];
|
|
85
|
-
let page = 1;
|
|
86
|
-
const maxItems = 500;
|
|
87
|
-
while (results.length < maxItems) {
|
|
88
|
-
const url = new URL(`/api/v4${path}`, this.baseUrl);
|
|
89
|
-
url.searchParams.set("per_page", "100");
|
|
90
|
-
url.searchParams.set("page", String(page));
|
|
91
|
-
if (params) {
|
|
92
|
-
for (const [key, value] of Object.entries(params)) {
|
|
93
|
-
if (value !== undefined && value !== "") {
|
|
94
|
-
url.searchParams.set(key, value);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const response = await fetch(url.toString(), {
|
|
99
|
-
method: "GET",
|
|
100
|
-
headers: {
|
|
101
|
-
"PRIVATE-TOKEN": this.token,
|
|
102
|
-
"Content-Type": "application/json",
|
|
103
|
-
},
|
|
104
|
-
signal: AbortSignal.timeout(15_000),
|
|
105
|
-
});
|
|
106
|
-
if (!response.ok) {
|
|
107
|
-
const text = await response.text().catch(() => "");
|
|
108
|
-
throw new Error(this.formatHttpError(response.status, text, path));
|
|
109
|
-
}
|
|
110
|
-
const data = (await response.json());
|
|
111
|
-
if (data.length === 0)
|
|
112
|
-
break;
|
|
113
|
-
results.push(...data);
|
|
114
|
-
const totalPages = response.headers.get("X-Total-Pages");
|
|
115
|
-
if (totalPages && page >= parseInt(totalPages, 10))
|
|
116
|
-
break;
|
|
117
|
-
page++;
|
|
105
|
+
return `Erreur GitLab ${status}: ${body.slice(0, 200)}`;
|
|
118
106
|
}
|
|
119
|
-
return results.slice(0, maxItems);
|
|
120
107
|
}
|
|
121
|
-
|
|
108
|
+
async resolveProjectPath(projectId) {
|
|
109
|
+
const cached = this.projectPathCache.get(projectId);
|
|
110
|
+
if (cached)
|
|
111
|
+
return cached;
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
|
+
const data = await this.graphql(Q_PROJECT_PATH, { id: toGid("Project", projectId) });
|
|
114
|
+
const fullPath = data.project?.fullPath;
|
|
115
|
+
if (!fullPath)
|
|
116
|
+
throw new Error(`Project ${projectId} not found`);
|
|
117
|
+
this.projectPathCache.set(projectId, fullPath);
|
|
118
|
+
return fullPath;
|
|
119
|
+
}
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Groups
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
122
123
|
async listGroups(params) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// --- Epics ---
|
|
124
|
+
return this.graphqlPaginate(Q_GROUPS, { search: params?.search ?? null },
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
126
|
+
(d) => d.groups, mapGroup);
|
|
127
|
+
}
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// Epics
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
131
|
async listEpics(groupId, params) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
queryParams["order_by"] = params.order_by;
|
|
141
|
-
if (params?.sort)
|
|
142
|
-
queryParams["sort"] = params.sort;
|
|
143
|
-
return this.paginate(`/groups/${encodeURIComponent(groupId)}/epics`, queryParams);
|
|
132
|
+
return this.graphqlPaginate(Q_EPICS, {
|
|
133
|
+
fullPath: groupId,
|
|
134
|
+
state: params?.state && params.state !== "all" ? params.state : null,
|
|
135
|
+
search: params?.search ?? null,
|
|
136
|
+
labelName: params?.labels ? params.labels.split(",").map(s => s.trim()) : null,
|
|
137
|
+
},
|
|
138
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
139
|
+
(d) => d.group?.epics, mapEpic);
|
|
144
140
|
}
|
|
145
141
|
async getEpic(groupId, epicIid) {
|
|
146
|
-
|
|
142
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
143
|
+
const data = await this.graphql(Q_EPIC, { fullPath: groupId, iid: String(epicIid) });
|
|
144
|
+
if (!data.group?.epic)
|
|
145
|
+
throw new Error(`Epic #${epicIid} not found in group ${groupId}`);
|
|
146
|
+
return mapEpic(data.group.epic);
|
|
147
147
|
}
|
|
148
148
|
async createEpic(groupId, data) {
|
|
149
|
-
|
|
149
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
150
|
+
const input = {
|
|
151
|
+
groupPath: groupId,
|
|
152
|
+
title: data.title,
|
|
153
|
+
};
|
|
154
|
+
if (data.description)
|
|
155
|
+
input.description = data.description;
|
|
156
|
+
if (data.labels)
|
|
157
|
+
input.addLabelIds = data.labels.split(",").map(s => s.trim());
|
|
158
|
+
if (data.milestone_id)
|
|
159
|
+
input.milestoneId = toGid("Milestone", data.milestone_id);
|
|
160
|
+
if (data.start_date)
|
|
161
|
+
input.startDateFixed = data.start_date;
|
|
162
|
+
if (data.due_date)
|
|
163
|
+
input.dueDateFixed = data.due_date;
|
|
164
|
+
const result = await this.mutate(M_CREATE_EPIC, input, "createEpic");
|
|
165
|
+
return mapEpic(result.epic);
|
|
150
166
|
}
|
|
151
167
|
async updateEpic(groupId, epicIid, data) {
|
|
152
|
-
|
|
168
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
169
|
+
const input = {
|
|
170
|
+
groupPath: groupId,
|
|
171
|
+
iid: String(epicIid),
|
|
172
|
+
};
|
|
173
|
+
if (data.title)
|
|
174
|
+
input.title = data.title;
|
|
175
|
+
if (data.description)
|
|
176
|
+
input.description = data.description;
|
|
177
|
+
if (data.labels)
|
|
178
|
+
input.addLabelIds = data.labels.split(",").map(s => s.trim());
|
|
179
|
+
if (data.milestone_id)
|
|
180
|
+
input.milestoneId = toGid("Milestone", data.milestone_id);
|
|
181
|
+
if (data.start_date)
|
|
182
|
+
input.startDateFixed = data.start_date;
|
|
183
|
+
if (data.due_date)
|
|
184
|
+
input.dueDateFixed = data.due_date;
|
|
185
|
+
if (data.state_event === "close")
|
|
186
|
+
input.stateEvent = "CLOSE";
|
|
187
|
+
const result = await this.mutate(M_UPDATE_EPIC, input, "updateEpic");
|
|
188
|
+
return mapEpic(result.epic);
|
|
153
189
|
}
|
|
154
190
|
async closeEpic(groupId, epicIid) {
|
|
155
191
|
return this.updateEpic(groupId, epicIid, { state_event: "close" });
|
|
156
192
|
}
|
|
157
193
|
async listEpicIssues(groupId, epicIid) {
|
|
158
|
-
return this.
|
|
194
|
+
return this.graphqlPaginate(Q_EPIC_ISSUES, { fullPath: groupId, epicIid: String(epicIid) },
|
|
195
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
196
|
+
(d) => d.group?.epic?.issues,
|
|
197
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
198
|
+
(n) => mapIssue(n, this.baseUrl));
|
|
159
199
|
}
|
|
160
200
|
async addIssueToEpic(groupId, epicIid, issueId) {
|
|
161
|
-
|
|
201
|
+
const epic = await this.getEpic(groupId, epicIid);
|
|
202
|
+
const input = {
|
|
203
|
+
iid: String(epicIid),
|
|
204
|
+
groupPath: groupId,
|
|
205
|
+
issueIid: String(issueId),
|
|
206
|
+
};
|
|
207
|
+
await this.mutate(M_EPIC_ADD_ISSUE, input, "epicAddIssue");
|
|
208
|
+
return { id: 0, epic, issue: {} };
|
|
162
209
|
}
|
|
163
210
|
async listEpicNotes(groupId, epicIid) {
|
|
164
|
-
return this.
|
|
211
|
+
return this.graphqlPaginate(Q_EPIC_NOTES, { fullPath: groupId, epicIid: String(epicIid) },
|
|
212
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
213
|
+
(d) => d.group?.epic?.notes, mapNote);
|
|
165
214
|
}
|
|
166
215
|
async addEpicNote(groupId, epicIid, body) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
216
|
+
const epic = await this.getEpic(groupId, epicIid);
|
|
217
|
+
const noteableId = toGid("Epic", epic.id);
|
|
218
|
+
const result = await this.mutate(M_CREATE_NOTE, { noteableId, body }, "createNote");
|
|
219
|
+
return mapNote(result.note);
|
|
220
|
+
}
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
// Issues
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
170
224
|
async listGroupIssues(groupId, params) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
queryParams["order_by"] = params.order_by;
|
|
184
|
-
if (params?.sort)
|
|
185
|
-
queryParams["sort"] = params.sort;
|
|
186
|
-
return this.paginate(`/groups/${encodeURIComponent(groupId)}/issues`, queryParams);
|
|
225
|
+
return this.graphqlPaginate(Q_GROUP_ISSUES, {
|
|
226
|
+
fullPath: groupId,
|
|
227
|
+
state: params?.state && params.state !== "all" ? params.state : null,
|
|
228
|
+
search: params?.search ?? null,
|
|
229
|
+
labelName: params?.labels ? params.labels.split(",").map(s => s.trim()) : null,
|
|
230
|
+
milestoneTitle: params?.milestone ? [params.milestone] : null,
|
|
231
|
+
assigneeUsernames: params?.assignee_username ? [params.assignee_username] : null,
|
|
232
|
+
},
|
|
233
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
234
|
+
(d) => d.group?.issues,
|
|
235
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
236
|
+
(n) => mapIssue(n, this.baseUrl));
|
|
187
237
|
}
|
|
188
238
|
async getIssue(projectId, issueIid) {
|
|
189
|
-
|
|
239
|
+
const projectPath = await this.resolveProjectPath(projectId);
|
|
240
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
241
|
+
const data = await this.graphql(Q_ISSUE, { projectPath, iid: String(issueIid) });
|
|
242
|
+
if (!data.project?.issue)
|
|
243
|
+
throw new Error(`Issue #${issueIid} not found in project ${projectId}`);
|
|
244
|
+
return mapIssue(data.project.issue, this.baseUrl);
|
|
190
245
|
}
|
|
191
246
|
async createIssue(projectId, data) {
|
|
192
|
-
|
|
247
|
+
const projectPath = await this.resolveProjectPath(projectId);
|
|
248
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
249
|
+
const input = {
|
|
250
|
+
projectPath,
|
|
251
|
+
title: data.title,
|
|
252
|
+
};
|
|
253
|
+
if (data.description)
|
|
254
|
+
input.description = data.description;
|
|
255
|
+
if (data.labels)
|
|
256
|
+
input.labels = data.labels.split(",").map(s => s.trim());
|
|
257
|
+
if (data.milestone_id)
|
|
258
|
+
input.milestoneId = toGid("Milestone", data.milestone_id);
|
|
259
|
+
if (data.assignee_ids)
|
|
260
|
+
input.assigneeIds = data.assignee_ids.map(id => toGid("User", id));
|
|
261
|
+
if (data.due_date)
|
|
262
|
+
input.dueDate = data.due_date;
|
|
263
|
+
if (data.weight != null)
|
|
264
|
+
input.weight = data.weight;
|
|
265
|
+
if (data.epic_id)
|
|
266
|
+
input.epicId = toGid("Epic", data.epic_id);
|
|
267
|
+
if (data.iteration_id)
|
|
268
|
+
input.iterationId = toGid("Iteration", data.iteration_id);
|
|
269
|
+
const result = await this.mutate(M_CREATE_ISSUE, input, "createIssue");
|
|
270
|
+
return mapIssue(result.issue, this.baseUrl);
|
|
193
271
|
}
|
|
194
272
|
async updateIssue(projectId, issueIid, data) {
|
|
195
|
-
|
|
273
|
+
const projectPath = await this.resolveProjectPath(projectId);
|
|
274
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
275
|
+
const input = {
|
|
276
|
+
projectPath,
|
|
277
|
+
iid: String(issueIid),
|
|
278
|
+
};
|
|
279
|
+
if (data.title)
|
|
280
|
+
input.title = data.title;
|
|
281
|
+
if (data.description)
|
|
282
|
+
input.description = data.description;
|
|
283
|
+
if (data.labels)
|
|
284
|
+
input.labels = data.labels.split(",").map(s => s.trim());
|
|
285
|
+
if (data.milestone_id)
|
|
286
|
+
input.milestoneId = toGid("Milestone", data.milestone_id);
|
|
287
|
+
if (data.assignee_ids)
|
|
288
|
+
input.assigneeIds = data.assignee_ids.map(id => toGid("User", id));
|
|
289
|
+
if (data.due_date)
|
|
290
|
+
input.dueDate = data.due_date;
|
|
291
|
+
if (data.weight != null)
|
|
292
|
+
input.weight = data.weight;
|
|
293
|
+
if (data.state_event === "close")
|
|
294
|
+
input.stateEvent = "CLOSE";
|
|
295
|
+
if (data.iteration_id)
|
|
296
|
+
input.iterationId = toGid("Iteration", data.iteration_id);
|
|
297
|
+
const result = await this.mutate(M_UPDATE_ISSUE, input, "updateIssue");
|
|
298
|
+
return mapIssue(result.issue, this.baseUrl);
|
|
196
299
|
}
|
|
197
300
|
async closeIssue(projectId, issueIid) {
|
|
198
301
|
return this.updateIssue(projectId, issueIid, { state_event: "close" });
|
|
199
302
|
}
|
|
200
303
|
async listIssueNotes(projectId, issueIid) {
|
|
201
|
-
|
|
304
|
+
const projectPath = await this.resolveProjectPath(projectId);
|
|
305
|
+
return this.graphqlPaginate(Q_ISSUE_NOTES, { projectPath, issueIid: String(issueIid) },
|
|
306
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
307
|
+
(d) => d.project?.issue?.notes, mapNote);
|
|
202
308
|
}
|
|
203
309
|
async addIssueNote(projectId, issueIid, body) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
310
|
+
const issue = await this.getIssue(projectId, issueIid);
|
|
311
|
+
const noteableId = toGid("Issue", issue.id);
|
|
312
|
+
const result = await this.mutate(M_CREATE_NOTE, { noteableId, body }, "createNote");
|
|
313
|
+
return mapNote(result.note);
|
|
314
|
+
}
|
|
315
|
+
// ---------------------------------------------------------------------------
|
|
316
|
+
// Merge Requests
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
207
318
|
async listGroupMergeRequests(groupId, params) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
queryParams["reviewer_username"] = params.reviewer_username;
|
|
221
|
-
if (params?.order_by)
|
|
222
|
-
queryParams["order_by"] = params.order_by;
|
|
223
|
-
if (params?.sort)
|
|
224
|
-
queryParams["sort"] = params.sort;
|
|
225
|
-
return this.paginate(`/groups/${encodeURIComponent(groupId)}/merge_requests`, queryParams);
|
|
319
|
+
return this.graphqlPaginate(Q_MERGE_REQUESTS, {
|
|
320
|
+
fullPath: groupId,
|
|
321
|
+
state: params?.state && params.state !== "all" ? params.state : null,
|
|
322
|
+
labels: params?.labels ? params.labels.split(",").map(s => s.trim()) : null,
|
|
323
|
+
milestoneTitle: params?.milestone ?? null,
|
|
324
|
+
authorUsername: params?.author_username ?? null,
|
|
325
|
+
reviewerUsername: params?.reviewer_username ?? null,
|
|
326
|
+
},
|
|
327
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
328
|
+
(d) => d.group?.mergeRequests,
|
|
329
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
330
|
+
(n) => mapMergeRequest(n, this.baseUrl));
|
|
226
331
|
}
|
|
227
332
|
async getMergeRequest(projectId, mrIid) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
333
|
+
const projectPath = await this.resolveProjectPath(projectId);
|
|
334
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
335
|
+
const data = await this.graphql(Q_MERGE_REQUEST, { projectPath, iid: String(mrIid) });
|
|
336
|
+
if (!data.project?.mergeRequest)
|
|
337
|
+
throw new Error(`MR !${mrIid} not found in project ${projectId}`);
|
|
338
|
+
return mapMergeRequest(data.project.mergeRequest, this.baseUrl);
|
|
339
|
+
}
|
|
340
|
+
// ---------------------------------------------------------------------------
|
|
341
|
+
// Milestones
|
|
342
|
+
// ---------------------------------------------------------------------------
|
|
231
343
|
async listGroupMilestones(groupId, params) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
344
|
+
return this.graphqlPaginate(Q_MILESTONES, {
|
|
345
|
+
fullPath: groupId,
|
|
346
|
+
state: params?.state ?? null,
|
|
347
|
+
searchTitle: params?.search ?? null,
|
|
348
|
+
},
|
|
349
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
350
|
+
(d) => d.group?.milestones,
|
|
351
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
352
|
+
(n) => mapMilestone(n, this.baseUrl));
|
|
238
353
|
}
|
|
239
354
|
async getMilestone(groupId, milestoneId) {
|
|
240
|
-
|
|
355
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
356
|
+
const data = await this.graphql(Q_MILESTONE, {
|
|
357
|
+
fullPath: groupId,
|
|
358
|
+
ids: [toGid("Milestone", milestoneId)],
|
|
359
|
+
});
|
|
360
|
+
const node = data.group?.milestones?.nodes?.[0];
|
|
361
|
+
if (!node)
|
|
362
|
+
throw new Error(`Milestone ${milestoneId} not found in group ${groupId}`);
|
|
363
|
+
return mapMilestone(node, this.baseUrl);
|
|
241
364
|
}
|
|
242
365
|
async createMilestone(groupId, data) {
|
|
243
|
-
|
|
366
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
367
|
+
const input = {
|
|
368
|
+
groupPath: groupId,
|
|
369
|
+
title: data.title,
|
|
370
|
+
};
|
|
371
|
+
if (data.description)
|
|
372
|
+
input.description = data.description;
|
|
373
|
+
if (data.start_date)
|
|
374
|
+
input.startDate = data.start_date;
|
|
375
|
+
if (data.due_date)
|
|
376
|
+
input.dueDate = data.due_date;
|
|
377
|
+
const result = await this.mutate(M_CREATE_MILESTONE, input, "createMilestone");
|
|
378
|
+
return mapMilestone(result.milestone, this.baseUrl);
|
|
244
379
|
}
|
|
245
380
|
async updateMilestone(groupId, milestoneId, data) {
|
|
246
|
-
|
|
381
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
382
|
+
const input = {
|
|
383
|
+
id: toGid("Milestone", milestoneId),
|
|
384
|
+
};
|
|
385
|
+
if (data.title)
|
|
386
|
+
input.title = data.title;
|
|
387
|
+
if (data.description)
|
|
388
|
+
input.description = data.description;
|
|
389
|
+
if (data.start_date)
|
|
390
|
+
input.startDate = data.start_date;
|
|
391
|
+
if (data.due_date)
|
|
392
|
+
input.dueDate = data.due_date;
|
|
393
|
+
if (data.state_event === "close")
|
|
394
|
+
input.stateEvent = "CLOSE";
|
|
395
|
+
const result = await this.mutate(M_UPDATE_MILESTONE, input, "updateMilestone");
|
|
396
|
+
return mapMilestone(result.milestone, this.baseUrl);
|
|
247
397
|
}
|
|
248
398
|
async closeMilestone(groupId, milestoneId) {
|
|
249
399
|
return this.updateMilestone(groupId, milestoneId, { state_event: "close" });
|
|
250
400
|
}
|
|
251
|
-
//
|
|
401
|
+
// ---------------------------------------------------------------------------
|
|
402
|
+
// Iterations
|
|
403
|
+
// ---------------------------------------------------------------------------
|
|
252
404
|
async listGroupIterations(groupId, params) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
405
|
+
return this.graphqlPaginate(Q_ITERATIONS, {
|
|
406
|
+
fullPath: groupId,
|
|
407
|
+
state: params?.state ?? null,
|
|
408
|
+
search: params?.search ?? null,
|
|
409
|
+
},
|
|
410
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
411
|
+
(d) => d.group?.iterations, mapIteration);
|
|
412
|
+
}
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
414
|
+
// Utils
|
|
415
|
+
// ---------------------------------------------------------------------------
|
|
261
416
|
async listProjects(groupId, params) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
417
|
+
return this.graphqlPaginate(Q_PROJECTS, {
|
|
418
|
+
fullPath: groupId,
|
|
419
|
+
search: params?.search ?? null,
|
|
420
|
+
includeSubgroups: true,
|
|
421
|
+
},
|
|
422
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
423
|
+
(d) => d.group?.projects, mapProject);
|
|
268
424
|
}
|
|
269
425
|
async listGroupMembers(groupId, params) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
return this.paginate(`/groups/${encodeURIComponent(groupId)}/members`, queryParams);
|
|
426
|
+
return this.graphqlPaginate(Q_MEMBERS, { fullPath: groupId, search: params?.search ?? null },
|
|
427
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
428
|
+
(d) => d.group?.groupMembers, mapMember);
|
|
274
429
|
}
|
|
275
430
|
async listGroupLabels(groupId, params) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return this.paginate(`/groups/${encodeURIComponent(groupId)}/labels`, queryParams);
|
|
431
|
+
return this.graphqlPaginate(Q_LABELS, { fullPath: groupId, searchTerm: params?.search ?? null },
|
|
432
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
433
|
+
(d) => d.group?.labels, mapLabel);
|
|
280
434
|
}
|
|
281
435
|
async listGroupBoards(groupId) {
|
|
282
|
-
return this.
|
|
436
|
+
return this.graphqlPaginate(Q_BOARDS, { fullPath: groupId },
|
|
437
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
438
|
+
(d) => d.group?.boards, mapBoard);
|
|
283
439
|
}
|
|
284
440
|
async getCurrentUser() {
|
|
285
|
-
|
|
441
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
442
|
+
const data = await this.graphql(Q_CURRENT_USER);
|
|
443
|
+
if (!data.currentUser)
|
|
444
|
+
throw new Error("Authentification echouee. Verifiez votre GITLAB_TOKEN.");
|
|
445
|
+
return mapUser(data.currentUser);
|
|
286
446
|
}
|
|
287
447
|
}
|
|
288
448
|
//# sourceMappingURL=client.js.map
|