@pschroee/redmine-mcp 0.3.1
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/README.md +360 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +99 -0
- package/dist/redmine/client.d.ts +349 -0
- package/dist/redmine/client.js +458 -0
- package/dist/redmine/types.d.ts +489 -0
- package/dist/redmine/types.js +2 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +10 -0
- package/dist/tools/account.d.ts +3 -0
- package/dist/tools/account.js +10 -0
- package/dist/tools/admin.d.ts +3 -0
- package/dist/tools/admin.js +150 -0
- package/dist/tools/core.d.ts +3 -0
- package/dist/tools/core.js +242 -0
- package/dist/tools/enumerations.d.ts +3 -0
- package/dist/tools/enumerations.js +26 -0
- package/dist/tools/files.d.ts +3 -0
- package/dist/tools/files.js +70 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +59 -0
- package/dist/tools/issues.d.ts +3 -0
- package/dist/tools/issues.js +61 -0
- package/dist/tools/memberships.d.ts +3 -0
- package/dist/tools/memberships.js +66 -0
- package/dist/tools/metadata.d.ts +3 -0
- package/dist/tools/metadata.js +102 -0
- package/dist/tools/projects.d.ts +3 -0
- package/dist/tools/projects.js +49 -0
- package/dist/tools/relations.d.ts +3 -0
- package/dist/tools/relations.js +157 -0
- package/dist/tools/roles.d.ts +3 -0
- package/dist/tools/roles.js +22 -0
- package/dist/tools/search.d.ts +3 -0
- package/dist/tools/search.js +28 -0
- package/dist/tools/time.d.ts +3 -0
- package/dist/tools/time.js +75 -0
- package/dist/tools/wiki.d.ts +3 -0
- package/dist/tools/wiki.js +75 -0
- package/package.json +55 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
export class RedmineClient {
|
|
2
|
+
baseUrl;
|
|
3
|
+
apiKey;
|
|
4
|
+
constructor(baseUrl, apiKey) {
|
|
5
|
+
this.baseUrl = baseUrl;
|
|
6
|
+
this.apiKey = apiKey;
|
|
7
|
+
// Remove trailing slash if present
|
|
8
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
9
|
+
}
|
|
10
|
+
async request(method, path, body) {
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
13
|
+
method,
|
|
14
|
+
headers: {
|
|
15
|
+
"X-Redmine-API-Key": this.apiKey,
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
},
|
|
18
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
19
|
+
});
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
const errorData = await response.json().catch(() => ({}));
|
|
22
|
+
const errorResponse = {
|
|
23
|
+
error: true,
|
|
24
|
+
status: response.status,
|
|
25
|
+
message: errorData.errors?.join(", ") ||
|
|
26
|
+
`HTTP ${response.status}`,
|
|
27
|
+
};
|
|
28
|
+
return errorResponse;
|
|
29
|
+
}
|
|
30
|
+
// Handle 204 No Content
|
|
31
|
+
if (response.status === 204) {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
const text = await response.text();
|
|
35
|
+
if (!text) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
return JSON.parse(text);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
const errorResponse = {
|
|
42
|
+
error: true,
|
|
43
|
+
status: 0,
|
|
44
|
+
message: err instanceof Error ? err.message : "Unknown error",
|
|
45
|
+
};
|
|
46
|
+
return errorResponse;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// ==================== ISSUES ====================
|
|
50
|
+
async listIssues(params) {
|
|
51
|
+
const query = new URLSearchParams();
|
|
52
|
+
if (params?.project_id)
|
|
53
|
+
query.set("project_id", String(params.project_id));
|
|
54
|
+
if (params?.subproject_id)
|
|
55
|
+
query.set("subproject_id", params.subproject_id);
|
|
56
|
+
if (params?.tracker_id)
|
|
57
|
+
query.set("tracker_id", String(params.tracker_id));
|
|
58
|
+
if (params?.status_id)
|
|
59
|
+
query.set("status_id", String(params.status_id));
|
|
60
|
+
if (params?.assigned_to_id)
|
|
61
|
+
query.set("assigned_to_id", String(params.assigned_to_id));
|
|
62
|
+
if (params?.parent_id)
|
|
63
|
+
query.set("parent_id", String(params.parent_id));
|
|
64
|
+
if (params?.author_id)
|
|
65
|
+
query.set("author_id", String(params.author_id));
|
|
66
|
+
if (params?.category_id)
|
|
67
|
+
query.set("category_id", String(params.category_id));
|
|
68
|
+
if (params?.fixed_version_id)
|
|
69
|
+
query.set("fixed_version_id", String(params.fixed_version_id));
|
|
70
|
+
if (params?.subject)
|
|
71
|
+
query.set("subject", params.subject);
|
|
72
|
+
if (params?.created_on)
|
|
73
|
+
query.set("created_on", params.created_on);
|
|
74
|
+
if (params?.updated_on)
|
|
75
|
+
query.set("updated_on", params.updated_on);
|
|
76
|
+
if (params?.sort)
|
|
77
|
+
query.set("sort", params.sort);
|
|
78
|
+
if (params?.include)
|
|
79
|
+
query.set("include", params.include);
|
|
80
|
+
if (params?.limit)
|
|
81
|
+
query.set("limit", String(params.limit));
|
|
82
|
+
if (params?.offset)
|
|
83
|
+
query.set("offset", String(params.offset));
|
|
84
|
+
const queryString = query.toString();
|
|
85
|
+
const path = `/issues.json${queryString ? `?${queryString}` : ""}`;
|
|
86
|
+
return this.request("GET", path);
|
|
87
|
+
}
|
|
88
|
+
async getIssue(id, include) {
|
|
89
|
+
const query = include ? `?include=${include}` : "";
|
|
90
|
+
return this.request("GET", `/issues/${id}.json${query}`);
|
|
91
|
+
}
|
|
92
|
+
async createIssue(data) {
|
|
93
|
+
return this.request("POST", "/issues.json", {
|
|
94
|
+
issue: data,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async updateIssue(id, data) {
|
|
98
|
+
return this.request("PUT", `/issues/${id}.json`, {
|
|
99
|
+
issue: data,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async deleteIssue(id) {
|
|
103
|
+
return this.request("DELETE", `/issues/${id}.json`);
|
|
104
|
+
}
|
|
105
|
+
async addIssueWatcher(issueId, userId) {
|
|
106
|
+
return this.request("POST", `/issues/${issueId}/watchers.json`, {
|
|
107
|
+
user_id: userId,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async removeIssueWatcher(issueId, userId) {
|
|
111
|
+
return this.request("DELETE", `/issues/${issueId}/watchers/${userId}.json`);
|
|
112
|
+
}
|
|
113
|
+
// ==================== PROJECTS ====================
|
|
114
|
+
async listProjects(params) {
|
|
115
|
+
const query = new URLSearchParams();
|
|
116
|
+
if (params?.include)
|
|
117
|
+
query.set("include", params.include);
|
|
118
|
+
if (params?.limit)
|
|
119
|
+
query.set("limit", String(params.limit));
|
|
120
|
+
if (params?.offset)
|
|
121
|
+
query.set("offset", String(params.offset));
|
|
122
|
+
const queryString = query.toString();
|
|
123
|
+
const path = `/projects.json${queryString ? `?${queryString}` : ""}`;
|
|
124
|
+
return this.request("GET", path);
|
|
125
|
+
}
|
|
126
|
+
async getProject(id, include) {
|
|
127
|
+
const query = include ? `?include=${include}` : "";
|
|
128
|
+
return this.request("GET", `/projects/${id}.json${query}`);
|
|
129
|
+
}
|
|
130
|
+
async createProject(data) {
|
|
131
|
+
return this.request("POST", "/projects.json", {
|
|
132
|
+
project: data,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
async updateProject(id, data) {
|
|
136
|
+
return this.request("PUT", `/projects/${id}.json`, { project: data });
|
|
137
|
+
}
|
|
138
|
+
async deleteProject(id) {
|
|
139
|
+
return this.request("DELETE", `/projects/${id}.json`);
|
|
140
|
+
}
|
|
141
|
+
async archiveProject(id) {
|
|
142
|
+
return this.request("PUT", `/projects/${id}/archive.json`);
|
|
143
|
+
}
|
|
144
|
+
async unarchiveProject(id) {
|
|
145
|
+
return this.request("PUT", `/projects/${id}/unarchive.json`);
|
|
146
|
+
}
|
|
147
|
+
// ==================== WIKI ====================
|
|
148
|
+
async listWikiPages(projectId) {
|
|
149
|
+
return this.request("GET", `/projects/${projectId}/wiki/index.json`);
|
|
150
|
+
}
|
|
151
|
+
async getWikiPage(projectId, pageName, options) {
|
|
152
|
+
const query = new URLSearchParams();
|
|
153
|
+
if (options?.include)
|
|
154
|
+
query.set("include", options.include);
|
|
155
|
+
const queryString = query.toString();
|
|
156
|
+
const versionPath = options?.version ? `/${options.version}` : "";
|
|
157
|
+
const path = `/projects/${projectId}/wiki/${encodeURIComponent(pageName)}${versionPath}.json${queryString ? `?${queryString}` : ""}`;
|
|
158
|
+
return this.request("GET", path);
|
|
159
|
+
}
|
|
160
|
+
async createOrUpdateWikiPage(projectId, pageName, data) {
|
|
161
|
+
return this.request("PUT", `/projects/${projectId}/wiki/${encodeURIComponent(pageName)}.json`, { wiki_page: data });
|
|
162
|
+
}
|
|
163
|
+
async deleteWikiPage(projectId, pageName) {
|
|
164
|
+
return this.request("DELETE", `/projects/${projectId}/wiki/${encodeURIComponent(pageName)}.json`);
|
|
165
|
+
}
|
|
166
|
+
// ==================== ATTACHMENTS ====================
|
|
167
|
+
async getAttachment(id) {
|
|
168
|
+
return this.request("GET", `/attachments/${id}.json`);
|
|
169
|
+
}
|
|
170
|
+
async deleteAttachment(id) {
|
|
171
|
+
return this.request("DELETE", `/attachments/${id}.json`);
|
|
172
|
+
}
|
|
173
|
+
async uploadFile(filename, contentType, content) {
|
|
174
|
+
try {
|
|
175
|
+
const response = await fetch(`${this.baseUrl}/uploads.json?filename=${encodeURIComponent(filename)}`, {
|
|
176
|
+
method: "POST",
|
|
177
|
+
headers: {
|
|
178
|
+
"X-Redmine-API-Key": this.apiKey,
|
|
179
|
+
"Content-Type": "application/octet-stream",
|
|
180
|
+
},
|
|
181
|
+
body: content,
|
|
182
|
+
});
|
|
183
|
+
if (!response.ok) {
|
|
184
|
+
const errorData = await response.json().catch(() => ({}));
|
|
185
|
+
const errorResponse = {
|
|
186
|
+
error: true,
|
|
187
|
+
status: response.status,
|
|
188
|
+
message: errorData.errors?.join(", ") ||
|
|
189
|
+
`HTTP ${response.status}`,
|
|
190
|
+
};
|
|
191
|
+
return errorResponse;
|
|
192
|
+
}
|
|
193
|
+
return (await response.json());
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
const errorResponse = {
|
|
197
|
+
error: true,
|
|
198
|
+
status: 0,
|
|
199
|
+
message: err instanceof Error ? err.message : "Unknown error",
|
|
200
|
+
};
|
|
201
|
+
return errorResponse;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// ==================== PROJECT FILES ====================
|
|
205
|
+
async listProjectFiles(projectId) {
|
|
206
|
+
return this.request("GET", `/projects/${projectId}/files.json`);
|
|
207
|
+
}
|
|
208
|
+
async uploadProjectFile(projectId, data) {
|
|
209
|
+
return this.request("POST", `/projects/${projectId}/files.json`, {
|
|
210
|
+
file: data,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
// ==================== ISSUE RELATIONS ====================
|
|
214
|
+
async listIssueRelations(issueId) {
|
|
215
|
+
return this.request("GET", `/issues/${issueId}/relations.json`);
|
|
216
|
+
}
|
|
217
|
+
async getRelation(id) {
|
|
218
|
+
return this.request("GET", `/relations/${id}.json`);
|
|
219
|
+
}
|
|
220
|
+
async createIssueRelation(issueId, data) {
|
|
221
|
+
return this.request("POST", `/issues/${issueId}/relations.json`, {
|
|
222
|
+
relation: data,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
async deleteRelation(id) {
|
|
226
|
+
return this.request("DELETE", `/relations/${id}.json`);
|
|
227
|
+
}
|
|
228
|
+
// ==================== VERSIONS ====================
|
|
229
|
+
async listVersions(projectId) {
|
|
230
|
+
return this.request("GET", `/projects/${projectId}/versions.json`);
|
|
231
|
+
}
|
|
232
|
+
async getVersion(id) {
|
|
233
|
+
return this.request("GET", `/versions/${id}.json`);
|
|
234
|
+
}
|
|
235
|
+
async createVersion(projectId, data) {
|
|
236
|
+
return this.request("POST", `/projects/${projectId}/versions.json`, {
|
|
237
|
+
version: data,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
async updateVersion(id, data) {
|
|
241
|
+
return this.request("PUT", `/versions/${id}.json`, {
|
|
242
|
+
version: data,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
async deleteVersion(id) {
|
|
246
|
+
return this.request("DELETE", `/versions/${id}.json`);
|
|
247
|
+
}
|
|
248
|
+
// ==================== TRACKERS ====================
|
|
249
|
+
async listTrackers() {
|
|
250
|
+
return this.request("GET", "/trackers.json");
|
|
251
|
+
}
|
|
252
|
+
// ==================== ISSUE STATUSES ====================
|
|
253
|
+
async listIssueStatuses() {
|
|
254
|
+
return this.request("GET", "/issue_statuses.json");
|
|
255
|
+
}
|
|
256
|
+
// ==================== ISSUE CATEGORIES ====================
|
|
257
|
+
async listIssueCategories(projectId) {
|
|
258
|
+
return this.request("GET", `/projects/${projectId}/issue_categories.json`);
|
|
259
|
+
}
|
|
260
|
+
async getIssueCategory(id) {
|
|
261
|
+
return this.request("GET", `/issue_categories/${id}.json`);
|
|
262
|
+
}
|
|
263
|
+
async createIssueCategory(projectId, data) {
|
|
264
|
+
return this.request("POST", `/projects/${projectId}/issue_categories.json`, { issue_category: data });
|
|
265
|
+
}
|
|
266
|
+
async updateIssueCategory(id, data) {
|
|
267
|
+
return this.request("PUT", `/issue_categories/${id}.json`, {
|
|
268
|
+
issue_category: data,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
async deleteIssueCategory(id, reassignToId) {
|
|
272
|
+
const query = reassignToId ? `?reassign_to_id=${reassignToId}` : "";
|
|
273
|
+
return this.request("DELETE", `/issue_categories/${id}.json${query}`);
|
|
274
|
+
}
|
|
275
|
+
// ==================== CUSTOM FIELDS ====================
|
|
276
|
+
async listCustomFields() {
|
|
277
|
+
return this.request("GET", "/custom_fields.json");
|
|
278
|
+
}
|
|
279
|
+
// ==================== QUERIES ====================
|
|
280
|
+
async listQueries() {
|
|
281
|
+
return this.request("GET", "/queries.json");
|
|
282
|
+
}
|
|
283
|
+
// ==================== SEARCH ====================
|
|
284
|
+
async search(params) {
|
|
285
|
+
const query = new URLSearchParams();
|
|
286
|
+
query.set("q", params.q);
|
|
287
|
+
if (params.scope)
|
|
288
|
+
query.set("scope", params.scope);
|
|
289
|
+
if (params.all_words !== undefined)
|
|
290
|
+
query.set("all_words", params.all_words ? "1" : "0");
|
|
291
|
+
if (params.titles_only !== undefined)
|
|
292
|
+
query.set("titles_only", params.titles_only ? "1" : "0");
|
|
293
|
+
if (params.open_issues !== undefined)
|
|
294
|
+
query.set("open_issues", params.open_issues ? "1" : "0");
|
|
295
|
+
if (params.attachments)
|
|
296
|
+
query.set("attachments", params.attachments);
|
|
297
|
+
if (params.issues)
|
|
298
|
+
query.set("issues", "1");
|
|
299
|
+
if (params.news)
|
|
300
|
+
query.set("news", "1");
|
|
301
|
+
if (params.documents)
|
|
302
|
+
query.set("documents", "1");
|
|
303
|
+
if (params.changesets)
|
|
304
|
+
query.set("changesets", "1");
|
|
305
|
+
if (params.wiki_pages)
|
|
306
|
+
query.set("wiki_pages", "1");
|
|
307
|
+
if (params.messages)
|
|
308
|
+
query.set("messages", "1");
|
|
309
|
+
if (params.projects)
|
|
310
|
+
query.set("projects", "1");
|
|
311
|
+
if (params.limit)
|
|
312
|
+
query.set("limit", String(params.limit));
|
|
313
|
+
if (params.offset)
|
|
314
|
+
query.set("offset", String(params.offset));
|
|
315
|
+
return this.request("GET", `/search.json?${query.toString()}`);
|
|
316
|
+
}
|
|
317
|
+
// ==================== MY ACCOUNT ====================
|
|
318
|
+
async getMyAccount() {
|
|
319
|
+
return this.request("GET", "/my/account.json");
|
|
320
|
+
}
|
|
321
|
+
// ==================== TIME ENTRIES ====================
|
|
322
|
+
async listTimeEntries(params) {
|
|
323
|
+
const query = new URLSearchParams();
|
|
324
|
+
if (params?.project_id)
|
|
325
|
+
query.set("project_id", String(params.project_id));
|
|
326
|
+
if (params?.user_id)
|
|
327
|
+
query.set("user_id", String(params.user_id));
|
|
328
|
+
if (params?.spent_on)
|
|
329
|
+
query.set("spent_on", params.spent_on);
|
|
330
|
+
if (params?.from)
|
|
331
|
+
query.set("from", params.from);
|
|
332
|
+
if (params?.to)
|
|
333
|
+
query.set("to", params.to);
|
|
334
|
+
if (params?.limit)
|
|
335
|
+
query.set("limit", String(params.limit));
|
|
336
|
+
if (params?.offset)
|
|
337
|
+
query.set("offset", String(params.offset));
|
|
338
|
+
const queryString = query.toString();
|
|
339
|
+
const path = `/time_entries.json${queryString ? `?${queryString}` : ""}`;
|
|
340
|
+
return this.request("GET", path);
|
|
341
|
+
}
|
|
342
|
+
async getTimeEntry(id) {
|
|
343
|
+
return this.request("GET", `/time_entries/${id}.json`);
|
|
344
|
+
}
|
|
345
|
+
async createTimeEntry(data) {
|
|
346
|
+
return this.request("POST", "/time_entries.json", {
|
|
347
|
+
time_entry: data,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
async updateTimeEntry(id, data) {
|
|
351
|
+
return this.request("PUT", `/time_entries/${id}.json`, {
|
|
352
|
+
time_entry: data,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
async deleteTimeEntry(id) {
|
|
356
|
+
return this.request("DELETE", `/time_entries/${id}.json`);
|
|
357
|
+
}
|
|
358
|
+
// ==================== ENUMERATIONS ====================
|
|
359
|
+
async listIssuePriorities() {
|
|
360
|
+
return this.request("GET", "/enumerations/issue_priorities.json");
|
|
361
|
+
}
|
|
362
|
+
async listTimeEntryActivities() {
|
|
363
|
+
return this.request("GET", "/enumerations/time_entry_activities.json");
|
|
364
|
+
}
|
|
365
|
+
async listDocumentCategories() {
|
|
366
|
+
return this.request("GET", "/enumerations/document_categories.json");
|
|
367
|
+
}
|
|
368
|
+
// ==================== USERS ====================
|
|
369
|
+
async listUsers(params) {
|
|
370
|
+
const query = new URLSearchParams();
|
|
371
|
+
if (params?.status !== undefined)
|
|
372
|
+
query.set("status", String(params.status));
|
|
373
|
+
if (params?.name)
|
|
374
|
+
query.set("name", params.name);
|
|
375
|
+
if (params?.group_id)
|
|
376
|
+
query.set("group_id", String(params.group_id));
|
|
377
|
+
if (params?.limit)
|
|
378
|
+
query.set("limit", String(params.limit));
|
|
379
|
+
if (params?.offset)
|
|
380
|
+
query.set("offset", String(params.offset));
|
|
381
|
+
const queryString = query.toString();
|
|
382
|
+
const path = `/users.json${queryString ? `?${queryString}` : ""}`;
|
|
383
|
+
return this.request("GET", path);
|
|
384
|
+
}
|
|
385
|
+
async getUser(id, include) {
|
|
386
|
+
const query = include ? `?include=${include}` : "";
|
|
387
|
+
return this.request("GET", `/users/${id}.json${query}`);
|
|
388
|
+
}
|
|
389
|
+
async createUser(data) {
|
|
390
|
+
return this.request("POST", "/users.json", {
|
|
391
|
+
user: data,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
async updateUser(id, data) {
|
|
395
|
+
return this.request("PUT", `/users/${id}.json`, {
|
|
396
|
+
user: data,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
async deleteUser(id) {
|
|
400
|
+
return this.request("DELETE", `/users/${id}.json`);
|
|
401
|
+
}
|
|
402
|
+
// ==================== GROUPS ====================
|
|
403
|
+
async listGroups() {
|
|
404
|
+
return this.request("GET", "/groups.json");
|
|
405
|
+
}
|
|
406
|
+
async getGroup(id, include) {
|
|
407
|
+
const query = include ? `?include=${include}` : "";
|
|
408
|
+
return this.request("GET", `/groups/${id}.json${query}`);
|
|
409
|
+
}
|
|
410
|
+
async createGroup(data) {
|
|
411
|
+
return this.request("POST", "/groups.json", {
|
|
412
|
+
group: data,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
async deleteGroup(id) {
|
|
416
|
+
return this.request("DELETE", `/groups/${id}.json`);
|
|
417
|
+
}
|
|
418
|
+
async addUserToGroup(groupId, userId) {
|
|
419
|
+
return this.request("POST", `/groups/${groupId}/users.json`, {
|
|
420
|
+
user_id: userId,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
async removeUserFromGroup(groupId, userId) {
|
|
424
|
+
return this.request("DELETE", `/groups/${groupId}/users/${userId}.json`);
|
|
425
|
+
}
|
|
426
|
+
// ==================== MEMBERSHIPS ====================
|
|
427
|
+
async listProjectMemberships(projectId, params) {
|
|
428
|
+
const query = new URLSearchParams();
|
|
429
|
+
if (params?.limit)
|
|
430
|
+
query.set("limit", String(params.limit));
|
|
431
|
+
if (params?.offset)
|
|
432
|
+
query.set("offset", String(params.offset));
|
|
433
|
+
const queryString = query.toString();
|
|
434
|
+
const path = `/projects/${projectId}/memberships.json${queryString ? `?${queryString}` : ""}`;
|
|
435
|
+
return this.request("GET", path);
|
|
436
|
+
}
|
|
437
|
+
async getMembership(id) {
|
|
438
|
+
return this.request("GET", `/memberships/${id}.json`);
|
|
439
|
+
}
|
|
440
|
+
async createProjectMembership(projectId, data) {
|
|
441
|
+
return this.request("POST", `/projects/${projectId}/memberships.json`, { membership: data });
|
|
442
|
+
}
|
|
443
|
+
async updateMembership(id, data) {
|
|
444
|
+
return this.request("PUT", `/memberships/${id}.json`, {
|
|
445
|
+
membership: data,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
async deleteMembership(id) {
|
|
449
|
+
return this.request("DELETE", `/memberships/${id}.json`);
|
|
450
|
+
}
|
|
451
|
+
// ==================== ROLES ====================
|
|
452
|
+
async listRoles() {
|
|
453
|
+
return this.request("GET", "/roles.json");
|
|
454
|
+
}
|
|
455
|
+
async getRole(id) {
|
|
456
|
+
return this.request("GET", `/roles/${id}.json`);
|
|
457
|
+
}
|
|
458
|
+
}
|