From 20c7491fd33ce3fe7b99c9fbd1ecbc53bbec8d62 Mon Sep 17 00:00:00 2001 From: Brian Buck Date: Mon, 23 Mar 2026 15:05:44 -0600 Subject: [PATCH] feat: Add schema file for instructor dashboard v2 API --- .../instructor-v2-course-team-api-spec.yaml | 608 ++++++++++++++++++ 1 file changed, 608 insertions(+) create mode 100644 lms/djangoapps/instructor/docs/references/instructor-v2-course-team-api-spec.yaml diff --git a/lms/djangoapps/instructor/docs/references/instructor-v2-course-team-api-spec.yaml b/lms/djangoapps/instructor/docs/references/instructor-v2-course-team-api-spec.yaml new file mode 100644 index 000000000000..1c12146e1973 --- /dev/null +++ b/lms/djangoapps/instructor/docs/references/instructor-v2-course-team-api-spec.yaml @@ -0,0 +1,608 @@ +swagger: '2.0' +info: + title: Instructor Dashboard Course Team API + version: 2.0.0 + description: | + Modern REST API for managing course team members (instructors, staff, beta testers, + and CCX coaches) on the instructor dashboard. + + **Design Principles:** + - RESTful resource-oriented URLs + - Query parameters for filtering operations + - Clear separation between read and write operations + - Consistent error handling + + **Execution Model:** + - All operations execute synchronously + - Single-user modifications are immediate (~100-500ms) + - Bulk beta tester operations process each identifier and return aggregated results + + **Authorization:** + - Listing and modifying course team members requires instructor-level access + - Bulk beta tester management requires beta test permission + - Instructors cannot remove their own instructor access + +host: courses.example.com +basePath: / +schemes: + - https + +securityDefinitions: + JWTAuth: + type: apiKey + in: header + name: Authorization + description: JWT token authentication. Header format depends on JWT_AUTH['JWT_AUTH_HEADER_PREFIX'] setting (default is 'JWT '). + +security: + - JWTAuth: [] + +tags: + - name: Course Team + description: Course team role membership management + - name: Beta Testers + description: Bulk beta tester management + +paths: + # ==================== COURSE TEAM ENDPOINTS ==================== + + /api/instructor/v2/courses/{course_key}/team: + get: + tags: + - Course Team + summary: List course team members by role + description: | + Retrieve a list of users who hold a specific role in the course. + + **Available Roles:** + - `instructor` — Full admin, can manage other roles + - `staff` — Can view/edit content, limited role management + - `limited_staff` — LMS-only staff + - `beta` — Beta testers + - `ccx_coach` — CCX coach + - `data_researcher` — Data researcher + + **Note:** This replaces the legacy `POST /api/instructor/list_course_role_members` endpoint. + operationId: listCourseTeamMembers + produces: + - application/json + parameters: + - $ref: '#/parameters/CourseKey' + - name: role + in: query + description: | + The role to list members for. One of: `instructor`, `staff`, `limited_staff`, + `beta`, `ccx_coach`, `data_researcher`. + required: true + type: string + enum: + - instructor + - staff + - limited_staff + - beta + - ccx_coach + - data_researcher + x-example: "staff" + responses: + 200: + description: Team members retrieved successfully + schema: + $ref: '#/definitions/CourseTeamMemberList' + examples: + application/json: + course_id: "course-v1:edX+DemoX+Demo_Course" + role: "staff" + results: + - username: "staff_user1" + email: "staff1@example.com" + first_name: "Alice" + last_name: "Smith" + - username: "staff_user2" + email: "staff2@example.com" + first_name: "Bob" + last_name: "Jones" + 400: + $ref: '#/responses/BadRequest' + 401: + $ref: '#/responses/Unauthorized' + 403: + $ref: '#/responses/Forbidden' + 404: + $ref: '#/responses/NotFound' + + post: + tags: + - Course Team + summary: Grant a course role to a user + description: | + Grant a specific course role to a user, identified by username or email. + + **Behavior:** + - If the user is not already enrolled in the course, they will be auto-enrolled + - For `ccx_coach` role, an enrollment email is sent automatically + - The user must exist and be active + + **Note:** This replaces the `action=allow` path of the legacy + `POST /api/instructor/modify_access` endpoint. + operationId: grantCourseRole + consumes: + - application/json + produces: + - application/json + parameters: + - $ref: '#/parameters/CourseKey' + - name: body + in: body + required: true + schema: + type: object + required: + - identifier + - role + properties: + identifier: + type: string + description: Username or email of the user to grant the role to + example: "jane_doe" + role: + type: string + description: The role to grant + enum: + - instructor + - staff + - limited_staff + - beta + - ccx_coach + - data_researcher + example: "staff" + responses: + 200: + description: Role granted successfully + schema: + $ref: '#/definitions/CourseTeamModifyResult' + examples: + application/json: + identifier: "jane_doe" + role: "staff" + action: "allow" + success: true + 400: + $ref: '#/responses/BadRequest' + 401: + $ref: '#/responses/Unauthorized' + 403: + $ref: '#/responses/Forbidden' + 404: + $ref: '#/responses/NotFound' + 409: + description: Cannot remove own instructor access + schema: + $ref: '#/definitions/Error' + examples: + application/json: + error_code: "SELF_REMOVAL_NOT_ALLOWED" + developer_message: "Instructors cannot remove their own instructor access" + user_message: "You cannot remove your own instructor access" + status_code: 409 + + /api/instructor/v2/courses/{course_key}/team/{identifier}: + delete: + tags: + - Course Team + summary: Revoke a course role from a user + description: | + Revoke a specific course role from a user. + + **Constraints:** + - Instructors cannot revoke their own instructor access + - The user must exist and be active + + **Note:** This replaces the `action=revoke` path of the legacy + `POST /api/instructor/modify_access` endpoint. + operationId: revokeCourseRole + produces: + - application/json + parameters: + - $ref: '#/parameters/CourseKey' + - name: identifier + in: path + description: Username or email of the user to revoke the role from + required: true + type: string + x-example: "jane_doe" + - name: role + in: query + description: The role to revoke + required: true + type: string + enum: + - instructor + - staff + - limited_staff + - beta + - ccx_coach + - data_researcher + x-example: "staff" + responses: + 200: + description: Role revoked successfully + schema: + $ref: '#/definitions/CourseTeamModifyResult' + examples: + application/json: + identifier: "jane_doe" + role: "staff" + action: "revoke" + success: true + 400: + $ref: '#/responses/BadRequest' + 401: + $ref: '#/responses/Unauthorized' + 403: + $ref: '#/responses/Forbidden' + 404: + $ref: '#/responses/NotFound' + 409: + description: Cannot remove own instructor access + schema: + $ref: '#/definitions/Error' + examples: + application/json: + error_code: "SELF_REMOVAL_NOT_ALLOWED" + developer_message: "Instructors cannot remove their own instructor access" + user_message: "You cannot remove your own instructor access" + status_code: 409 + + # ==================== BULK BETA TESTER ENDPOINTS ==================== + + /api/instructor/v2/courses/{course_key}/team/beta-testers: + post: + tags: + - Beta Testers + summary: Bulk add beta testers + description: | + Add multiple users as beta testers for a course. + + **Behavior:** + - Processes each identifier independently; one failure does not block others + - If `auto_enroll` is true, users not already enrolled will be enrolled + - If `email_students` is true, notification emails are sent to each user + - Users that do not exist are reported as errors in the results + + **Note:** This replaces the `action=add` path of the legacy + `POST /api/instructor/bulk_beta_modify_access` endpoint. + operationId: bulkAddBetaTesters + consumes: + - application/json + produces: + - application/json + parameters: + - $ref: '#/parameters/CourseKey' + - name: body + in: body + required: true + schema: + type: object + required: + - identifiers + properties: + identifiers: + type: array + description: List of usernames or email addresses to add as beta testers + minItems: 1 + items: + type: string + example: ["beta_user1", "beta@example.com"] + auto_enroll: + type: boolean + description: Automatically enroll users who are not already enrolled + default: false + email_students: + type: boolean + description: Send notification email to users + default: false + responses: + 200: + description: Bulk operation completed + schema: + $ref: '#/definitions/BulkBetaTesterResult' + examples: + application/json: + action: "add" + results: + - identifier: "beta_user1" + error: false + user_does_not_exist: false + is_active: true + - identifier: "unknown@example.com" + error: true + user_does_not_exist: true + is_active: null + 400: + $ref: '#/responses/BadRequest' + 401: + $ref: '#/responses/Unauthorized' + 403: + $ref: '#/responses/Forbidden' + 404: + $ref: '#/responses/NotFound' + + delete: + tags: + - Beta Testers + summary: Bulk remove beta testers + description: | + Remove multiple users from the beta tester role for a course. + + **Behavior:** + - Processes each identifier independently; one failure does not block others + - If `email_students` is true, notification emails are sent to each user + - Users that do not exist are reported as errors in the results + + **Note:** This replaces the `action=remove` path of the legacy + `POST /api/instructor/bulk_beta_modify_access` endpoint. + operationId: bulkRemoveBetaTesters + consumes: + - application/json + produces: + - application/json + parameters: + - $ref: '#/parameters/CourseKey' + - name: body + in: body + required: true + schema: + type: object + required: + - identifiers + properties: + identifiers: + type: array + description: List of usernames or email addresses to remove as beta testers + minItems: 1 + items: + type: string + example: ["beta_user1", "beta@example.com"] + email_students: + type: boolean + description: Send notification email to users + default: false + responses: + 200: + description: Bulk operation completed + schema: + $ref: '#/definitions/BulkBetaTesterResult' + examples: + application/json: + action: "remove" + results: + - identifier: "beta_user1" + error: false + user_does_not_exist: false + is_active: true + 400: + $ref: '#/responses/BadRequest' + 401: + $ref: '#/responses/Unauthorized' + 403: + $ref: '#/responses/Forbidden' + 404: + $ref: '#/responses/NotFound' + +# ==================== COMPONENTS ==================== + +parameters: + CourseKey: + name: course_key + in: path + required: true + description: Course identifier in format `course-v1:{org}+{course}+{run}` + type: string + pattern: '^course-v1:[^/+]+(\+[^/+]+)+(\+[^/]+)$' + x-example: "course-v1:edX+DemoX+Demo_Course" + +responses: + BadRequest: + description: Bad request - Invalid parameters or malformed request + schema: + $ref: '#/definitions/Error' + examples: + application/json: + error_code: "INVALID_PARAMETER" + developer_message: "Invalid course key format" + user_message: "The course identifier is not valid" + status_code: 400 + + Unauthorized: + description: Unauthorized - Authentication required + schema: + $ref: '#/definitions/Error' + examples: + application/json: + error_code: "AUTHENTICATION_REQUIRED" + developer_message: "You must be authenticated to access this endpoint" + user_message: "Please log in to continue" + status_code: 401 + + Forbidden: + description: Forbidden - Insufficient permissions + schema: + $ref: '#/definitions/Error' + examples: + application/json: + error_code: "PERMISSION_DENIED" + developer_message: "You do not have instructor permissions for this course" + user_message: "You do not have permission to perform this action" + status_code: 403 + + NotFound: + description: Not found - Resource does not exist + schema: + $ref: '#/definitions/Error' + examples: + application/json: + error_code: "RESOURCE_NOT_FOUND" + developer_message: "The specified resource does not exist" + user_message: "The requested item could not be found" + status_code: 404 + +definitions: + CourseTeamMemberList: + type: object + description: List of course team members for a specific role + required: + - course_id + - role + - results + properties: + course_id: + type: string + description: Course identifier + example: "course-v1:edX+DemoX+Demo_Course" + role: + type: string + description: The role that was queried + example: "staff" + results: + type: array + description: List of users with this role + items: + $ref: '#/definitions/CourseTeamMember' + + CourseTeamMember: + type: object + description: A user with a course team role + required: + - username + - email + - first_name + - last_name + properties: + username: + type: string + description: User's username + example: "staff_user1" + email: + type: string + format: email + description: User's email address + example: "staff1@example.com" + first_name: + type: string + description: User's first name + example: "Alice" + last_name: + type: string + description: User's last name + example: "Smith" + + CourseTeamModifyResult: + type: object + description: Result of granting or revoking a course role + required: + - identifier + - role + - action + - success + properties: + identifier: + type: string + description: Username of the affected user + example: "jane_doe" + role: + type: string + description: The role that was modified + example: "staff" + action: + type: string + enum: ["allow", "revoke"] + description: The action that was performed + example: "allow" + success: + type: boolean + description: Whether the operation succeeded + example: true + + BulkBetaTesterResult: + type: object + description: Aggregated result of a bulk beta tester operation + required: + - action + - results + properties: + action: + type: string + enum: ["add", "remove"] + description: The action that was performed + example: "add" + results: + type: array + description: Per-identifier results + items: + $ref: '#/definitions/BulkBetaTesterItemResult' + + BulkBetaTesterItemResult: + type: object + description: Result for a single identifier in a bulk beta tester operation + required: + - identifier + - error + - user_does_not_exist + - is_active + properties: + identifier: + type: string + description: The email or username that was processed + example: "beta_user1" + error: + type: boolean + description: Whether an error occurred processing this identifier + example: false + user_does_not_exist: + type: boolean + description: Whether the user was not found + example: false + is_active: + type: boolean + description: Whether the user account is active (null if user does not exist) + x-nullable: true + example: true + + Error: + type: object + description: Error response + required: + - error_code + - developer_message + - user_message + - status_code + properties: + error_code: + type: string + description: Machine-readable error code + example: "RESOURCE_NOT_FOUND" + developer_message: + type: string + description: Verbose, plain language description of the problem for developers + example: "The specified course does not exist in the modulestore" + user_message: + type: string + description: User-friendly error message (internationalized) + example: "The requested course could not be found" + status_code: + type: integer + description: HTTP status code + example: 404 + field_errors: + type: object + description: Field-specific validation errors (if applicable) + x-nullable: true + additionalProperties: + type: object + properties: + developer_message: + type: string + description: Technical error details + user_message: + type: string + description: User-friendly error message \ No newline at end of file