diff --git a/.gitignore b/.gitignore index 9a18eae27..f9956f727 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,4 @@ test.js test.html docker-compose.build.yml docker-compose.bdd.yml +*.http diff --git a/server/controllers/classroom.ts b/server/controllers/classroom.ts new file mode 100644 index 000000000..45e099514 --- /dev/null +++ b/server/controllers/classroom.ts @@ -0,0 +1,139 @@ +import type { JSONSchemaType } from 'ajv'; +import type { NextFunction, Request, Response } from 'express'; +import { getRepository } from 'typeorm'; + +import { Classroom } from '../entities/classroom'; +import { UserType } from '../entities/user'; +import { AppError, ErrorCode } from '../middlewares/handleErrors'; +import { ajv, sendInvalidDataError } from '../utils/jsonSchemaValidator'; +import { Controller } from './controller'; + +export const classroomController = new Controller('/classrooms'); + +/** + * Classroom controller to get teacher's class parameters. + * ExpressMiddleware signature + * @param {string} id classroom id + * @param {object} req Express request object + * @param {object} res Express response object + * @returns {string} Route API JSON response + */ + +classroomController.get({ path: '/:id', userType: UserType.TEACHER }, async (req: Request, res: Response, next: NextFunction) => { + const id = parseInt(req.params.id, 10) || 0; + const classroom = await getRepository(Classroom).findOne({ where: { id } }); + if (classroom === undefined) return next(); + res.json(classroom); +}); + +type CreateClassroomData = { + userId: number; + villageId: number; + name?: string; + avatar?: string; + delayedDays?: number; +}; + +const createClassroomValidator = ajv.compile({ + type: 'object', + properties: { + userId: { type: 'number', nullable: false }, + villageId: { type: 'number', nullable: false }, + name: { type: 'string', nullable: true }, + avatar: { type: 'string', nullable: true }, + delayedDays: { type: 'number', nullable: true }, + }, + required: ['userId', 'villageId'], + additionalProperties: false, +} as JSONSchemaType); + +/** + * Classroom controller to create a teacher's class parameters. + * ExpressMiddleware signature + * @param {object} req Express request object + * @param {object} res Express response object + * @returns {string} Route API JSON response + */ + +classroomController.post({ path: '', userType: UserType.TEACHER }, async (req: Request, res: Response) => { + const data = req.body; + if (!createClassroomValidator(data)) { + sendInvalidDataError(createClassroomValidator); + } + if (!req.user) { + throw new AppError('Forbidden', ErrorCode.UNKNOWN); + } + const classroom = new Classroom(); + classroom.userId = data.userId; + classroom.villageId = data.villageId; + classroom.name = data.name; + classroom.avatar = data.avatar ?? null; + classroom.delayedDays = data.delayedDays ?? null; + + await getRepository(Classroom).save(classroom); + res.json(classroom); +}); + +type UpdateClassroomData = { + name?: string; + avatar?: string; + delayedDays?: number; +}; + +const updateClassroomValidator = ajv.compile({ + type: 'object', + properties: { + name: { type: 'string', nullable: true }, + avatar: { type: 'string', nullable: true }, + delayedDays: { type: 'number', nullable: true }, + }, + required: [], + additionalProperties: false, +} as JSONSchemaType); + +/** + * Classroom controller to modify a teacher's class parameters. + * ExpressMiddleware signature + * @param {object} req Express request object + * @param {object} res Express response object + * @returns {string} Route API JSON response + */ + +classroomController.put({ path: '/:id', userType: UserType.TEACHER }, async (req: Request, res: Response, next: NextFunction) => { + const data = req.body; + if (!updateClassroomValidator(data)) { + sendInvalidDataError(updateClassroomValidator); + } + if (!req.user) { + throw new AppError('Forbidden', ErrorCode.UNKNOWN); + } + + const id = parseInt(req.params.id, 10) || 0; + const classroom = await getRepository(Classroom).findOne({ where: { id } }); + + if (!classroom) return next(); + + classroom.avatar = data.avatar ?? classroom.avatar; + classroom.delayedDays = data.delayedDays ?? classroom.delayedDays; + classroom.name = data.name ?? classroom.name; + + await getRepository(Classroom).save(classroom); + res.json(classroom); +}); + +/** + * Classroom controller to delete a teacher's class parameters. + * ExpressMiddleware signature + * @param {object} req Express request object + * @param {object} res Express response object + * @returns {string} Route API JSON response + */ + +classroomController.delete({ path: '/:id', userType: UserType.TEACHER }, async (req: Request, res: Response) => { + const id = parseInt(req.params.id, 10) || 0; + const classroom = await getRepository(Classroom).findOne({ where: { id } }); + if (!classroom || !req.user) return res.status(204).send(); + + await getRepository(Classroom).delete({ id }); + res.status(204).send(); +}); diff --git a/server/controllers/index.ts b/server/controllers/index.ts index 2fe075fc2..41327fe8c 100644 --- a/server/controllers/index.ts +++ b/server/controllers/index.ts @@ -4,6 +4,7 @@ import { activityController } from './activity'; import { analyticController } from './analytic'; import { archiveController } from './archive'; import { audioController } from './audio'; +import { classroomController } from './classroom'; import { countryController } from './countries'; import { currencyController } from './currencies'; import { gameController } from './game'; @@ -33,6 +34,7 @@ const controllers = [ weatherController, storyController, xapiController, + classroomController, ]; for (let i = 0, n = controllers.length; i < n; i++) {