canvas-lms-mcp 1.4.2 → 1.5.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 +5 -4
- package/dist/canvas/index.cjs +2 -2
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-XAOI3FO6.cjs → chunk-6HRYCB4C.cjs} +42 -1
- package/dist/chunk-6HRYCB4C.cjs.map +1 -0
- package/dist/{chunk-LZ3KWP3F.js → chunk-FNBRFPKK.js} +254 -168
- package/dist/chunk-FNBRFPKK.js.map +1 -0
- package/dist/{chunk-2BS2WOAF.cjs → chunk-MZE3RD3B.cjs} +109 -23
- package/dist/chunk-MZE3RD3B.cjs.map +1 -0
- package/dist/{chunk-XP3FFZQV.js → chunk-X7YODHAN.js} +42 -1
- package/dist/chunk-X7YODHAN.js.map +1 -0
- package/dist/http.cjs +3 -3
- package/dist/http.js +2 -2
- package/dist/server.cjs +3 -3
- package/dist/server.js +2 -2
- package/dist/stdio.cjs +3 -3
- package/dist/stdio.js +2 -2
- package/package.json +2 -2
- package/dist/chunk-2BS2WOAF.cjs.map +0 -1
- package/dist/chunk-LZ3KWP3F.js.map +0 -1
- package/dist/chunk-XAOI3FO6.cjs.map +0 -1
- package/dist/chunk-XP3FFZQV.js.map +0 -1
package/README.md
CHANGED
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
MCP server for [Canvas LMS](https://www.instructure.com/canvas). Read courses, assignments, submissions, rubrics, quizzes; grade, comment, manage course content, and handle Canvas admin workflows from any AI agent.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
104 tools across Canvas courses, assignments, submissions, gradebook history, rubrics, quizzes, files, users, groups, enrollments, discussions, modules, pages, calendar, conversations, peer reviews, accounts, analytics, outcomes, student workflows, dashboard, and health checks. Three deployment modes: stdio, HTTP, and library import.
|
|
14
14
|
|
|
15
15
|
## Comparison
|
|
16
16
|
|
|
17
17
|
| | canvas-lms-mcp | [vishalsachdev/canvas-mcp](https://github.com/vishalsachdev/canvas-mcp) | [DMontgomery40/mcp-canvas-lms](https://github.com/DMontgomery40/mcp-canvas-lms) |
|
|
18
18
|
|---|---|---|---|
|
|
19
19
|
| Language | TypeScript | Python | TypeScript |
|
|
20
|
-
| Tools |
|
|
20
|
+
| Tools | 104 | 80+ | 54 |
|
|
21
21
|
| License | [](https://github.com/bruchris/canvas-lms-mcp/blob/main/LICENSE) | [](https://github.com/vishalsachdev/canvas-mcp/blob/main/LICENSE) | [](https://github.com/DMontgomery40/mcp-canvas-lms/blob/main/LICENSE) |
|
|
22
22
|
| Last commit | [](https://github.com/bruchris/canvas-lms-mcp) | [](https://github.com/vishalsachdev/canvas-mcp) | [](https://github.com/DMontgomery40/mcp-canvas-lms) |
|
|
23
23
|
|
|
@@ -173,7 +173,7 @@ Once configured, try these prompts with your AI client:
|
|
|
173
173
|
|
|
174
174
|
## Tool Inventory
|
|
175
175
|
|
|
176
|
-
### All Registered Tools (
|
|
176
|
+
### All Registered Tools (104)
|
|
177
177
|
|
|
178
178
|
| Category | Tools |
|
|
179
179
|
|----------|-------|
|
|
@@ -184,6 +184,7 @@ Once configured, try these prompts with your AI client:
|
|
|
184
184
|
| Rubrics | `list_rubrics`, `get_rubric`, `get_rubric_assessment`, `submit_rubric_assessment` |
|
|
185
185
|
| Quizzes | `list_quizzes`, `get_quiz`, `list_quiz_submissions`, `list_quiz_questions`, `get_quiz_submission_answers`, `score_quiz_question` |
|
|
186
186
|
| Files | `list_files`, `list_folders`, `get_file`, `upload_file`, `delete_file` |
|
|
187
|
+
| Gradebook History | `list_gradebook_history_days`, `get_gradebook_history_day`, `list_gradebook_history_submissions`, `get_gradebook_history_feed` |
|
|
187
188
|
| Users | `list_students`, `get_user`, `get_profile`, `search_users`, `list_course_users` |
|
|
188
189
|
| Groups | `list_groups`, `list_group_members` |
|
|
189
190
|
| Enrollments | `list_enrollments`, `enroll_user`, `remove_enrollment` |
|
|
@@ -199,7 +200,7 @@ Once configured, try these prompts with your AI client:
|
|
|
199
200
|
| Student | `get_my_courses`, `get_my_grades`, `get_my_submissions`, `get_my_upcoming_assignments` |
|
|
200
201
|
| Dashboard | `get_dashboard_cards`, `get_todo_items`, `get_upcoming_events`, `get_missing_submissions` |
|
|
201
202
|
|
|
202
|
-
|
|
203
|
+
76 tools are read-only and 28 tools perform Canvas write operations.
|
|
203
204
|
|
|
204
205
|
All write tools require appropriate Canvas permissions. Canvas enforces its own permission model -- the MCP server does not bypass it.
|
|
205
206
|
|
package/dist/canvas/index.cjs
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _chunk6HRYCB4Ccjs = require('../chunk-6HRYCB4C.cjs');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
exports.CanvasApiError =
|
|
10
|
+
exports.CanvasApiError = _chunk6HRYCB4Ccjs.CanvasApiError; exports.CanvasClient = _chunk6HRYCB4Ccjs.CanvasClient; exports.CanvasHttpClient = _chunk6HRYCB4Ccjs.CanvasHttpClient;
|
|
11
11
|
//# sourceMappingURL=index.cjs.map
|
package/dist/canvas/index.js
CHANGED
|
@@ -1022,6 +1022,45 @@ var OutcomesModule = class {
|
|
|
1022
1022
|
}
|
|
1023
1023
|
};
|
|
1024
1024
|
|
|
1025
|
+
// src/canvas/gradebook-history.ts
|
|
1026
|
+
var GradebookHistoryModule = class {
|
|
1027
|
+
constructor(client) {
|
|
1028
|
+
this.client = client;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
async listDays(courseId) {
|
|
1032
|
+
return this.client.request(
|
|
1033
|
+
`/api/v1/courses/${courseId}/gradebook_history/days`
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
async getDay(courseId, date) {
|
|
1037
|
+
return this.client.request(
|
|
1038
|
+
`/api/v1/courses/${courseId}/gradebook_history/${encodeURIComponent(date)}`
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
async listSubmissions(courseId, date, graderId, assignmentId) {
|
|
1042
|
+
return this.client.request(
|
|
1043
|
+
`/api/v1/courses/${courseId}/gradebook_history/${encodeURIComponent(date)}/graders/${graderId}/assignments/${assignmentId}/submissions`
|
|
1044
|
+
);
|
|
1045
|
+
}
|
|
1046
|
+
async getFeed(courseId, params = {}) {
|
|
1047
|
+
const searchParams = new URLSearchParams();
|
|
1048
|
+
if (params.assignment_id !== void 0) {
|
|
1049
|
+
searchParams.set("assignment_id", String(params.assignment_id));
|
|
1050
|
+
}
|
|
1051
|
+
if (params.user_id !== void 0) {
|
|
1052
|
+
searchParams.set("user_id", String(params.user_id));
|
|
1053
|
+
}
|
|
1054
|
+
if (params.ascending !== void 0) {
|
|
1055
|
+
searchParams.set("ascending", String(params.ascending));
|
|
1056
|
+
}
|
|
1057
|
+
const query = searchParams.toString();
|
|
1058
|
+
return this.client.paginate(
|
|
1059
|
+
`/api/v1/courses/${courseId}/gradebook_history/feed${query ? `?${query}` : ""}`
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1025
1064
|
// src/canvas/index.ts
|
|
1026
1065
|
var CanvasClient = class {
|
|
1027
1066
|
|
|
@@ -1043,6 +1082,7 @@ var CanvasClient = class {
|
|
|
1043
1082
|
|
|
1044
1083
|
|
|
1045
1084
|
|
|
1085
|
+
|
|
1046
1086
|
|
|
1047
1087
|
constructor(config) {
|
|
1048
1088
|
this.client = new CanvasHttpClient(config);
|
|
@@ -1065,6 +1105,7 @@ var CanvasClient = class {
|
|
|
1065
1105
|
this.analytics = new AnalyticsModule(this.client);
|
|
1066
1106
|
this.dashboard = new DashboardModule(this.client);
|
|
1067
1107
|
this.outcomes = new OutcomesModule(this.client);
|
|
1108
|
+
this.gradebookHistory = new GradebookHistoryModule(this.client);
|
|
1068
1109
|
}
|
|
1069
1110
|
};
|
|
1070
1111
|
|
|
@@ -1080,4 +1121,4 @@ var CanvasClient = class {
|
|
|
1080
1121
|
|
|
1081
1122
|
|
|
1082
1123
|
exports.CanvasApiError = CanvasApiError; exports.CanvasHttpClient = CanvasHttpClient; exports.SEARCH_CONTENT_TYPES = SEARCH_CONTENT_TYPES; exports.OUTCOME_CONTEXT_TYPES = OUTCOME_CONTEXT_TYPES; exports.OUTCOME_DETAIL_LEVELS = OUTCOME_DETAIL_LEVELS; exports.OUTCOME_ROLLUP_AGGREGATE_STATS = OUTCOME_ROLLUP_AGGREGATE_STATS; exports.OUTCOME_ROLLUP_SORT_BY = OUTCOME_ROLLUP_SORT_BY; exports.OUTCOME_SORT_ORDER = OUTCOME_SORT_ORDER; exports.OUTCOME_EXCLUDE_OPTIONS = OUTCOME_EXCLUDE_OPTIONS; exports.CanvasClient = CanvasClient;
|
|
1083
|
-
//# sourceMappingURL=chunk-
|
|
1124
|
+
//# sourceMappingURL=chunk-6HRYCB4C.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/canvas-lms-mcp/canvas-lms-mcp/dist/chunk-6HRYCB4C.cjs","../src/canvas/client.ts","../src/canvas/courses.ts","../src/canvas/assignments.ts","../src/canvas/submissions.ts","../src/canvas/rubrics.ts","../src/canvas/quizzes.ts","../src/canvas/files.ts","../src/canvas/users.ts","../src/canvas/groups.ts","../src/canvas/enrollments.ts","../src/canvas/discussions.ts","../src/canvas/modules.ts","../src/canvas/pages.ts","../src/canvas/calendar.ts","../src/canvas/conversations.ts","../src/canvas/peer-reviews.ts","../src/canvas/accounts.ts","../src/canvas/analytics.ts","../src/canvas/dashboard.ts","../src/canvas/outcomes.ts","../src/canvas/gradebook-history.ts","../src/canvas/index.ts"],"names":["body"],"mappings":"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","file":"/home/runner/work/canvas-lms-mcp/canvas-lms-mcp/dist/chunk-6HRYCB4C.cjs","sourcesContent":[null,"import type { CanvasClientConfig, CanvasErrorResponse } from './types'\n\nexport class CanvasApiError extends Error {\n status: number\n endpoint: string\n\n constructor(message: string, status: number, endpoint: string) {\n super(message)\n this.name = 'CanvasApiError'\n this.status = status\n this.endpoint = endpoint\n }\n}\n\nconst DEFAULT_MAX_PAGINATION_PAGES = 1000\nconst USER_AGENT = 'canvas-lms-mcp/1.0'\n\nexport class CanvasHttpClient {\n private token: string\n private _baseUrl: string\n private maxPaginationPages: number\n\n constructor(config: CanvasClientConfig) {\n this.token = config.token\n this._baseUrl = config.baseUrl.replace(/\\/+$/, '')\n this.maxPaginationPages = config.maxPaginationPages ?? DEFAULT_MAX_PAGINATION_PAGES\n }\n\n get baseUrl(): string {\n return this._baseUrl\n }\n\n /**\n * Makes a single authenticated request to the Canvas API.\n * Returns `undefined` (typed as `T`) when Canvas responds with 204 No Content,\n * which is the expected response for DELETE operations.\n */\n async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n const url = endpoint.startsWith('http') ? endpoint : `${this._baseUrl}${endpoint}`\n\n const method = (options.method ?? 'GET').toUpperCase()\n if (options.body != null && (method === 'GET' || method === 'HEAD')) {\n throw new Error(\n `GET requests must not include a body (Canvas CloudFront CDN rejects them with 403): ${endpoint}`,\n )\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': USER_AGENT,\n }\n if (options.body) {\n headers['Content-Type'] = 'application/json'\n }\n\n const response = await fetch(url, {\n ...options,\n headers: {\n ...headers,\n ...options.headers,\n },\n })\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as CanvasErrorResponse\n const message =\n body.errors?.[0]?.message ?? body.message ?? `Canvas API error: ${response.status}`\n throw new CanvasApiError(message, response.status, endpoint)\n }\n\n if (response.status === 204) {\n return undefined as T\n }\n\n return response.json() as Promise<T>\n }\n\n async paginate<T>(endpoint: string, params?: Record<string, string>): Promise<T[]> {\n const url = new URL(`${this._baseUrl}${endpoint}`)\n url.searchParams.set('per_page', '100')\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value)\n }\n }\n\n const results: T[] = []\n let nextUrl: string | null = url.toString()\n let pages = 0\n\n while (nextUrl && pages < this.maxPaginationPages) {\n const response = await fetch(nextUrl, {\n headers: {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': USER_AGENT,\n },\n })\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as CanvasErrorResponse\n const message =\n body.errors?.[0]?.message ?? body.message ?? `Canvas API error: ${response.status}`\n throw new CanvasApiError(message, response.status, endpoint)\n }\n\n const data = (await response.json()) as T[]\n results.push(...data)\n pages++\n\n nextUrl = this.parseNextLink(response.headers.get('Link'))\n }\n\n return results\n }\n\n async paginateEnvelope<T>(\n endpoint: string,\n envelopeKey: string,\n params?: Record<string, string>,\n ): Promise<T[]> {\n const url = new URL(`${this._baseUrl}${endpoint}`)\n url.searchParams.set('per_page', '100')\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value)\n }\n }\n\n const results: T[] = []\n let nextUrl: string | null = url.toString()\n let pages = 0\n\n while (nextUrl && pages < this.maxPaginationPages) {\n const response = await fetch(nextUrl, {\n headers: {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': USER_AGENT,\n },\n })\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as CanvasErrorResponse\n const message =\n body.errors?.[0]?.message ?? body.message ?? `Canvas API error: ${response.status}`\n throw new CanvasApiError(message, response.status, endpoint)\n }\n\n const body = (await response.json()) as Record<string, T[]>\n const data = body[envelopeKey] ?? []\n results.push(...data)\n pages++\n\n nextUrl = this.parseNextLink(response.headers.get('Link'))\n }\n\n return results\n }\n\n private parseNextLink(linkHeader: string | null): string | null {\n if (!linkHeader) return null\n const match = linkHeader.match(/<([^>]+)>;\\s*rel=\"next\"/)\n return match?.[1] ?? null\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasCourse, CreateCourseParams, UpdateCourseParams } from './types'\n\nexport class CoursesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(params?: { enrollment_state?: string }): Promise<CanvasCourse[]> {\n const queryParams: Record<string, string> = {\n 'include[]': 'term',\n }\n if (params?.enrollment_state) {\n queryParams.enrollment_state = params.enrollment_state\n }\n return this.client.paginate<CanvasCourse>('/api/v1/courses', queryParams)\n }\n\n async get(courseId: number): Promise<CanvasCourse> {\n return this.client.request<CanvasCourse>(\n `/api/v1/courses/${courseId}?include[]=term&include[]=total_students`,\n )\n }\n\n async getSyllabus(courseId: number): Promise<string | null> {\n const course = await this.client.request<CanvasCourse>(\n `/api/v1/courses/${courseId}?include[]=syllabus_body`,\n )\n return course.syllabus_body ?? null\n }\n\n async create(params: CreateCourseParams): Promise<CanvasCourse> {\n const { account_id, ...courseFields } = params\n return this.client.request<CanvasCourse>(`/api/v1/accounts/${account_id}/courses`, {\n method: 'POST',\n body: JSON.stringify({ course: courseFields }),\n })\n }\n\n async update(courseId: number, params: UpdateCourseParams): Promise<CanvasCourse> {\n return this.client.request<CanvasCourse>(`/api/v1/courses/${courseId}`, {\n method: 'PUT',\n body: JSON.stringify({ course: params }),\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasAssignment,\n CanvasAssignmentGroup,\n CreateAssignmentParams,\n UpdateAssignmentParams,\n} from './types'\n\nexport class AssignmentsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasAssignment[]> {\n return this.client.paginate<CanvasAssignment>(`/api/v1/courses/${courseId}/assignments`)\n }\n\n async get(courseId: number, assignmentId: number): Promise<CanvasAssignment> {\n return this.client.request<CanvasAssignment>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}`,\n )\n }\n\n async listGroups(courseId: number): Promise<CanvasAssignmentGroup[]> {\n return this.client.paginate<CanvasAssignmentGroup>(\n `/api/v1/courses/${courseId}/assignment_groups`,\n )\n }\n\n async create(courseId: number, params: CreateAssignmentParams): Promise<CanvasAssignment> {\n return this.client.request<CanvasAssignment>(`/api/v1/courses/${courseId}/assignments`, {\n method: 'POST',\n body: JSON.stringify({ assignment: params }),\n })\n }\n\n async update(\n courseId: number,\n assignmentId: number,\n params: UpdateAssignmentParams,\n ): Promise<CanvasAssignment> {\n return this.client.request<CanvasAssignment>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}`,\n {\n method: 'PUT',\n body: JSON.stringify({ assignment: params }),\n },\n )\n }\n\n async delete(courseId: number, assignmentId: number): Promise<void> {\n await this.client.request<void>(`/api/v1/courses/${courseId}/assignments/${assignmentId}`, {\n method: 'DELETE',\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasSubmission } from './types'\n\nexport class SubmissionsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number, assignmentId: number): Promise<CanvasSubmission[]> {\n return this.client.paginate<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions`,\n { 'include[]': 'submission_comments' },\n )\n }\n\n async get(courseId: number, assignmentId: number, userId: number): Promise<CanvasSubmission> {\n return this.client.request<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}?include[]=submission_comments`,\n )\n }\n\n async grade(\n courseId: number,\n assignmentId: number,\n userId: number,\n grade: string,\n ): Promise<CanvasSubmission> {\n return this.client.request<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`,\n {\n method: 'PUT',\n body: JSON.stringify({ submission: { posted_grade: grade } }),\n },\n )\n }\n\n async comment(\n courseId: number,\n assignmentId: number,\n userId: number,\n comment: string,\n ): Promise<CanvasSubmission> {\n return this.client.request<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`,\n {\n method: 'PUT',\n body: JSON.stringify({\n comment: { text_comment: comment },\n }),\n },\n )\n }\n\n async listMy(courseId: number): Promise<CanvasSubmission[]> {\n return this.client.paginate<CanvasSubmission>(\n `/api/v1/courses/${courseId}/students/submissions`,\n { 'student_ids[]': 'self' },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasRubric, CanvasRubricAssessment, CanvasSubmission } from './types'\n\nexport class RubricsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasRubric[]> {\n return this.client.paginate<CanvasRubric>(`/api/v1/courses/${courseId}/rubrics`)\n }\n\n async get(courseId: number, rubricId: number): Promise<CanvasRubric> {\n return this.client.request<CanvasRubric>(`/api/v1/courses/${courseId}/rubrics/${rubricId}`)\n }\n\n async getAssessment(\n courseId: number,\n assignmentId: number,\n userId: number,\n ): Promise<CanvasSubmission> {\n return this.client.request<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}?include[]=rubric_assessment`,\n )\n }\n\n async submitAssessment(\n courseId: number,\n associationId: number,\n data: CanvasRubricAssessment['data'],\n ): Promise<CanvasRubricAssessment> {\n return this.client.request<CanvasRubricAssessment>(\n `/api/v1/courses/${courseId}/rubric_associations/${associationId}/rubric_assessments`,\n {\n method: 'POST',\n body: JSON.stringify({ rubric_assessment: { data } }),\n },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasQuiz,\n CanvasQuizSubmission,\n CanvasQuizQuestion,\n CanvasQuizSubmissionQuestion,\n} from './types'\n\nexport class QuizzesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasQuiz[]> {\n return this.client.paginate<CanvasQuiz>(`/api/v1/courses/${courseId}/quizzes`)\n }\n\n async get(courseId: number, quizId: number): Promise<CanvasQuiz> {\n return this.client.request<CanvasQuiz>(`/api/v1/courses/${courseId}/quizzes/${quizId}`)\n }\n\n async listSubmissions(courseId: number, quizId: number): Promise<CanvasQuizSubmission[]> {\n return this.client.paginateEnvelope<CanvasQuizSubmission>(\n `/api/v1/courses/${courseId}/quizzes/${quizId}/submissions`,\n 'quiz_submissions',\n )\n }\n\n async listQuestions(courseId: number, quizId: number): Promise<CanvasQuizQuestion[]> {\n return this.client.paginate<CanvasQuizQuestion>(\n `/api/v1/courses/${courseId}/quizzes/${quizId}/questions`,\n )\n }\n\n async getSubmissionAnswers(quizSubmissionId: number): Promise<CanvasQuizSubmissionQuestion[]> {\n return this.client.paginateEnvelope<CanvasQuizSubmissionQuestion>(\n `/api/v1/quiz_submissions/${quizSubmissionId}/questions`,\n 'quiz_submission_questions',\n { 'include[]': 'quiz_question' },\n )\n }\n\n async scoreQuestion(\n courseId: number,\n quizId: number,\n submissionId: number,\n questionId: number,\n score: number,\n comment?: string,\n attempt?: number,\n ): Promise<void> {\n const submission: Record<string, unknown> = {\n questions: {\n [questionId]: { score, comment },\n },\n }\n if (attempt !== undefined) {\n submission.attempt = attempt\n }\n const body: Record<string, unknown> = {\n quiz_submissions: [submission],\n }\n await this.client.request(\n `/api/v1/courses/${courseId}/quizzes/${quizId}/submissions/${submissionId}`,\n {\n method: 'PUT',\n body: JSON.stringify(body),\n },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasFile, CanvasFileUploadInfo, CanvasFolder } from './types'\n\nexport class FilesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasFile[]> {\n return this.client.paginate<CanvasFile>(`/api/v1/courses/${courseId}/files`)\n }\n\n async listFolders(courseId: number): Promise<CanvasFolder[]> {\n return this.client.paginate<CanvasFolder>(`/api/v1/courses/${courseId}/folders`)\n }\n\n async get(courseId: number, fileId: number): Promise<CanvasFile> {\n return this.client.request<CanvasFile>(`/api/v1/courses/${courseId}/files/${fileId}`)\n }\n\n async upload(\n courseId: number,\n name: string,\n contentBase64: string,\n contentType: string,\n parentFolderPath?: string,\n ): Promise<CanvasFile> {\n const content = Buffer.from(contentBase64, 'base64')\n if (content.toString('base64') !== contentBase64) {\n throw new Error('Invalid base64 content: string is not valid base64 encoding')\n }\n\n // Step 1: Notify Canvas of the pending upload\n const uploadBody: Record<string, string> = {\n name,\n content_type: contentType,\n size: String(content.length),\n }\n if (parentFolderPath) {\n uploadBody.parent_folder_path = parentFolderPath\n }\n\n const uploadInfo = await this.client.request<CanvasFileUploadInfo>(\n `/api/v1/courses/${courseId}/files`,\n { method: 'POST', body: JSON.stringify(uploadBody) },\n )\n\n // Step 2: POST file to upload_url (may be S3 — must NOT add Authorization header)\n const form = new FormData()\n for (const [key, value] of Object.entries(uploadInfo.upload_params)) {\n form.append(key, value)\n }\n form.append('file', new Blob([content], { type: contentType }), name)\n\n // Use redirect: 'manual' so we can handle the 303 → Canvas confirm step ourselves\n let s3Response: Response\n try {\n s3Response = await fetch(uploadInfo.upload_url, {\n method: 'POST',\n body: form,\n redirect: 'manual',\n })\n } catch (networkErr) {\n let hostname: string\n try {\n hostname = new URL(uploadInfo.upload_url).hostname\n } catch {\n hostname = uploadInfo.upload_url\n }\n throw new Error(`Unable to reach file storage (${hostname})`, { cause: networkErr })\n }\n\n // Step 3: If S3 returned a redirect, POST to Canvas confirm URL\n if (s3Response.status >= 300 && s3Response.status < 400) {\n const confirmUrl = s3Response.headers.get('location')\n if (!confirmUrl) {\n throw new Error(`File upload redirect missing Location header (HTTP ${s3Response.status})`)\n }\n let confirmHostname: string\n try {\n confirmHostname = new URL(confirmUrl).hostname\n } catch {\n confirmHostname = ''\n }\n const canvasHostname = new URL(this.client.baseUrl).hostname\n if (confirmHostname !== canvasHostname) {\n throw new Error(\n `File upload redirect points to unexpected host (${confirmHostname}); expected Canvas host (${canvasHostname})`,\n )\n }\n return this.client.request<CanvasFile>(confirmUrl, { method: 'POST' })\n }\n\n if (!s3Response.ok) {\n const body = await s3Response.text()\n throw new Error(\n `File upload to storage failed (HTTP ${s3Response.status}): ${body.slice(0, 500)}`,\n )\n }\n\n const responseText = await s3Response.text()\n let parsed: unknown\n try {\n parsed = JSON.parse(responseText)\n } catch (parseErr) {\n throw new Error(\n `File upload response is not valid JSON (${parseErr instanceof Error ? parseErr.message : String(parseErr)}): ${responseText.slice(0, 500)}`,\n { cause: parseErr },\n )\n }\n if (typeof parsed !== 'object' || parsed === null || !('id' in parsed)) {\n throw new Error(\n `File upload returned unexpected response (no file ID): ${responseText.slice(0, 500)}`,\n )\n }\n return parsed as CanvasFile\n }\n\n async delete(fileId: number): Promise<CanvasFile> {\n return this.client.request<CanvasFile>(`/api/v1/files/${fileId}`, { method: 'DELETE' })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasUpcomingEvent, CanvasUser, CanvasUserProfile } from './types'\n\nexport class UsersModule {\n constructor(private client: CanvasHttpClient) {}\n\n async listStudents(courseId: number): Promise<CanvasUser[]> {\n return this.client.paginate<CanvasUser>(`/api/v1/courses/${courseId}/users`, {\n 'enrollment_type[]': 'student',\n })\n }\n\n async get(userId: number): Promise<CanvasUser> {\n return this.client.request<CanvasUser>(`/api/v1/users/${userId}`)\n }\n\n async getProfile(): Promise<CanvasUserProfile> {\n return this.client.request<CanvasUserProfile>('/api/v1/users/self/profile')\n }\n\n async searchUsers(\n accountId: number,\n searchTerm: string,\n sort?: string,\n order?: string,\n ): Promise<CanvasUser[]> {\n const params: Record<string, string> = { search_term: searchTerm }\n if (sort) params.sort = sort\n if (order) params.order = order\n return this.client.paginate<CanvasUser>(`/api/v1/accounts/${accountId}/users`, params)\n }\n\n async listCourseUsers(courseId: number, enrollmentType?: string): Promise<CanvasUser[]> {\n const params: Record<string, string> = {}\n if (enrollmentType) params['enrollment_type[]'] = enrollmentType\n return this.client.paginate<CanvasUser>(`/api/v1/courses/${courseId}/users`, params)\n }\n\n async getUpcomingAssignments(): Promise<CanvasUpcomingEvent[]> {\n return this.client.request<CanvasUpcomingEvent[]>(\n '/api/v1/users/self/upcoming_events?type=Assignment',\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasGroup, CanvasUser } from './types'\n\nexport class GroupsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasGroup[]> {\n return this.client.paginate<CanvasGroup>(`/api/v1/courses/${courseId}/groups`)\n }\n\n async listMembers(groupId: number): Promise<CanvasUser[]> {\n return this.client.paginate<CanvasUser>(`/api/v1/groups/${groupId}/users`)\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasEnrollment } from './types'\n\nexport class EnrollmentsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(): Promise<CanvasEnrollment[]> {\n return this.client.paginate<CanvasEnrollment>('/api/v1/users/self/enrollments')\n }\n\n async enroll(\n courseId: number,\n userId: number,\n type: string,\n enrollmentState?: string,\n ): Promise<CanvasEnrollment> {\n const body: Record<string, unknown> = {\n enrollment: {\n user_id: userId,\n type,\n ...(enrollmentState ? { enrollment_state: enrollmentState } : {}),\n },\n }\n return this.client.request<CanvasEnrollment>(`/api/v1/courses/${courseId}/enrollments`, {\n method: 'POST',\n body: JSON.stringify(body),\n })\n }\n\n async remove(courseId: number, enrollmentId: number, task: string): Promise<CanvasEnrollment> {\n return this.client.request<CanvasEnrollment>(\n `/api/v1/courses/${courseId}/enrollments/${enrollmentId}?task=${encodeURIComponent(task)}`,\n { method: 'DELETE' },\n )\n }\n\n async listMyGrades(courseId?: number): Promise<CanvasEnrollment[]> {\n if (courseId !== undefined) {\n return this.client.paginate<CanvasEnrollment>(`/api/v1/courses/${courseId}/enrollments`, {\n user_id: 'self',\n 'include[]': 'grades',\n })\n }\n return this.client.paginate<CanvasEnrollment>('/api/v1/users/self/enrollments', {\n 'include[]': 'grades',\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasDiscussionTopic,\n CanvasDiscussionEntry,\n CanvasAnnouncement,\n CreateDiscussionParams,\n UpdateDiscussionParams,\n} from './types'\n\nexport class DiscussionsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasDiscussionTopic[]> {\n return this.client.paginate<CanvasDiscussionTopic>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n )\n }\n\n async get(courseId: number, topicId: number): Promise<CanvasDiscussionTopic> {\n return this.client.request<CanvasDiscussionTopic>(\n `/api/v1/courses/${courseId}/discussion_topics/${topicId}?include[]=all_dates`,\n )\n }\n\n async listAnnouncements(courseId: number): Promise<CanvasAnnouncement[]> {\n return this.client.paginate<CanvasAnnouncement>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n { only_announcements: 'true' },\n )\n }\n\n async postEntry(\n courseId: number,\n topicId: number,\n message: string,\n ): Promise<CanvasDiscussionEntry> {\n return this.client.request<CanvasDiscussionEntry>(\n `/api/v1/courses/${courseId}/discussion_topics/${topicId}/entries`,\n {\n method: 'POST',\n body: JSON.stringify({ message }),\n },\n )\n }\n\n async create(courseId: number, params: CreateDiscussionParams): Promise<CanvasDiscussionTopic> {\n return this.client.request<CanvasDiscussionTopic>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n {\n method: 'POST',\n body: JSON.stringify(params),\n },\n )\n }\n\n async update(\n courseId: number,\n topicId: number,\n params: UpdateDiscussionParams,\n ): Promise<CanvasDiscussionTopic> {\n return this.client.request<CanvasDiscussionTopic>(\n `/api/v1/courses/${courseId}/discussion_topics/${topicId}`,\n {\n method: 'PUT',\n body: JSON.stringify(params),\n },\n )\n }\n\n async delete(courseId: number, topicId: number): Promise<void> {\n await this.client.request<void>(`/api/v1/courses/${courseId}/discussion_topics/${topicId}`, {\n method: 'DELETE',\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasModule, CanvasModuleItem } from './types'\n\nexport class ModulesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasModule[]> {\n return this.client.paginate<CanvasModule>(`/api/v1/courses/${courseId}/modules`)\n }\n\n async get(courseId: number, moduleId: number): Promise<CanvasModule> {\n return this.client.request<CanvasModule>(`/api/v1/courses/${courseId}/modules/${moduleId}`)\n }\n\n async listItems(courseId: number, moduleId: number): Promise<CanvasModuleItem[]> {\n return this.client.paginate<CanvasModuleItem>(\n `/api/v1/courses/${courseId}/modules/${moduleId}/items`,\n )\n }\n\n async create(\n courseId: number,\n params: {\n name: string\n position?: number\n unlock_at?: string\n prerequisite_module_ids?: number[]\n },\n ): Promise<CanvasModule> {\n return this.client.request<CanvasModule>(`/api/v1/courses/${courseId}/modules`, {\n method: 'POST',\n body: JSON.stringify({ module: params }),\n })\n }\n\n async update(\n courseId: number,\n moduleId: number,\n params: { name?: string; position?: number; published?: boolean },\n ): Promise<CanvasModule> {\n return this.client.request<CanvasModule>(`/api/v1/courses/${courseId}/modules/${moduleId}`, {\n method: 'PUT',\n body: JSON.stringify({ module: params }),\n })\n }\n\n async createItem(\n courseId: number,\n moduleId: number,\n params: {\n title: string\n type: string\n content_id?: number\n external_url?: string\n position?: number\n },\n ): Promise<CanvasModuleItem> {\n return this.client.request<CanvasModuleItem>(\n `/api/v1/courses/${courseId}/modules/${moduleId}/items`,\n {\n method: 'POST',\n body: JSON.stringify({ module_item: params }),\n },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasPage } from './types'\n\nexport class PagesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasPage[]> {\n return this.client.paginate<CanvasPage>(`/api/v1/courses/${courseId}/pages`)\n }\n\n async get(courseId: number, pageUrl: string): Promise<CanvasPage> {\n return this.client.request<CanvasPage>(\n `/api/v1/courses/${courseId}/pages/${encodeURIComponent(pageUrl)}`,\n )\n }\n\n async create(\n courseId: number,\n params: { title: string; body?: string; published?: boolean; editing_roles?: string },\n ): Promise<CanvasPage> {\n return this.client.request<CanvasPage>(`/api/v1/courses/${courseId}/pages`, {\n method: 'POST',\n body: JSON.stringify({ wiki_page: params }),\n })\n }\n\n async update(\n courseId: number,\n pageUrl: string,\n params: { title?: string; body?: string; published?: boolean; editing_roles?: string },\n ): Promise<CanvasPage> {\n return this.client.request<CanvasPage>(\n `/api/v1/courses/${courseId}/pages/${encodeURIComponent(pageUrl)}`,\n {\n method: 'PUT',\n body: JSON.stringify({ wiki_page: params }),\n },\n )\n }\n\n async delete(courseId: number, pageUrl: string): Promise<void> {\n await this.client.request<void>(\n `/api/v1/courses/${courseId}/pages/${encodeURIComponent(pageUrl)}`,\n { method: 'DELETE' },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasCalendarEvent } from './types'\n\nexport class CalendarModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasCalendarEvent[]> {\n return this.client.paginate<CanvasCalendarEvent>('/api/v1/calendar_events', {\n 'context_codes[]': `course_${courseId}`,\n })\n }\n\n async createEvent(params: {\n context_code: string\n title: string\n start_at: string\n end_at?: string\n description?: string\n location_name?: string\n }): Promise<CanvasCalendarEvent> {\n return this.client.request<CanvasCalendarEvent>('/api/v1/calendar_events', {\n method: 'POST',\n body: JSON.stringify({ calendar_event: params }),\n })\n }\n\n async updateEvent(\n eventId: number,\n params: {\n title?: string\n start_at?: string\n end_at?: string\n description?: string\n location_name?: string\n },\n ): Promise<CanvasCalendarEvent> {\n return this.client.request<CanvasCalendarEvent>(`/api/v1/calendar_events/${eventId}`, {\n method: 'PUT',\n body: JSON.stringify({ calendar_event: params }),\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasConversation,\n CanvasConversationDetail,\n CanvasConversationUnreadCount,\n} from './types'\n\nexport class ConversationsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(): Promise<CanvasConversation[]> {\n return this.client.paginate<CanvasConversation>('/api/v1/conversations')\n }\n\n async get(conversationId: number): Promise<CanvasConversationDetail> {\n return this.client.request<CanvasConversationDetail>(`/api/v1/conversations/${conversationId}`)\n }\n\n async getUnreadCount(): Promise<CanvasConversationUnreadCount> {\n const raw = await this.client.request<{ unread_count: string }>(\n '/api/v1/conversations/unread_count',\n )\n const count = parseInt(raw.unread_count, 10)\n if (Number.isNaN(count)) {\n throw new Error(\n `Canvas returned unexpected unread_count value: ${JSON.stringify(raw.unread_count)}`,\n )\n }\n return { unread_count: count }\n }\n\n async send(recipients: string[], subject: string, body: string): Promise<CanvasConversation[]> {\n return this.client.request<CanvasConversation[]>('/api/v1/conversations', {\n method: 'POST',\n body: JSON.stringify({\n recipients,\n subject,\n body,\n }),\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasPeerReview } from './types'\n\nexport class PeerReviewsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async listForAssignment(courseId: number, assignmentId: number): Promise<CanvasPeerReview[]> {\n return this.client.paginate<CanvasPeerReview>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/peer_reviews`,\n )\n }\n\n async listForSubmission(\n courseId: number,\n assignmentId: number,\n submissionId: number,\n ): Promise<CanvasPeerReview[]> {\n return this.client.paginate<CanvasPeerReview>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${submissionId}/peer_reviews`,\n )\n }\n\n async create(\n courseId: number,\n assignmentId: number,\n submissionId: number,\n userId: number,\n ): Promise<CanvasPeerReview> {\n return this.client.request<CanvasPeerReview>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${submissionId}/peer_reviews`,\n {\n method: 'POST',\n body: JSON.stringify({ user_id: userId }),\n },\n )\n }\n\n async delete(\n courseId: number,\n assignmentId: number,\n submissionId: number,\n userId: number,\n ): Promise<void> {\n await this.client.request<void>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${submissionId}/peer_reviews?user_id=${userId}`,\n { method: 'DELETE' },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasAccount, CanvasAccountReport, CanvasCourse, CanvasUser } from './types'\n\nexport class AccountsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async get(accountId: number): Promise<CanvasAccount> {\n return this.client.request<CanvasAccount>(`/api/v1/accounts/${accountId}`)\n }\n\n async list(): Promise<CanvasAccount[]> {\n return this.client.paginate<CanvasAccount>('/api/v1/accounts')\n }\n\n async listSubAccounts(accountId: number): Promise<CanvasAccount[]> {\n return this.client.paginate<CanvasAccount>(`/api/v1/accounts/${accountId}/sub_accounts`)\n }\n\n async listCourses(accountId: number, params?: { search_term?: string }): Promise<CanvasCourse[]> {\n const query: Record<string, string> = {}\n if (params?.search_term) query.search_term = params.search_term\n return this.client.paginate<CanvasCourse>(\n `/api/v1/accounts/${accountId}/courses`,\n Object.keys(query).length ? query : undefined,\n )\n }\n\n async listUsers(accountId: number, params?: { search_term?: string }): Promise<CanvasUser[]> {\n const query: Record<string, string> = {}\n if (params?.search_term) query.search_term = params.search_term\n return this.client.paginate<CanvasUser>(\n `/api/v1/accounts/${accountId}/users`,\n Object.keys(query).length ? query : undefined,\n )\n }\n\n async getReports(accountId: number): Promise<CanvasAccountReport[]> {\n return this.client.request<CanvasAccountReport[]>(`/api/v1/accounts/${accountId}/reports`)\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasCourseActivitySummary,\n CanvasStudentActivitySummary,\n CanvasActivityStreamItem,\n CourseSearchResult,\n} from './types'\n\nexport const SEARCH_CONTENT_TYPES = [\n 'pages',\n 'discussions',\n 'assignments',\n 'announcements',\n] as const\nexport type SearchContentType = (typeof SEARCH_CONTENT_TYPES)[number]\n\nexport class AnalyticsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async searchContentType(\n courseId: number,\n searchTerm: string,\n type: SearchContentType,\n ): Promise<CourseSearchResult[]> {\n switch (type) {\n case 'pages': {\n const pages = await this.client.paginate<{ page_id: number; title: string; url: string }>(\n `/api/v1/courses/${courseId}/pages`,\n { search_term: searchTerm },\n )\n return pages.map((p) => ({\n id: p.page_id,\n title: p.title,\n type: 'page' as const,\n url: p.url,\n course_id: courseId,\n }))\n }\n case 'assignments': {\n const assignments = await this.client.paginate<{ id: number; name: string }>(\n `/api/v1/courses/${courseId}/assignments`,\n { search_term: searchTerm },\n )\n return assignments.map((a) => ({\n id: a.id,\n title: a.name,\n type: 'assignment' as const,\n course_id: courseId,\n }))\n }\n case 'discussions': {\n const discussions = await this.client.paginate<{ id: number; title: string }>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n { search_term: searchTerm },\n )\n return discussions.map((d) => ({\n id: d.id,\n title: d.title,\n type: 'discussion' as const,\n course_id: courseId,\n }))\n }\n case 'announcements': {\n const announcements = await this.client.paginate<{ id: number; title: string }>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n { search_term: searchTerm, only_announcements: 'true' },\n )\n return announcements.map((a) => ({\n id: a.id,\n title: a.title,\n type: 'announcement' as const,\n course_id: courseId,\n }))\n }\n default: {\n const _exhaustive: never = type\n throw new Error(`Unknown content type: ${_exhaustive}`)\n }\n }\n }\n\n async getCourseActivity(courseId: number): Promise<CanvasCourseActivitySummary[]> {\n return this.client.request<CanvasCourseActivitySummary[]>(\n `/api/v1/courses/${courseId}/analytics/activity`,\n )\n }\n\n async getStudentActivity(\n courseId: number,\n studentId: number,\n ): Promise<CanvasStudentActivitySummary> {\n return this.client.request<CanvasStudentActivitySummary>(\n `/api/v1/courses/${courseId}/analytics/users/${studentId}/activity`,\n )\n }\n\n async getCourseActivityStream(courseId: number): Promise<CanvasActivityStreamItem[]> {\n return this.client.request<CanvasActivityStreamItem[]>(\n `/api/v1/courses/${courseId}/activity_stream/summary`,\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasDashboardCard,\n CanvasTodoItem,\n CanvasUpcomingEvent,\n CanvasMissingSubmission,\n} from './types'\n\nexport class DashboardModule {\n constructor(private client: CanvasHttpClient) {}\n\n async getDashboardCards(): Promise<CanvasDashboardCard[]> {\n return this.client.request<CanvasDashboardCard[]>('/api/v1/dashboard/dashboard_cards')\n }\n\n async getTodoItems(): Promise<CanvasTodoItem[]> {\n return this.client.paginate<CanvasTodoItem>('/api/v1/users/self/todo')\n }\n\n async getUpcomingEvents(): Promise<CanvasUpcomingEvent[]> {\n return this.client.request<CanvasUpcomingEvent[]>('/api/v1/users/self/upcoming_events')\n }\n\n async getMissingSubmissions(): Promise<CanvasMissingSubmission[]> {\n return this.client.paginate<CanvasMissingSubmission>('/api/v1/users/self/missing_submissions')\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasOutcomeContextType,\n CanvasOutcome,\n CanvasOutcomeAlignment,\n CanvasOutcomeContributingScoresResponse,\n CanvasOutcomeGroup,\n CanvasOutcomeLink,\n CanvasOutcomeMasteryDistributionResponse,\n CanvasOutcomeResultsResponse,\n CanvasOutcomeRollupsResponse,\n} from './types'\n\nexport const OUTCOME_CONTEXT_TYPES = ['account', 'course'] as const\nexport const OUTCOME_DETAIL_LEVELS = ['abbrev', 'full'] as const\nexport const OUTCOME_ROLLUP_AGGREGATE_STATS = ['mean', 'median'] as const\nexport const OUTCOME_ROLLUP_SORT_BY = ['student', 'outcome'] as const\nexport const OUTCOME_SORT_ORDER = ['asc', 'desc'] as const\nexport const OUTCOME_EXCLUDE_OPTIONS = ['missing_user_rollups', 'missing_outcome_results'] as const\n\ntype ContextPathSuffix = 'outcome_groups' | 'outcome_group_links' | 'root_outcome_group'\n\nexport class OutcomesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async getRootOutcomeGroup(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n ): Promise<CanvasOutcomeGroup> {\n return this.client.request<CanvasOutcomeGroup>(this.buildContextPath(contextType, contextId))\n }\n\n async listOutcomeGroups(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n ): Promise<CanvasOutcomeGroup[]> {\n return this.client.paginate<CanvasOutcomeGroup>(\n this.buildContextPath(contextType, contextId, 'outcome_groups'),\n )\n }\n\n async listOutcomeGroupLinks(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n options?: { outcome_style?: 'abbrev' | 'full'; outcome_group_style?: 'abbrev' | 'full' },\n ): Promise<CanvasOutcomeLink[]> {\n return this.client.paginate<CanvasOutcomeLink>(\n this.withQuery(this.buildContextPath(contextType, contextId, 'outcome_group_links'), {\n outcome_style: options?.outcome_style,\n outcome_group_style: options?.outcome_group_style,\n }),\n )\n }\n\n async getOutcomeGroup(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n groupId: number,\n ): Promise<CanvasOutcomeGroup> {\n return this.client.request<CanvasOutcomeGroup>(\n `${this.buildContextPath(contextType, contextId, 'outcome_groups')}/${groupId}`,\n )\n }\n\n async listGroupOutcomes(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n groupId: number,\n options?: { outcome_style?: 'abbrev' | 'full' },\n ): Promise<CanvasOutcomeLink[]> {\n return this.client.paginate<CanvasOutcomeLink>(\n this.withQuery(\n `${this.buildContextPath(contextType, contextId, 'outcome_groups')}/${groupId}/outcomes`,\n { outcome_style: options?.outcome_style },\n ),\n )\n }\n\n async listGroupSubgroups(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n groupId: number,\n ): Promise<CanvasOutcomeGroup[]> {\n return this.client.paginate<CanvasOutcomeGroup>(\n `${this.buildContextPath(contextType, contextId, 'outcome_groups')}/${groupId}/subgroups`,\n )\n }\n\n async getOutcome(\n outcomeId: number,\n options?: { add_defaults?: boolean },\n ): Promise<CanvasOutcome> {\n return this.client.request<CanvasOutcome>(\n this.withQuery(`/api/v1/outcomes/${outcomeId}`, {\n add_defaults: options?.add_defaults,\n }),\n )\n }\n\n async getOutcomeAlignments(\n courseId: number,\n options?: { student_id?: number; assignment_id?: number },\n ): Promise<CanvasOutcomeAlignment[]> {\n return this.client.request<CanvasOutcomeAlignment[]>(\n this.withQuery(`/api/v1/courses/${courseId}/outcome_alignments`, {\n student_id: options?.student_id,\n assignment_id: options?.assignment_id,\n }),\n )\n }\n\n async getOutcomeResults(\n courseId: number,\n options?: {\n user_ids?: Array<number | string>\n outcome_ids?: number[]\n include_alignments?: boolean\n include_hidden?: boolean\n },\n ): Promise<CanvasOutcomeResultsResponse> {\n return this.client.request<CanvasOutcomeResultsResponse>(\n this.withQuery(`/api/v1/courses/${courseId}/outcome_results`, {\n 'user_ids[]': options?.user_ids,\n 'outcome_ids[]': options?.outcome_ids,\n 'include[]': options?.include_alignments ? ['alignments'] : undefined,\n include_hidden: options?.include_hidden,\n }),\n )\n }\n\n async getOutcomeRollups(\n courseId: number,\n options?: {\n aggregate?: 'course'\n aggregate_stat?: 'mean' | 'median'\n user_ids?: Array<number | string>\n outcome_ids?: number[]\n include_courses?: boolean\n exclude?: Array<'missing_user_rollups' | 'missing_outcome_results'>\n sort_by?: 'student' | 'outcome'\n sort_outcome_id?: number\n sort_order?: 'asc' | 'desc'\n add_defaults?: boolean\n },\n ): Promise<CanvasOutcomeRollupsResponse> {\n return this.client.request<CanvasOutcomeRollupsResponse>(\n this.withQuery(`/api/v1/courses/${courseId}/outcome_rollups`, {\n aggregate: options?.aggregate,\n aggregate_stat: options?.aggregate_stat,\n 'user_ids[]': options?.user_ids,\n 'outcome_ids[]': options?.outcome_ids,\n 'include[]': options?.include_courses ? ['courses'] : undefined,\n 'exclude[]': options?.exclude,\n sort_by: options?.sort_by,\n sort_outcome_id: options?.sort_outcome_id,\n sort_order: options?.sort_order,\n add_defaults: options?.add_defaults,\n }),\n )\n }\n\n async getOutcomeContributingScores(\n courseId: number,\n outcomeId: number,\n options?: {\n user_ids?: Array<number | string>\n only_assignment_alignments?: boolean\n show_unpublished_assignments?: boolean\n },\n ): Promise<CanvasOutcomeContributingScoresResponse> {\n return this.client.request<CanvasOutcomeContributingScoresResponse>(\n this.withQuery(`/api/v1/courses/${courseId}/outcomes/${outcomeId}/contributing_scores`, {\n 'user_ids[]': options?.user_ids,\n only_assignment_alignments: options?.only_assignment_alignments,\n show_unpublished_assignments: options?.show_unpublished_assignments,\n }),\n )\n }\n\n async getOutcomeMasteryDistribution(\n courseId: number,\n options?: {\n exclude?: Array<'missing_user_rollups' | 'missing_outcome_results'>\n outcome_ids?: number[]\n student_ids?: Array<number | string>\n include_alignment_distributions?: boolean\n only_assignment_alignments?: boolean\n show_unpublished_assignments?: boolean\n add_defaults?: boolean\n },\n ): Promise<CanvasOutcomeMasteryDistributionResponse> {\n return this.client.request<CanvasOutcomeMasteryDistributionResponse>(\n this.withQuery(`/api/v1/courses/${courseId}/outcome_mastery_distribution`, {\n 'exclude[]': options?.exclude,\n 'outcome_ids[]': options?.outcome_ids,\n 'student_ids[]': options?.student_ids,\n 'include[]': options?.include_alignment_distributions\n ? ['alignment_distributions']\n : undefined,\n only_assignment_alignments: options?.only_assignment_alignments,\n show_unpublished_assignments: options?.show_unpublished_assignments,\n add_defaults: options?.add_defaults,\n }),\n )\n }\n\n private buildContextPath(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n suffix: ContextPathSuffix = 'root_outcome_group',\n ): string {\n const segment = contextType === 'account' ? 'accounts' : 'courses'\n return `/api/v1/${segment}/${contextId}/${suffix}`\n }\n\n private withQuery(\n endpoint: string,\n params: Record<string, string | number | boolean | Array<string | number> | undefined>,\n ): string {\n const searchParams = new URLSearchParams()\n\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue\n if (Array.isArray(value)) {\n for (const item of value) searchParams.append(key, String(item))\n continue\n }\n searchParams.set(key, String(value))\n }\n\n const query = searchParams.toString()\n return query ? `${endpoint}?${query}` : endpoint\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasGradebookHistoryDay,\n CanvasGradebookHistoryGrader,\n CanvasGradebookHistorySubmission,\n CanvasGradebookHistorySubmissionVersion,\n} from './types'\n\nexport interface GradebookHistoryFeedParams {\n assignment_id?: number\n user_id?: number\n ascending?: boolean\n}\n\nexport class GradebookHistoryModule {\n constructor(private client: CanvasHttpClient) {}\n\n async listDays(courseId: number): Promise<CanvasGradebookHistoryDay[]> {\n return this.client.request<CanvasGradebookHistoryDay[]>(\n `/api/v1/courses/${courseId}/gradebook_history/days`,\n )\n }\n\n async getDay(courseId: number, date: string): Promise<CanvasGradebookHistoryGrader[]> {\n return this.client.request<CanvasGradebookHistoryGrader[]>(\n `/api/v1/courses/${courseId}/gradebook_history/${encodeURIComponent(date)}`,\n )\n }\n\n async listSubmissions(\n courseId: number,\n date: string,\n graderId: number,\n assignmentId: number,\n ): Promise<CanvasGradebookHistorySubmission[]> {\n return this.client.request<CanvasGradebookHistorySubmission[]>(\n `/api/v1/courses/${courseId}/gradebook_history/${encodeURIComponent(date)}/graders/${graderId}/assignments/${assignmentId}/submissions`,\n )\n }\n\n async getFeed(\n courseId: number,\n params: GradebookHistoryFeedParams = {},\n ): Promise<CanvasGradebookHistorySubmissionVersion[]> {\n const searchParams = new URLSearchParams()\n\n if (params.assignment_id !== undefined) {\n searchParams.set('assignment_id', String(params.assignment_id))\n }\n if (params.user_id !== undefined) {\n searchParams.set('user_id', String(params.user_id))\n }\n if (params.ascending !== undefined) {\n searchParams.set('ascending', String(params.ascending))\n }\n\n const query = searchParams.toString()\n\n return this.client.paginate<CanvasGradebookHistorySubmissionVersion>(\n `/api/v1/courses/${courseId}/gradebook_history/feed${query ? `?${query}` : ''}`,\n )\n }\n}\n","import { CanvasHttpClient } from './client'\nimport type { CanvasClientConfig } from './types'\nimport { CoursesModule } from './courses'\nimport { AssignmentsModule } from './assignments'\nimport { SubmissionsModule } from './submissions'\nimport { RubricsModule } from './rubrics'\nimport { QuizzesModule } from './quizzes'\nimport { FilesModule } from './files'\nimport { UsersModule } from './users'\nimport { GroupsModule } from './groups'\nimport { EnrollmentsModule } from './enrollments'\nimport { DiscussionsModule } from './discussions'\nimport { ModulesModule } from './modules'\nimport { PagesModule } from './pages'\nimport { CalendarModule } from './calendar'\nimport { ConversationsModule } from './conversations'\nimport { PeerReviewsModule } from './peer-reviews'\nimport { AccountsModule } from './accounts'\nimport { AnalyticsModule } from './analytics'\nimport { DashboardModule } from './dashboard'\nimport { OutcomesModule } from './outcomes'\nimport { GradebookHistoryModule } from './gradebook-history'\n\nexport class CanvasClient {\n private client: CanvasHttpClient\n courses: CoursesModule\n assignments: AssignmentsModule\n submissions: SubmissionsModule\n rubrics: RubricsModule\n quizzes: QuizzesModule\n files: FilesModule\n users: UsersModule\n groups: GroupsModule\n enrollments: EnrollmentsModule\n discussions: DiscussionsModule\n modules: ModulesModule\n pages: PagesModule\n calendar: CalendarModule\n conversations: ConversationsModule\n peerReviews: PeerReviewsModule\n accounts: AccountsModule\n analytics: AnalyticsModule\n dashboard: DashboardModule\n outcomes: OutcomesModule\n gradebookHistory: GradebookHistoryModule\n\n constructor(config: CanvasClientConfig) {\n this.client = new CanvasHttpClient(config)\n this.courses = new CoursesModule(this.client)\n this.assignments = new AssignmentsModule(this.client)\n this.submissions = new SubmissionsModule(this.client)\n this.rubrics = new RubricsModule(this.client)\n this.quizzes = new QuizzesModule(this.client)\n this.files = new FilesModule(this.client)\n this.users = new UsersModule(this.client)\n this.groups = new GroupsModule(this.client)\n this.enrollments = new EnrollmentsModule(this.client)\n this.discussions = new DiscussionsModule(this.client)\n this.modules = new ModulesModule(this.client)\n this.pages = new PagesModule(this.client)\n this.calendar = new CalendarModule(this.client)\n this.conversations = new ConversationsModule(this.client)\n this.peerReviews = new PeerReviewsModule(this.client)\n this.accounts = new AccountsModule(this.client)\n this.analytics = new AnalyticsModule(this.client)\n this.dashboard = new DashboardModule(this.client)\n this.outcomes = new OutcomesModule(this.client)\n this.gradebookHistory = new GradebookHistoryModule(this.client)\n }\n}\n\nexport { CanvasHttpClient, CanvasApiError } from './client'\nexport type * from './types'\n"]}
|