eduverse-backend / src /modules /courses /controllers /courses.controller.ts
Awab-Elsadig's picture
feat(ai-services): harden attendance/quiz deployments and backend integrations.
1e03ebd
Raw
History Blame Contribute Delete
10.6 kB
import {
Controller,
Get,
Post,
Patch,
Delete,
Body,
Param,
Query,
UseGuards,
HttpStatus,
HttpCode,
ParseIntPipe,
} from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiBearerAuth,
ApiParam,
ApiQuery,
ApiBody,
} from '@nestjs/swagger';
import { CoursesService } from '../services/courses.service';
import { CreateCourseDto, UpdateCourseDto, CreatePrerequisiteDto } from '../dtos';
import { CourseStatus } from '../enums';
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../../auth/guards/roles.guard';
import { Roles } from '../../auth/roles.decorator';
import { RoleName } from '../../auth/entities/role.entity';
@ApiTags('📖 Courses')
@Controller('api/courses')
export class CoursesController {
constructor(private readonly coursesService: CoursesService) {}
@Get()
@ApiOperation({
summary: 'List all courses',
description: `
## List All Courses
Retrieves a paginated list of courses with optional filters.
### Access Control
- **Authentication Required**: No (Public endpoint)
- **Roles Required**: None (accessible to all)
### Filtering Options
- \`departmentId\`: Filter by department
- \`level\`: Filter by course level (100, 200, 300, etc.)
- \`status\`: Filter by course status
- \`search\`: Search by course name or code
### Pagination
- \`page\`: Page number (default: 1)
- \`limit\`: Items per page (default: 20, max: 100)
`,
})
@ApiQuery({ name: 'departmentId', required: false, type: Number })
@ApiQuery({ name: 'level', required: false, type: String, example: '300' })
@ApiQuery({
name: 'status',
required: false,
type: String,
schema: { type: 'string', enum: Object.values(CourseStatus) },
})
@ApiQuery({ name: 'search', required: false, type: String })
@ApiQuery({ name: 'page', required: false, type: Number, example: 1 })
@ApiQuery({ name: 'limit', required: false, type: Number, example: 20 })
@ApiResponse({ status: 200, description: 'Paginated list of courses' })
async findAll(
@Query('departmentId', new ParseIntPipe({ optional: true }))
departmentId?: number,
@Query('level') level?: string,
@Query('status') status?: CourseStatus,
@Query('search') search?: string,
@Query('page', new ParseIntPipe({ optional: true })) page = 1,
@Query('limit', new ParseIntPipe({ optional: true })) limit = 20,
) {
return this.coursesService.findAll(
departmentId,
level,
status,
search,
page,
limit,
);
}
@Get('department/:deptId')
@ApiOperation({
summary: 'Get courses by department',
description: `
## Get Courses by Department
Retrieves all courses offered by a specific department.
### Access Control
- **Authentication Required**: No (Public endpoint)
- **Roles Required**: None
`,
})
@ApiParam({ name: 'deptId', description: 'Department ID', type: Number })
@ApiResponse({ status: 200, description: 'List of courses' })
@ApiResponse({ status: 404, description: 'Department not found' })
async findByDepartment(
@Param('deptId', ParseIntPipe) deptId: number,
) {
return this.coursesService.findByDepartment(deptId);
}
@Get(':id/recent-activity')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(RoleName.INSTRUCTOR, RoleName.TA, RoleName.ADMIN, RoleName.IT_ADMIN)
@ApiBearerAuth('JWT-auth')
@ApiOperation({
summary: 'Get recent course activity',
description:
'Returns recent timeline items for a course (assignments, materials, submissions, grading).',
})
@ApiParam({ name: 'id', description: 'Course ID', type: Number })
@ApiQuery({ name: 'limit', required: false, type: Number, example: 8 })
@ApiResponse({ status: 200, description: 'Recent activity list' })
async getRecentActivity(
@Param('id', ParseIntPipe) id: number,
@Query('limit', new ParseIntPipe({ optional: true })) limit = 8,
) {
return this.coursesService.getRecentActivity(id, limit);
}
@Get(':id')
@ApiOperation({
summary: 'Get course by ID',
description: `
## Get Course Details
Retrieves detailed information about a specific course.
### Access Control
- **Authentication Required**: No (Public endpoint)
- **Roles Required**: None
### Response Includes
- Course information (code, name, credits)
- Prerequisites count
- Available sections count
`,
})
@ApiParam({ name: 'id', description: 'Course ID', type: Number })
@ApiResponse({ status: 200, description: 'Course details' })
@ApiResponse({ status: 404, description: 'Course not found' })
async findById(@Param('id', ParseIntPipe) id: number) {
const course = await this.coursesService.findById(id);
const prerequisites = await this.coursesService.getPrerequisites(id);
return {
...course,
prerequisitesCount: prerequisites.length,
sectionsCount: course.sections.length,
};
}
@Post()
@HttpCode(HttpStatus.CREATED)
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(RoleName.ADMIN, RoleName.IT_ADMIN)
@ApiBearerAuth('JWT-auth')
@ApiOperation({
summary: 'Create new course',
description: `
## Create New Course
Creates a new course in the catalog.
### Access Control
- **Authentication Required**: ✅ Yes (Bearer Token recommended)
- **Roles Required**: ADMIN, IT_ADMIN, INSTRUCTOR
### Required Fields
- Course code (unique)
- Course name
- Credit hours
- Department ID
### Notes
- Course codes must be unique
- New courses are active by default
`,
})
@ApiBody({ type: CreateCourseDto })
@ApiResponse({ status: 201, description: 'Course created successfully' })
@ApiResponse({ status: 400, description: 'Invalid input data' })
@ApiResponse({ status: 409, description: 'Course code already exists' })
async create(@Body() dto: CreateCourseDto) {
return this.coursesService.create(dto);
}
@Patch(':id')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(RoleName.ADMIN, RoleName.IT_ADMIN)
@ApiBearerAuth('JWT-auth')
@ApiOperation({
summary: 'Update course',
description: `
## Update Course
Updates an existing course's information.
### Access Control
- **Authentication Required**: ✅ Yes (Bearer Token recommended)
- **Roles Required**: ADMIN, IT_ADMIN, INSTRUCTOR
`,
})
@ApiParam({ name: 'id', description: 'Course ID', type: Number })
@ApiBody({ type: UpdateCourseDto })
@ApiResponse({ status: 200, description: 'Course updated successfully' })
@ApiResponse({ status: 400, description: 'Invalid input data' })
@ApiResponse({ status: 404, description: 'Course not found' })
async update(
@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateCourseDto,
) {
return this.coursesService.update(id, dto);
}
@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT)
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(RoleName.ADMIN, RoleName.IT_ADMIN)
@ApiBearerAuth('JWT-auth')
@ApiOperation({
summary: 'Delete course',
description: `
## Delete Course (Soft Delete)
Soft deletes a course from the system.
### Access Control
- **Authentication Required**: ✅ Yes (Bearer Token recommended)
- **Roles Required**: ADMIN, IT_ADMIN
### Notes
- This performs a soft delete (course is marked as inactive)
- Courses with active enrollments cannot be deleted
`,
})
@ApiParam({ name: 'id', description: 'Course ID', type: Number })
@ApiResponse({ status: 204, description: 'Course deleted successfully' })
@ApiResponse({ status: 400, description: 'Cannot delete course with enrollments' })
@ApiResponse({ status: 404, description: 'Course not found' })
async delete(@Param('id', ParseIntPipe) id: number) {
await this.coursesService.softDelete(id);
}
@Get(':id/prerequisites')
@ApiOperation({
summary: 'Get course prerequisites',
description: `
## Get Course Prerequisites
Retrieves all prerequisite courses for a specific course.
### Access Control
- **Authentication Required**: No (Public endpoint)
- **Roles Required**: None
### Response
Returns list of courses that must be completed before enrolling.
`,
})
@ApiParam({ name: 'id', description: 'Course ID', type: Number })
@ApiResponse({ status: 200, description: 'List of prerequisite courses' })
@ApiResponse({ status: 404, description: 'Course not found' })
async getPrerequisites(@Param('id', ParseIntPipe) id: number) {
return this.coursesService.getPrerequisites(id);
}
@Post(':id/prerequisites')
@HttpCode(HttpStatus.CREATED)
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(RoleName.ADMIN, RoleName.IT_ADMIN)
@ApiBearerAuth('JWT-auth')
@ApiOperation({
summary: 'Add prerequisite to course',
description: `
## Add Course Prerequisite
Adds a prerequisite requirement to a course.
### Access Control
- **Authentication Required**: ✅ Yes (Bearer Token recommended)
- **Roles Required**: ADMIN, IT_ADMIN
### Notes
- Cannot add circular prerequisites
- A course cannot be its own prerequisite
`,
})
@ApiParam({ name: 'id', description: 'Course ID', type: Number })
@ApiBody({ type: CreatePrerequisiteDto })
@ApiResponse({ status: 201, description: 'Prerequisite added successfully' })
@ApiResponse({ status: 400, description: 'Invalid prerequisite or circular dependency' })
@ApiResponse({ status: 404, description: 'Course not found' })
async addPrerequisite(
@Param('id', ParseIntPipe) id: number,
@Body() dto: CreatePrerequisiteDto,
) {
return this.coursesService.addPrerequisite(
id,
dto.prerequisiteCourseId,
dto.isMandatory,
);
}
@Delete(':id/prerequisites/:prereqId')
@HttpCode(HttpStatus.NO_CONTENT)
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(RoleName.ADMIN, RoleName.IT_ADMIN)
@ApiBearerAuth('JWT-auth')
@ApiOperation({
summary: 'Remove prerequisite from course',
description: `
## Remove Course Prerequisite
Removes a prerequisite requirement from a course.
### Access Control
- **Authentication Required**: ✅ Yes (Bearer Token recommended)
- **Roles Required**: ADMIN, IT_ADMIN
`,
})
@ApiParam({ name: 'id', description: 'Course ID', type: Number })
@ApiParam({ name: 'prereqId', description: 'Prerequisite Course ID', type: Number })
@ApiResponse({ status: 204, description: 'Prerequisite removed successfully' })
@ApiResponse({ status: 404, description: 'Course or prerequisite not found' })
async removePrerequisite(
@Param('id', ParseIntPipe) id: number,
@Param('prereqId', ParseIntPipe) prereqId: number,
) {
await this.coursesService.removePrerequisite(id, prereqId);
}
}