File

src/module/student/student.controller.ts

Index

Properties

Properties

dto
dto: AttendanceDTO
Type : AttendanceDTO
studentId
studentId: string
Type : string
user
user: Express.User
Type : Express.User
Optional
import {
    BadRequestException,
    Body,
    Controller,
    Delete,
    ForbiddenException,
    Get,
    HttpCode,
    HttpStatus,
    Param,
    Patch,
    Post,
    Put,
    Request,
    UseGuards,
    UsePipes,
    ValidationPipe,
} from '@nestjs/common';
import { Request as ExpressRequest } from 'express';
import { DateTime } from 'luxon';
import { AttendanceState, IAttendance } from 'shared/model/Attendance';
import { Role } from 'shared/model/Role';
import { IStudent } from 'shared/model/Student';
import { CreatedInOwnTutorialGuard } from '../../guards/created-in-own-tutorial.guard';
import { AllowCorrectors } from '../../guards/decorators/allowCorrectors.decorator';
import { AllowSubstitutes } from '../../guards/decorators/allowSubstitutes.decorator';
import { Roles } from '../../guards/decorators/roles.decorator';
import { HasRoleGuard } from '../../guards/has-role.guard';
import { StudentGuard } from '../../guards/student.guard';
import { SettingsService } from '../settings/settings.service';
import {
    AttendanceDTO,
    CakeCountDTO,
    CreateStudentDTO,
    CreateStudentsDTO,
    PresentationPointsDTO,
} from './student.dto';
import { StudentService } from './student.service';

interface CheckCanExcuseParams {
    dto: AttendanceDTO;
    studentId: string;
    user?: Express.User;
}

@Controller('student')
export class StudentController {
    constructor(
        private readonly studentService: StudentService,
        private readonly settingsService: SettingsService
    ) {}

    @Get()
    @UseGuards(HasRoleGuard)
    @Roles(Role.ADMIN, Role.EMPLOYEE)
    async getAllStudents(): Promise<IStudent[]> {
        const students = await this.studentService.findAll();

        return students.map((user) => user.toDTO());
    }

    @Post()
    @UseGuards(HasRoleGuard, CreatedInOwnTutorialGuard)
    @Roles(Role.ADMIN, Role.TUTOR)
    @UsePipes(ValidationPipe)
    async createStudent(@Body() dto: CreateStudentDTO): Promise<IStudent> {
        return await this.studentService.create(dto);
    }

    @Post('/generate')
    @UseGuards(HasRoleGuard, CreatedInOwnTutorialGuard)
    @Roles(Role.ADMIN, Role.TUTOR)
    @UsePipes(ValidationPipe)
    async createManyStudents(@Body() dto: CreateStudentsDTO): Promise<IStudent[]> {
        return await this.studentService.createMany(dto);
    }

    @Get('/:id')
    @UseGuards(StudentGuard)
    @AllowSubstitutes()
    @AllowCorrectors()
    async getStudent(@Param('id') id: string): Promise<IStudent> {
        const student = await this.studentService.findById(id);

        return student.toDTO();
    }

    @Patch('/:id')
    @UseGuards(StudentGuard)
    @UsePipes(ValidationPipe)
    async updateStudent(@Param('id') id: string, @Body() dto: CreateStudentDTO): Promise<IStudent> {
        return await this.studentService.update(id, dto);
    }

    @Delete('/:id')
    @HttpCode(HttpStatus.NO_CONTENT)
    @UseGuards(StudentGuard)
    async deleteStudent(@Param('id') id: string): Promise<void> {
        await this.studentService.delete(id);
    }

    @Put('/:id/attendance')
    @UseGuards(StudentGuard)
    @Roles(Role.ADMIN, Role.EMPLOYEE)
    @AllowSubstitutes()
    @UsePipes(ValidationPipe)
    async updateAttendance(
        @Param('id') id: string,
        @Body() dto: AttendanceDTO,
        @Request() request: ExpressRequest
    ): Promise<IAttendance> {
        await this.checkUserCanExcuseOrThrow({
            dto,
            studentId: id,
            user: request.user,
        });

        return await this.studentService.setAttendance(id, dto);
    }

    @Put('/:id/presentation')
    @HttpCode(HttpStatus.NO_CONTENT)
    @UseGuards(StudentGuard)
    @AllowSubstitutes()
    @UsePipes(ValidationPipe)
    async updatePresentationPoint(
        @Param('id') id: string,
        @Body() dto: PresentationPointsDTO
    ): Promise<void> {
        await this.studentService.setPresentationPoints(id, dto);
    }

    @Put('/:id/cakecount')
    @HttpCode(HttpStatus.NO_CONTENT)
    @UseGuards(StudentGuard)
    @AllowSubstitutes()
    @UsePipes(ValidationPipe)
    async updateCakeCount(@Param('id') id: string, @Body() dto: CakeCountDTO): Promise<void> {
        await this.studentService.setCakeCount(id, dto);
    }

    /**
     * Checks if the given user is allowed to proceed with the request to change the attendance state with regard to the application settings.
     *
     * __Important__: This does __NOT__ check if the user is allowed in general (ie correct role, is tutor, ...). Those checks must still be performed by the corresponding route guard!
     *
     * This checks if all the following conditions are met. If so an exception is thrown:
     * - The application settings disallow non-admins to excuse a student.
     * - The user making the request is __not__ an admin.
     * - The DTO would change the attendance state of a student to `excused`.
     *
     * @param params Must contain the `studentId`, the `dto` of the request and the `user` making the request (optional).
     *
     * @throws `BadRequestException` - If the given `user` is not defined.
     * @throws `ForbiddenException` - If the `user` is not allowed to proceed with the request (see above).
     */
    private async checkUserCanExcuseOrThrow({
        user,
        dto,
        studentId,
    }: CheckCanExcuseParams): Promise<void> {
        if (!user) {
            throw new BadRequestException('No user available in request.');
        }

        const settings = await this.settingsService.getClientSettings();
        const student = await this.studentService.findById(studentId);
        const wouldChangeAttendance =
            student.getAttendance(DateTime.fromISO(dto.date))?.state !== dto.state;

        if (!wouldChangeAttendance) {
            return;
        }

        if (
            !settings.canTutorExcuseStudents &&
            !user.roles.includes(Role.ADMIN) &&
            dto.state === AttendanceState.EXCUSED
        ) {
            throw new ForbiddenException('User is not allowed to excuse a student.');
        }
    }
}

results matching ""

    No results matching ""