@charlie.act7/canvas-mcp-server 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/README.md +117 -0
- package/dist/common/config-manager.js +28 -0
- package/dist/common/helpers.js +46 -0
- package/dist/common/tool-model.js +1 -0
- package/dist/common/types.js +1 -0
- package/dist/http-server.js +760 -0
- package/dist/index.js +168 -0
- package/dist/prompts/canvas-prompts.js +64 -0
- package/dist/resources/canvas-resources.js +55 -0
- package/dist/services/canvas-client.js +459 -0
- package/dist/tools/assignment-tools.js +626 -0
- package/dist/tools/calendar-tools.js +240 -0
- package/dist/tools/communication-tools.js +130 -0
- package/dist/tools/config-tools.js +39 -0
- package/dist/tools/course-tools.js +123 -0
- package/dist/tools/create-tools.js +76 -0
- package/dist/tools/file-tools.js +229 -0
- package/dist/tools/grading-tools.js +187 -0
- package/dist/tools/group-tools.js +192 -0
- package/dist/tools/module-tools.js +269 -0
- package/dist/tools/question-bank-tools.js +238 -0
- package/dist/tools/quiz-question-tools.js +303 -0
- package/dist/tools/quiz-tools.js +184 -0
- package/dist/tools/rubric-tools.js +145 -0
- package/dist/tools/student-tools.js +172 -0
- package/package.json +65 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
export class CanvasClient {
|
|
3
|
+
client;
|
|
4
|
+
token;
|
|
5
|
+
domain;
|
|
6
|
+
constructor(token, domain) {
|
|
7
|
+
this.token = token;
|
|
8
|
+
this.domain = domain;
|
|
9
|
+
this.client = this.createAxiosInstance(token, domain);
|
|
10
|
+
}
|
|
11
|
+
createAxiosInstance(token, domain) {
|
|
12
|
+
const baseURL = `https://${domain}/api/v1`;
|
|
13
|
+
return axios.create({
|
|
14
|
+
baseURL,
|
|
15
|
+
headers: {
|
|
16
|
+
'Authorization': `Bearer ${token}`,
|
|
17
|
+
'Content-Type': 'application/json'
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
updateConfig(token, domain) {
|
|
22
|
+
this.token = token;
|
|
23
|
+
this.domain = domain;
|
|
24
|
+
this.client = this.createAxiosInstance(token, domain);
|
|
25
|
+
}
|
|
26
|
+
parseLinkHeader(header) {
|
|
27
|
+
if (!header)
|
|
28
|
+
return {};
|
|
29
|
+
const links = {};
|
|
30
|
+
const parts = header.split(',');
|
|
31
|
+
parts.forEach(part => {
|
|
32
|
+
const section = part.split(';');
|
|
33
|
+
if (section.length < 2)
|
|
34
|
+
return;
|
|
35
|
+
const url = section[0].replace(/<(.*)>/, '$1').trim();
|
|
36
|
+
const name = section[1].replace(/rel="?([^"]+)"?/, '$1').trim();
|
|
37
|
+
links[name] = url;
|
|
38
|
+
});
|
|
39
|
+
return links;
|
|
40
|
+
}
|
|
41
|
+
async getAllPages(initialUrl, params) {
|
|
42
|
+
let allResults = [];
|
|
43
|
+
let nextUrl = initialUrl;
|
|
44
|
+
while (nextUrl) {
|
|
45
|
+
const response = await this.client.get(nextUrl, { params: nextUrl === initialUrl ? params : undefined });
|
|
46
|
+
if (response.data) {
|
|
47
|
+
allResults = allResults.concat(response.data);
|
|
48
|
+
}
|
|
49
|
+
const links = this.parseLinkHeader(response.headers['link']);
|
|
50
|
+
nextUrl = links['next'] || null;
|
|
51
|
+
}
|
|
52
|
+
return allResults;
|
|
53
|
+
}
|
|
54
|
+
async getCourses() {
|
|
55
|
+
return this.getAllPages('courses', {
|
|
56
|
+
include: ['term'],
|
|
57
|
+
enrollment_state: 'active',
|
|
58
|
+
per_page: 100
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async getModules(courseId) {
|
|
62
|
+
return this.getAllPages(`courses/${courseId}/modules`, {
|
|
63
|
+
include: ['items']
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async getAssignments(courseId) {
|
|
67
|
+
return this.getAllPages(`courses/${courseId}/assignments`, {
|
|
68
|
+
per_page: 100
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async getAssignmentGroups(courseId) {
|
|
72
|
+
return this.getAllPages(`courses/${courseId}/assignment_groups`, {
|
|
73
|
+
per_page: 100
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async getQuizzes(courseId) {
|
|
77
|
+
return this.getAllPages(`courses/${courseId}/quizzes`, {
|
|
78
|
+
per_page: 100
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async getAssignment(courseId, assignmentId) {
|
|
82
|
+
const response = await this.client.get(`courses/${courseId}/assignments/${assignmentId}`, {
|
|
83
|
+
params: {
|
|
84
|
+
include: ['submission', 'rubric_settings', 'overrides']
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return response.data;
|
|
88
|
+
}
|
|
89
|
+
async updateAssignmentDates(courseId, assignmentId, dates) {
|
|
90
|
+
const response = await this.client.put(`courses/${courseId}/assignments/${assignmentId}`, {
|
|
91
|
+
assignment: dates
|
|
92
|
+
});
|
|
93
|
+
return response.data;
|
|
94
|
+
}
|
|
95
|
+
async createAssignment(courseId, assignment) {
|
|
96
|
+
const response = await this.client.post(`courses/${courseId}/assignments`, {
|
|
97
|
+
assignment: assignment
|
|
98
|
+
});
|
|
99
|
+
return response.data;
|
|
100
|
+
}
|
|
101
|
+
async updateAssignment(courseId, assignmentId, assignment) {
|
|
102
|
+
const response = await this.client.put(`courses/${courseId}/assignments/${assignmentId}`, {
|
|
103
|
+
assignment: assignment
|
|
104
|
+
});
|
|
105
|
+
return response.data;
|
|
106
|
+
}
|
|
107
|
+
async getQuiz(courseId, quizId) {
|
|
108
|
+
const response = await this.client.get(`courses/${courseId}/quizzes/${quizId}`);
|
|
109
|
+
return response.data;
|
|
110
|
+
}
|
|
111
|
+
async updateQuizDates(courseId, quizId, dates) {
|
|
112
|
+
const response = await this.client.put(`courses/${courseId}/quizzes/${quizId}`, {
|
|
113
|
+
quiz: dates
|
|
114
|
+
});
|
|
115
|
+
return response.data;
|
|
116
|
+
}
|
|
117
|
+
async getSubmissions(courseId, assignmentId) {
|
|
118
|
+
return this.getAllPages(`courses/${courseId}/assignments/${assignmentId}/submissions`, {
|
|
119
|
+
include: ['user'],
|
|
120
|
+
per_page: 100
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async gradeSubmission(courseId, assignmentId, userId, grade, comment, rubric_assessment) {
|
|
124
|
+
const url = `courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`;
|
|
125
|
+
const data = {
|
|
126
|
+
submission: {
|
|
127
|
+
posted_grade: grade
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
if (comment) {
|
|
131
|
+
data.comment = {
|
|
132
|
+
text_comment: comment
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (rubric_assessment) {
|
|
136
|
+
data.rubric_assessment = rubric_assessment;
|
|
137
|
+
}
|
|
138
|
+
const response = await this.client.put(url, data);
|
|
139
|
+
return response.data;
|
|
140
|
+
}
|
|
141
|
+
async getSingleSubmission(courseId, assignmentId, userId) {
|
|
142
|
+
const url = `courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`;
|
|
143
|
+
const response = await this.client.get(url, {
|
|
144
|
+
params: {
|
|
145
|
+
include: ['submission_history', 'submission_comments', 'rubric_assessment', 'visibility', 'user']
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
return response.data;
|
|
149
|
+
}
|
|
150
|
+
async getEnrollments(courseId) {
|
|
151
|
+
return this.getAllPages(`courses/${courseId}/users`, {
|
|
152
|
+
enrollment_type: ['student'],
|
|
153
|
+
include: ['email', 'enrollments'],
|
|
154
|
+
per_page: 100
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
async getStudentInCourse(courseId, studentId) {
|
|
158
|
+
const response = await this.client.get(`courses/${courseId}/users/${studentId}`, {
|
|
159
|
+
params: {
|
|
160
|
+
include: ['email', 'enrollments']
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
return response.data;
|
|
164
|
+
}
|
|
165
|
+
async getStudentCourseSubmissions(courseId, studentId) {
|
|
166
|
+
return this.getAllPages(`courses/${courseId}/students/submissions`, {
|
|
167
|
+
student_ids: [studentId],
|
|
168
|
+
include: ['assignment'],
|
|
169
|
+
per_page: 100
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
async getPages(courseId) {
|
|
173
|
+
return this.getAllPages(`courses/${courseId}/pages`);
|
|
174
|
+
}
|
|
175
|
+
async getPage(courseId, pageUrlOrId) {
|
|
176
|
+
const response = await this.client.get(`courses/${courseId}/pages/${pageUrlOrId}`);
|
|
177
|
+
return response.data;
|
|
178
|
+
}
|
|
179
|
+
async getFiles(courseId) {
|
|
180
|
+
return this.getAllPages(`courses/${courseId}/files`);
|
|
181
|
+
}
|
|
182
|
+
async getAnnouncements(courseIds) {
|
|
183
|
+
return this.getAllPages('announcements', {
|
|
184
|
+
context_codes: courseIds.map(id => `course_${id}`)
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
async deleteSubmissionComment(courseId, assignmentId, userId, commentId) {
|
|
188
|
+
const url = `courses/${courseId}/assignments/${assignmentId}/submissions/${userId}/comments/${commentId}`;
|
|
189
|
+
await this.client.delete(url);
|
|
190
|
+
return { deleted: true, comment_id: commentId };
|
|
191
|
+
}
|
|
192
|
+
async getSubmissionComments(courseId, assignmentId, userId) {
|
|
193
|
+
return this.getSingleSubmission(courseId, assignmentId, userId);
|
|
194
|
+
}
|
|
195
|
+
async getDiscussionTopics(courseId) {
|
|
196
|
+
return this.getAllPages(`courses/${courseId}/discussion_topics`);
|
|
197
|
+
}
|
|
198
|
+
async getDiscussionEntries(courseId, topicId) {
|
|
199
|
+
return this.getAllPages(`courses/${courseId}/discussion_topics/${topicId}/entries`);
|
|
200
|
+
}
|
|
201
|
+
async postDiscussionReply(courseId, topicId, message) {
|
|
202
|
+
const url = `courses/${courseId}/discussion_topics/${topicId}/entries`;
|
|
203
|
+
const response = await this.client.post(url, { message });
|
|
204
|
+
return response.data;
|
|
205
|
+
}
|
|
206
|
+
async postAnnouncement(courseId, title, message) {
|
|
207
|
+
// Announcements are technically discussion topics with is_announcement=true
|
|
208
|
+
const url = `courses/${courseId}/discussion_topics`;
|
|
209
|
+
const response = await this.client.post(url, {
|
|
210
|
+
title,
|
|
211
|
+
message,
|
|
212
|
+
is_announcement: true
|
|
213
|
+
});
|
|
214
|
+
return response.data;
|
|
215
|
+
}
|
|
216
|
+
// --- Quiz Questions ---
|
|
217
|
+
async listQuizQuestions(courseId, quizId) {
|
|
218
|
+
return this.getAllPages(`courses/${courseId}/quizzes/${quizId}/questions`, {
|
|
219
|
+
per_page: 100
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
async getQuizQuestion(courseId, quizId, questionId) {
|
|
223
|
+
const response = await this.client.get(`courses/${courseId}/quizzes/${quizId}/questions/${questionId}`);
|
|
224
|
+
return response.data;
|
|
225
|
+
}
|
|
226
|
+
async createQuizQuestion(courseId, quizId, data) {
|
|
227
|
+
const response = await this.client.post(`courses/${courseId}/quizzes/${quizId}/questions`, { question: data });
|
|
228
|
+
return response.data;
|
|
229
|
+
}
|
|
230
|
+
async updateQuizQuestion(courseId, quizId, questionId, data) {
|
|
231
|
+
const response = await this.client.put(`courses/${courseId}/quizzes/${quizId}/questions/${questionId}`, { question: data });
|
|
232
|
+
return response.data;
|
|
233
|
+
}
|
|
234
|
+
async deleteQuizQuestion(courseId, quizId, questionId) {
|
|
235
|
+
await this.client.delete(`courses/${courseId}/quizzes/${quizId}/questions/${questionId}`);
|
|
236
|
+
return { deleted: true };
|
|
237
|
+
}
|
|
238
|
+
// --- Quiz Groups ---
|
|
239
|
+
async createQuizGroup(courseId, quizId, groupData) {
|
|
240
|
+
const response = await this.client.post(`courses/${courseId}/quizzes/${quizId}/groups`, { quiz_groups: [groupData] });
|
|
241
|
+
return response.data.quiz_groups[0];
|
|
242
|
+
}
|
|
243
|
+
async createPage(courseId, title, body, published = false) {
|
|
244
|
+
const response = await this.client.post(`courses/${courseId}/pages`, {
|
|
245
|
+
wiki_page: {
|
|
246
|
+
title,
|
|
247
|
+
body,
|
|
248
|
+
published
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
return response.data;
|
|
252
|
+
}
|
|
253
|
+
async updatePage(courseId, pageUrlOrId, data) {
|
|
254
|
+
const response = await this.client.put(`courses/${courseId}/pages/${pageUrlOrId}`, {
|
|
255
|
+
wiki_page: data
|
|
256
|
+
});
|
|
257
|
+
return response.data;
|
|
258
|
+
}
|
|
259
|
+
// --- Modules ---
|
|
260
|
+
async createModule(courseId, name, published = false, position) {
|
|
261
|
+
const response = await this.client.post(`courses/${courseId}/modules`, {
|
|
262
|
+
module: {
|
|
263
|
+
name,
|
|
264
|
+
published,
|
|
265
|
+
position
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
return response.data;
|
|
269
|
+
}
|
|
270
|
+
async updateModule(courseId, moduleId, data) {
|
|
271
|
+
const response = await this.client.put(`courses/${courseId}/modules/${moduleId}`, {
|
|
272
|
+
module: data
|
|
273
|
+
});
|
|
274
|
+
return response.data;
|
|
275
|
+
}
|
|
276
|
+
async deleteModule(courseId, moduleId) {
|
|
277
|
+
await this.client.delete(`courses/${courseId}/modules/${moduleId}`);
|
|
278
|
+
return { deleted: true };
|
|
279
|
+
}
|
|
280
|
+
// --- Module Items ---
|
|
281
|
+
async createModuleItem(courseId, moduleId, item) {
|
|
282
|
+
const response = await this.client.post(`courses/${courseId}/modules/${moduleId}/items`, {
|
|
283
|
+
module_item: item
|
|
284
|
+
});
|
|
285
|
+
return response.data;
|
|
286
|
+
}
|
|
287
|
+
async updateModuleItem(courseId, moduleId, itemId, data) {
|
|
288
|
+
const response = await this.client.put(`courses/${courseId}/modules/${moduleId}/items/${itemId}`, {
|
|
289
|
+
module_item: data
|
|
290
|
+
});
|
|
291
|
+
return response.data;
|
|
292
|
+
}
|
|
293
|
+
async deleteModuleItem(courseId, moduleId, itemId) {
|
|
294
|
+
await this.client.delete(`courses/${courseId}/modules/${moduleId}/items/${itemId}`);
|
|
295
|
+
return { deleted: true };
|
|
296
|
+
}
|
|
297
|
+
// --- File Uploads ---
|
|
298
|
+
async uploadFile(courseId, filePath, fileName, contentType, parentFolderId) {
|
|
299
|
+
// Step 1: Pre-flight request to Canvas
|
|
300
|
+
const preflightData = {
|
|
301
|
+
name: fileName,
|
|
302
|
+
content_type: contentType
|
|
303
|
+
};
|
|
304
|
+
if (parentFolderId) {
|
|
305
|
+
preflightData.parent_folder_id = parentFolderId;
|
|
306
|
+
}
|
|
307
|
+
const preflightResponse = await this.client.post(`courses/${courseId}/files`, preflightData);
|
|
308
|
+
const { upload_url, upload_params } = preflightResponse.data;
|
|
309
|
+
// Step 2: Upload to S3 (or other storage)
|
|
310
|
+
// Note: Using dynamic import for 'fs' and 'form-data' to avoid issues in some environments
|
|
311
|
+
const fs = await import('node:fs');
|
|
312
|
+
const FormData = (await import('form-data')).default;
|
|
313
|
+
const form = new FormData();
|
|
314
|
+
Object.entries(upload_params).forEach(([key, value]) => {
|
|
315
|
+
form.append(key, value);
|
|
316
|
+
});
|
|
317
|
+
form.append('file', fs.createReadStream(filePath));
|
|
318
|
+
const uploadResponse = await axios.post(upload_url, form, {
|
|
319
|
+
headers: form.getHeaders()
|
|
320
|
+
});
|
|
321
|
+
// Step 3: Handle redirection if necessary (Canvas sometimes returns a 303 or a Location header)
|
|
322
|
+
if (uploadResponse.status === 301 || uploadResponse.status === 201 || uploadResponse.headers.location) {
|
|
323
|
+
const finalUrl = uploadResponse.headers.location || uploadResponse.data.location;
|
|
324
|
+
if (finalUrl) {
|
|
325
|
+
const finalResponse = await this.client.get(finalUrl);
|
|
326
|
+
return finalResponse.data;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return uploadResponse.data;
|
|
330
|
+
}
|
|
331
|
+
async createQuiz(courseId, quiz) {
|
|
332
|
+
const response = await this.client.post(`courses/${courseId}/quizzes`, {
|
|
333
|
+
quiz: quiz
|
|
334
|
+
});
|
|
335
|
+
return response.data;
|
|
336
|
+
}
|
|
337
|
+
async updateQuiz(courseId, quizId, quiz) {
|
|
338
|
+
const response = await this.client.put(`courses/${courseId}/quizzes/${quizId}`, {
|
|
339
|
+
quiz: quiz
|
|
340
|
+
});
|
|
341
|
+
return response.data;
|
|
342
|
+
}
|
|
343
|
+
// --- Folders ---
|
|
344
|
+
async getFolders(courseId) {
|
|
345
|
+
return this.getAllPages(`courses/${courseId}/folders`);
|
|
346
|
+
}
|
|
347
|
+
async getFolder(folderId) {
|
|
348
|
+
const response = await this.client.get(`folders/${folderId}`);
|
|
349
|
+
return response.data;
|
|
350
|
+
}
|
|
351
|
+
async createFolder(courseId, name, parentFolderId) {
|
|
352
|
+
const data = { name };
|
|
353
|
+
if (parentFolderId) {
|
|
354
|
+
data.parent_folder_id = parentFolderId;
|
|
355
|
+
}
|
|
356
|
+
const response = await this.client.post(`courses/${courseId}/folders`, data);
|
|
357
|
+
return response.data;
|
|
358
|
+
}
|
|
359
|
+
async updateFolder(folderId, data) {
|
|
360
|
+
const response = await this.client.put(`folders/${folderId}`, data);
|
|
361
|
+
return response.data;
|
|
362
|
+
}
|
|
363
|
+
async deleteFolder(folderId, force = false) {
|
|
364
|
+
await this.client.delete(`folders/${folderId}`, { params: { force } });
|
|
365
|
+
return { deleted: true };
|
|
366
|
+
}
|
|
367
|
+
// --- Files (More) ---
|
|
368
|
+
async getFile(fileId) {
|
|
369
|
+
const response = await this.client.get(`files/${fileId}`);
|
|
370
|
+
return response.data;
|
|
371
|
+
}
|
|
372
|
+
async updateFile(fileId, data) {
|
|
373
|
+
const response = await this.client.put(`files/${fileId}`, data);
|
|
374
|
+
return response.data;
|
|
375
|
+
}
|
|
376
|
+
async deleteFile(fileId) {
|
|
377
|
+
await this.client.delete(`files/${fileId}`);
|
|
378
|
+
return { deleted: true };
|
|
379
|
+
}
|
|
380
|
+
// --- Rubrics ---
|
|
381
|
+
async createRubric(courseId, rubricData, rubricAssociationData) {
|
|
382
|
+
const payload = { rubric: rubricData };
|
|
383
|
+
if (rubricAssociationData) {
|
|
384
|
+
payload.rubric_association = rubricAssociationData;
|
|
385
|
+
}
|
|
386
|
+
const response = await this.client.post(`courses/${courseId}/rubrics`, payload);
|
|
387
|
+
return response.data;
|
|
388
|
+
}
|
|
389
|
+
async createRubricAssociation(courseId, associationData) {
|
|
390
|
+
const response = await this.client.post(`courses/${courseId}/rubric_associations`, {
|
|
391
|
+
rubric_association: associationData
|
|
392
|
+
});
|
|
393
|
+
return response.data;
|
|
394
|
+
}
|
|
395
|
+
// --- Appointment Groups ---
|
|
396
|
+
async listAppointmentGroups(scope = 'all', include) {
|
|
397
|
+
return this.getAllPages('appointment_groups', {
|
|
398
|
+
scope,
|
|
399
|
+
include
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
async getAppointmentGroup(id, include) {
|
|
403
|
+
const response = await this.client.get(`appointment_groups/${id}`, {
|
|
404
|
+
params: { include }
|
|
405
|
+
});
|
|
406
|
+
return response.data;
|
|
407
|
+
}
|
|
408
|
+
async createAppointmentGroup(data) {
|
|
409
|
+
const response = await this.client.post('appointment_groups', {
|
|
410
|
+
appointment_group: data
|
|
411
|
+
});
|
|
412
|
+
return response.data;
|
|
413
|
+
}
|
|
414
|
+
async updateAppointmentGroup(id, data) {
|
|
415
|
+
const response = await this.client.put(`appointment_groups/${id}`, {
|
|
416
|
+
appointment_group: data
|
|
417
|
+
});
|
|
418
|
+
return response.data;
|
|
419
|
+
}
|
|
420
|
+
async deleteAppointmentGroup(id, cancelReason) {
|
|
421
|
+
await this.client.delete(`appointment_groups/${id}`, {
|
|
422
|
+
params: { cancel_reason: cancelReason }
|
|
423
|
+
});
|
|
424
|
+
return { deleted: true };
|
|
425
|
+
}
|
|
426
|
+
async listAppointmentGroupUsers(id) {
|
|
427
|
+
return this.getAllPages(`appointment_groups/${id}/users`);
|
|
428
|
+
}
|
|
429
|
+
async listAppointmentGroupGroups(id) {
|
|
430
|
+
return this.getAllPages(`appointment_groups/${id}/groups`);
|
|
431
|
+
}
|
|
432
|
+
async getNextAppointment() {
|
|
433
|
+
const response = await this.client.get('appointment_groups/next_appointment');
|
|
434
|
+
return response.data;
|
|
435
|
+
}
|
|
436
|
+
// --- Groups and Group Categories ---
|
|
437
|
+
async getGroupCategories(courseId) {
|
|
438
|
+
return this.getAllPages(`courses/${courseId}/group_categories`);
|
|
439
|
+
}
|
|
440
|
+
async createGroupCategory(courseId, data) {
|
|
441
|
+
const response = await this.client.post(`courses/${courseId}/group_categories`, data);
|
|
442
|
+
return response.data;
|
|
443
|
+
}
|
|
444
|
+
async createGroup(groupCategoryId, data) {
|
|
445
|
+
const response = await this.client.post(`group_categories/${groupCategoryId}/groups`, data);
|
|
446
|
+
return response.data;
|
|
447
|
+
}
|
|
448
|
+
async getGroupsInCategory(groupCategoryId) {
|
|
449
|
+
return this.getAllPages(`group_categories/${groupCategoryId}/groups`);
|
|
450
|
+
}
|
|
451
|
+
async assignUnassignedMembers(groupCategoryId, sync = false) {
|
|
452
|
+
const response = await this.client.post(`group_categories/${groupCategoryId}/assign_unassigned_members`, { sync });
|
|
453
|
+
return response.data;
|
|
454
|
+
}
|
|
455
|
+
async addGroupMember(groupId, userId) {
|
|
456
|
+
const response = await this.client.post(`groups/${groupId}/memberships`, { user_id: userId });
|
|
457
|
+
return response.data;
|
|
458
|
+
}
|
|
459
|
+
}
|