From b57d0578462485c355a76102bac19ab4d3db2c6f Mon Sep 17 00:00:00 2001 From: Dmitrii Suchkov Date: Thu, 6 Nov 2025 11:23:09 +0000 Subject: [PATCH 1/4] Add API method to add a teacher to a course see https://bugtracker.codiodev.com/issue/codio-17247 --- .github/workflows/lint.yaml | 6 ++- .github/workflows/publish.yaml | 6 ++- .gitignore | 2 + README.md | 74 +++++++++++++++++++++++++++++++--- examples/course/addTeacher.js | 12 ++++++ examples/data.js | 23 ++++++++++- package.json | 2 +- src/lib/course.ts | 27 ++++++++++++- 8 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 examples/course/addTeacher.js diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 5ac025f..95a5cb9 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -5,9 +5,11 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 + - name: Install modules run: yarn + - name: Run ESLint run: yarn run lint @@ -17,4 +19,4 @@ jobs: slack_hook_url: ${{ secrets.SLACK_WEBHOOK_URL }} message: " for ${{ github.repository }} by ${{ github.actor }} has ${{ job.status }} on branch ${{ github.ref }}" success: ${{ job.status }} - if: always() \ No newline at end of file + if: always() diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index fe71b61..c73ca4b 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -8,11 +8,13 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v5 + + - uses: actions/setup-node@v6 with: node-version: '16.x' registry-url: 'https://registry.npmjs.org' + - run: yarn && yarn build - uses: JS-DevTools/npm-publish@v1 diff --git a/.gitignore b/.gitignore index 1de3458..ca567e2 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,5 @@ dist /lib *.iml +*.ipr +*.iws diff --git a/README.md b/README.md index 3d12e8c..a1e4090 100644 --- a/README.md +++ b/README.md @@ -143,8 +143,11 @@ Course = { modules: Module[], assignments: Assignment[], creationDate: Date, - archivedDate?: Date, - archived: boolen + archivedDate: Date, + archived: boolean, + start?: Date, + timezone?: string, + tags?: string[] } Module = { id: string, @@ -170,7 +173,13 @@ Course = { id: string, name: string, modules: Module[], - assignments: Assignment[] + assignments: Assignment[], + creationDate: Date, + archivedDate: Date, + archived: boolean, + start?: Date, + timezone?: string, + tags?: string[] } Module = { id: string, @@ -271,6 +280,61 @@ Fetch course teachers ``` returns user `User[]` object +Add teacher to course + +``` + await codio.v1.course.addTeacher(courseId, userId) +``` +returns `boolean` — true if the teacher was successfully added + +#### Course management + +Create course + +``` + await codio.v1.course.createCourse(courseData) +``` +Parameters: +``` +CreateCourseRequest = { + name: string, + description?: string, + start?: string, + end?: string, + timezone?: string, + tags?: string[] +} +``` +Notes: +- `start` and `end` are ISO 8601 strings (e.g., `2025-09-01T13:00:00Z`). +- `timezone` should be an IANA timezone name (e.g., `America/New_York`). +- In responses, `Course.start` is a `Date` (or absent), while `CreateCourseRequest.start` is a string. +returns `string` — newly created courseId + +Example: +```javascript +const courseId = await codio.v1.course.createCourse({ + name: 'Intro to CS', + description: 'Fall 2025 section', + start: '2025-09-01T13:00:00Z', // ISO 8601 string + end: '2025-12-20T23:59:59Z', // ISO 8601 string + timezone: 'America/New_York', + tags: ['CS101','Fall-2025'] +}) +``` + +Create module + +``` + await codio.v1.course.createModule(courseId, moduleName) +``` +returns `string` — newly created moduleId + +Example: +```javascript +const moduleId = await codio.v1.course.createModule(courseId, 'Module 1: Basics') +``` + #### Export student CSV @@ -509,7 +573,7 @@ returns `AssignmentSettings` - Settings, missed properties won't be updated const settings = await codio.assignment.getSettings('', '') ``` -```javascript +``` enableResetAssignmentByStudent?: boolean disableDownloadByStudent?: boolean visibilityOnDisabled?: string, // "READ_ONLY", "NO_ACCESS", @@ -575,7 +639,7 @@ Set time limits on a per-student basis ```javascript await codio.assignment.updateStudentTimeExtension(courseId, assignmentId, studentId, { - extendedDeadline: minutes + extendedDeadline: minutes, extendedTimeLimit: minutes }) ``` diff --git a/examples/course/addTeacher.js b/examples/course/addTeacher.js new file mode 100644 index 0000000..1839e56 --- /dev/null +++ b/examples/course/addTeacher.js @@ -0,0 +1,12 @@ +const { codio, auth } = require('../auth.js') +const { courseId, userIdToAdd } = require('../data.js') + +async function main() { + await auth + + const result = await codio.course.addTeacher(courseId, userIdToAdd) + console.log(result) + +} + +main() diff --git a/examples/data.js b/examples/data.js index 2131e89..0100f91 100644 --- a/examples/data.js +++ b/examples/data.js @@ -43,5 +43,26 @@ const assignmentData = { } } } +const userIdToAdd = "your user id" -module.exports = { courseId, courseName, assignmentId, studentId, studentEmail, studentLogin, libraryId, libraryName, projectPath, stackId, stackVersionId, archivePath, yamlMapDir, courseIdToArchive, leanersMapping, moduleName, courseData, assignmentData } +module.exports = { + courseId, + courseName, + assignmentId, + studentId, + studentEmail, + studentLogin, + libraryId, + libraryName, + projectPath, + stackId, + stackVersionId, + archivePath, + yamlMapDir, + courseIdToArchive, + leanersMapping, + moduleName, + courseData, + assignmentData, + userIdToAdd +} diff --git a/package.json b/package.json index 129fdb8..bf69fcf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codio-api-js", - "version": "0.16.0", + "version": "0.17.0", "description": "JS client to Codio API", "repository": "https://github.com/codio/codio-api-js", "author": "Max Kraev ", diff --git a/src/lib/course.ts b/src/lib/course.ts index fef4b43..f9a576a 100644 --- a/src/lib/course.ts +++ b/src/lib/course.ts @@ -26,6 +26,9 @@ export type Course = { creationDate: Date archivedDate: Date archived: boolean + start: Date | undefined + timezone: string | undefined + tags: string[] | undefined } export type StudentProgress = { @@ -94,6 +97,9 @@ function flattenAssignments(course: any) { if (course.archivedDate) { course.archivedDate = new Date(course.archivedDate) } + if (course.start) { + course.start = new Date(course.start) + } } export async function info(courseId: string, withHiddenAssignments = true): Promise { @@ -625,6 +631,24 @@ export async function createModule(courseId: string, moduleName: string): Promis } } +export async function addTeacher(courseId: string, userId: string): Promise { + const api = bent(getApiV1Url(), 'POST', 'json', 200) + try { + const res = await api(`/courses/${courseId}/teachers`, { userId: userId }, getBearer()) + const completed = res['completed'] + if (!completed) { + throw new Error('Something went wrong') + } + return completed + } catch (error: any) { + if (error.json) { + const message = JSON.stringify(await error.json()) + throw new Error(message) + } + throw error + } +} + const course = { assignmentStudentsProgress, info, @@ -654,7 +678,8 @@ const course = { exportLLMProxyData, filterLearnersForMentors, createCourse, - createModule + createModule, + addTeacher } export default course From d7ee5ba8bb07ea129d0fbf82d036d2a20bd6e6f7 Mon Sep 17 00:00:00 2001 From: Dmitrii Suchkov Date: Thu, 6 Nov 2025 13:00:11 +0000 Subject: [PATCH 2/4] readOnly flag --- README.md | 14 ++++++++++++-- examples/course/addTeacher.js | 3 ++- src/lib/course.ts | 10 ++++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a1e4090..3bbf509 100644 --- a/README.md +++ b/README.md @@ -283,9 +283,19 @@ returns user `User[]` object Add teacher to course ``` - await codio.v1.course.addTeacher(courseId, userId) + await codio.v1.course.addTeacher(courseId, userId, readOnly?) +``` +- `readOnly` (optional, default `false`): when `true`, the user is added as a read‑only teacher. +- returns `boolean` — true if the teacher was successfully added + +Example: +```javascript +// Add full teacher (default) +await codio.v1.course.addTeacher(courseId, userId) + +// Add read-only teacher +await codio.v1.course.addTeacher(courseId, userId, true) ``` -returns `boolean` — true if the teacher was successfully added #### Course management diff --git a/examples/course/addTeacher.js b/examples/course/addTeacher.js index 1839e56..ae65f5b 100644 --- a/examples/course/addTeacher.js +++ b/examples/course/addTeacher.js @@ -4,7 +4,8 @@ const { courseId, userIdToAdd } = require('../data.js') async function main() { await auth - const result = await codio.course.addTeacher(courseId, userIdToAdd) + // Add the user as a read-only teacher (third param is optional; default is false) + const result = await codio.course.addTeacher(courseId, userIdToAdd, true) console.log(result) } diff --git a/src/lib/course.ts b/src/lib/course.ts index f9a576a..1bb8e56 100644 --- a/src/lib/course.ts +++ b/src/lib/course.ts @@ -21,12 +21,14 @@ export type Module = { export type Course = { id: string name: string + description: string | undefined modules: Module[] assignments: Assignment[] creationDate: Date archivedDate: Date archived: boolean start: Date | undefined + end: Date | undefined timezone: string | undefined tags: string[] | undefined } @@ -100,6 +102,9 @@ function flattenAssignments(course: any) { if (course.start) { course.start = new Date(course.start) } + if (course.end) { + course.end = new Date(course.end) + } } export async function info(courseId: string, withHiddenAssignments = true): Promise { @@ -631,10 +636,11 @@ export async function createModule(courseId: string, moduleName: string): Promis } } -export async function addTeacher(courseId: string, userId: string): Promise { +export async function addTeacher(courseId: string, userId: string, readOnly: boolean = false): Promise { const api = bent(getApiV1Url(), 'POST', 'json', 200) try { - const res = await api(`/courses/${courseId}/teachers`, { userId: userId }, getBearer()) + const body = { userId: userId, readOnly: readOnly } + const res = await api(`/courses/${courseId}/teachers`, body, getBearer()) const completed = res['completed'] if (!completed) { throw new Error('Something went wrong') From 731208f512dad302e0dcfa12a3efadacddcd283a Mon Sep 17 00:00:00 2001 From: Dmitrii Suchkov Date: Thu, 6 Nov 2025 13:07:13 +0000 Subject: [PATCH 3/4] remove type --- src/lib/course.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/course.ts b/src/lib/course.ts index 1bb8e56..d77321e 100644 --- a/src/lib/course.ts +++ b/src/lib/course.ts @@ -636,7 +636,7 @@ export async function createModule(courseId: string, moduleName: string): Promis } } -export async function addTeacher(courseId: string, userId: string, readOnly: boolean = false): Promise { +export async function addTeacher(courseId: string, userId: string, readOnly = false): Promise { const api = bent(getApiV1Url(), 'POST', 'json', 200) try { const body = { userId: userId, readOnly: readOnly } From 3173ff5ae422d5c55248aef1a67806ab5ac75900 Mon Sep 17 00:00:00 2001 From: Dmitrii Suchkov Date: Thu, 6 Nov 2025 13:52:42 +0000 Subject: [PATCH 4/4] add description to readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3bbf509..f8fbabd 100644 --- a/README.md +++ b/README.md @@ -140,12 +140,14 @@ return `Course` object Course = { id: string, name: string, + description?: string, modules: Module[], assignments: Assignment[], creationDate: Date, archivedDate: Date, archived: boolean, start?: Date, + end?: Date, timezone?: string, tags?: string[] } @@ -172,12 +174,14 @@ return `Course` object Course = { id: string, name: string, + description?: string, modules: Module[], assignments: Assignment[], creationDate: Date, archivedDate: Date, archived: boolean, start?: Date, + end?: Date, timezone?: string, tags?: string[] }