Merge branch 'dev' into feat/leerpad-vragen
This commit is contained in:
		
						commit
						90ece281fd
					
				
					 20 changed files with 204 additions and 37 deletions
				
			
		|  | @ -11,3 +11,10 @@ DWENGO_PORT=3000 | |||
| 
 | ||||
| DWENGO_DB_NAME=":memory:" | ||||
| DWENGO_DB_UPDATE=true | ||||
| 
 | ||||
| DWENGO_AUTH_STUDENT_URL=http://localhost:7080/realms/student | ||||
| DWENGO_AUTH_STUDENT_JWKS_ENDPOINT=http://localhost:7080/realms/student/protocol/openid-connect/certs | ||||
| DWENGO_AUTH_TEACHER_URL=http://localhost:7080/realms/teacher | ||||
| DWENGO_AUTH_TEACHER_JWKS_ENDPOINT=http://localhost:7080/realms/teacher/protocol/openid-connect/certs | ||||
| 
 | ||||
| DWENGO_CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000 | ||||
|  |  | |||
|  | @ -7,7 +7,8 @@ | |||
|     "main": "dist/app.js", | ||||
|     "scripts": { | ||||
|         "build": "cross-env NODE_ENV=production tsc --build", | ||||
|         "dev": "cross-env NODE_ENV=development tsx tool/seed.ts; tsx watch --env-file=.env.development.local src/app.ts", | ||||
|         "predev": "tsc --build ../common/tsconfig.json", | ||||
|         "dev": "cross-env NODE_ENV=development tsx tool/seed.ts && tsx watch --env-file=.env.development.local src/app.ts", | ||||
|         "start": "cross-env NODE_ENV=production node --env-file=.env dist/app.js", | ||||
|         "format": "prettier --write src/", | ||||
|         "format-check": "prettier --check src/", | ||||
|  |  | |||
|  | @ -3,8 +3,6 @@ import { createGroup, deleteGroup, getAllGroups, getGroup, getGroupSubmissions, | |||
| import { GroupDTO } from '@dwengo-1/common/interfaces/group'; | ||||
| import { requireFields } from './error-helper.js'; | ||||
| import { BadRequestException } from '../exceptions/bad-request-exception.js'; | ||||
| import { EntityDTO } from '@mikro-orm/core'; | ||||
| import { Group } from '../entities/assignments/group.entity.js'; | ||||
| 
 | ||||
| function checkGroupFields(classId: string, assignmentId: number, groupId: number): void { | ||||
|     requireFields({ classId, assignmentId, groupId }); | ||||
|  | @ -35,7 +33,11 @@ export async function putGroupHandler(req: Request, res: Response): Promise<void | |||
|     const groupId = parseInt(req.params.groupid); | ||||
|     checkGroupFields(classId, assignmentId, groupId); | ||||
| 
 | ||||
|     const group = await putGroup(classId, assignmentId, groupId, req.body as Partial<EntityDTO<Group>>); | ||||
|     // Only members field can be changed
 | ||||
|     const members = req.body.members; | ||||
|     requireFields({ members }); | ||||
| 
 | ||||
|     const group = await putGroup(classId, assignmentId, groupId, { members } as Partial<GroupDTO>); | ||||
| 
 | ||||
|     res.json({ group }); | ||||
| } | ||||
|  |  | |||
|  | @ -26,13 +26,15 @@ function initializeLogger(): Logger { | |||
| 
 | ||||
|     const consoleTransport = new transports.Console({ | ||||
|         level: getEnvVar(envVars.LogLevel), | ||||
|         format: format.combine(format.cli(), format.colorize()), | ||||
|         format: format.combine(format.cli(), format.simple()), | ||||
|     }); | ||||
| 
 | ||||
|     if (getEnvVar(envVars.RunMode) === 'dev') { | ||||
|         return createLogger({ | ||||
|         logger = createLogger({ | ||||
|             transports: [consoleTransport], | ||||
|         }); | ||||
|         logger.debug(`Logger initialized with level ${logLevel} to console`); | ||||
|         return logger; | ||||
|     } | ||||
| 
 | ||||
|     const lokiHost = getEnvVar(envVars.LokiHost); | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ export class MikroOrmLogger extends DefaultLogger { | |||
|         }; | ||||
| 
 | ||||
|         let message: string; | ||||
|         if (context?.label) { | ||||
|         if (context !== undefined && context.labels !== undefined) { | ||||
|             message = `[${namespace}] (${context.label}) ${messageArg}`; | ||||
|         } else { | ||||
|             message = `[${namespace}] ${messageArg}`; | ||||
|  |  | |||
|  | @ -7,8 +7,17 @@ import { GroupDTO, GroupDTOId } from '@dwengo-1/common/interfaces/group'; | |||
| import { SubmissionDTO, SubmissionDTOId } from '@dwengo-1/common/interfaces/submission'; | ||||
| import { fetchAssignment } from './assignments.js'; | ||||
| import { NotFoundException } from '../exceptions/not-found-exception.js'; | ||||
| import { putObject } from './service-helper.js'; | ||||
| import { fetchStudents } from './students.js'; | ||||
| import { fetchClass } from './classes.js'; | ||||
| import { BadRequestException } from '../exceptions/bad-request-exception.js'; | ||||
| import { Student } from '../entities/users/student.entity.js'; | ||||
| import { Class } from '../entities/classes/class.entity.js'; | ||||
| 
 | ||||
| async function assertMembersInClass(members: Student[], cls: Class): Promise<void> { | ||||
|     if (!members.every((student) => cls.students.contains(student))) { | ||||
|         throw new BadRequestException('Student does not belong to class'); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export async function fetchGroup(classId: string, assignmentNumber: number, groupNumber: number): Promise<Group> { | ||||
|     const assignment = await fetchAssignment(classId, assignmentNumber); | ||||
|  | @ -28,15 +37,18 @@ export async function getGroup(classId: string, assignmentNumber: number, groupN | |||
|     return mapToGroupDTO(group, group.assignment.within); | ||||
| } | ||||
| 
 | ||||
| export async function putGroup( | ||||
|     classId: string, | ||||
|     assignmentNumber: number, | ||||
|     groupNumber: number, | ||||
|     groupData: Partial<EntityDTO<Group>> | ||||
| ): Promise<GroupDTO> { | ||||
| export async function putGroup(classId: string, assignmentNumber: number, groupNumber: number, groupData: Partial<GroupDTO>): Promise<GroupDTO> { | ||||
|     const group = await fetchGroup(classId, assignmentNumber, groupNumber); | ||||
| 
 | ||||
|     await putObject<Group>(group, groupData, getGroupRepository()); | ||||
|     const memberUsernames = groupData.members as string[]; | ||||
|     const members = await fetchStudents(memberUsernames); | ||||
| 
 | ||||
|     const cls = await fetchClass(classId); | ||||
|     await assertMembersInClass(members, cls); | ||||
| 
 | ||||
|     const groupRepository = getGroupRepository(); | ||||
|     groupRepository.assign(group, { members } as Partial<EntityDTO<Group>>); | ||||
|     await groupRepository.getEntityManager().persistAndFlush(group); | ||||
| 
 | ||||
|     return mapToGroupDTO(group, group.assignment.within); | ||||
| } | ||||
|  | @ -63,6 +75,9 @@ export async function createGroup(groupData: GroupDTO, classid: string, assignme | |||
|     const memberUsernames = (groupData.members as string[]) || []; | ||||
|     const members = await fetchStudents(memberUsernames); | ||||
| 
 | ||||
|     const cls = await fetchClass(classid); | ||||
|     await assertMembersInClass(members, cls); | ||||
| 
 | ||||
|     const assignment = await fetchAssignment(classid, assignmentNumber); | ||||
| 
 | ||||
|     const groupRepository = getGroupRepository(); | ||||
|  |  | |||
|  | @ -61,7 +61,7 @@ export function makeTestGroups(em: EntityManager, students: Student[], assignmen | |||
|      */ | ||||
|     group1ConditionalLearningPath = em.create(Group, { | ||||
|         assignment: getConditionalPathAssignment(), | ||||
|         groupNumber: 1, | ||||
|         groupNumber: 21005, | ||||
|         members: [getTestleerling1()], | ||||
|     }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,14 +14,12 @@ import { makeTestQuestions } from '../tests/test_assets/questions/questions.test | |||
| import { makeTestStudents } from '../tests/test_assets/users/students.testdata.js'; | ||||
| import { makeTestTeachers } from '../tests/test_assets/users/teachers.testdata.js'; | ||||
| import { getLogger, Logger } from '../src/logging/initalize.js'; | ||||
| import { Collection } from '@mikro-orm/core'; | ||||
| import { Collection, MikroORM } from '@mikro-orm/core'; | ||||
| import { Group } from '../src/entities/assignments/group.entity'; | ||||
| 
 | ||||
| const logger: Logger = getLogger(); | ||||
| 
 | ||||
| export async function seedDatabase(): Promise<void> { | ||||
|     dotenv.config({ path: '.env.development.local' }); | ||||
|     const orm = await initORM(); | ||||
| export async function seedORM(orm: MikroORM): Promise<void> { | ||||
|     await orm.schema.clearDatabase(); | ||||
| 
 | ||||
|     const em = forkEntityManager(); | ||||
|  | @ -68,8 +66,17 @@ export async function seedDatabase(): Promise<void> { | |||
|     ]); | ||||
| 
 | ||||
|     logger.info('Development database seeded successfully!'); | ||||
| } | ||||
| 
 | ||||
| export async function seedDatabase(envFile = '.env.development.local', testMode = false): Promise<void> { | ||||
|     dotenv.config({ path: envFile }); | ||||
|     const orm = await initORM(testMode); | ||||
| 
 | ||||
|     await seedORM(orm); | ||||
| 
 | ||||
|     await orm.close(); | ||||
| } | ||||
| 
 | ||||
| seedDatabase().catch(logger.error); | ||||
| seedDatabase().catch((err) => { | ||||
|     logger.error(err); | ||||
| }); | ||||
|  |  | |||
							
								
								
									
										29
									
								
								backend/tool/startTestApp.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								backend/tool/startTestApp.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | |||
| import express, { Express } from 'express'; | ||||
| import { initORM } from '../src/orm.js'; | ||||
| import apiRouter from '../src/routes/router.js'; | ||||
| import { errorHandler } from '../src/middleware/error-handling/error-handler.js'; | ||||
| import dotenv from 'dotenv'; | ||||
| import cors from '../src/middleware/cors'; | ||||
| import { authenticateUser } from '../src/middleware/auth/auth'; | ||||
| import { seedORM } from './seed'; | ||||
| 
 | ||||
| const envFile = '../.env.test'; | ||||
| 
 | ||||
| dotenv.config({ path: envFile }); | ||||
| 
 | ||||
| const app: Express = express(); | ||||
| 
 | ||||
| app.use(express.json()); | ||||
| app.use(cors); | ||||
| app.use(authenticateUser); | ||||
| 
 | ||||
| app.use('/api', apiRouter); | ||||
| app.use(errorHandler); | ||||
| 
 | ||||
| async function startServer(): Promise<void> { | ||||
|     await seedORM(await initORM(true)); | ||||
| 
 | ||||
|     app.listen(9876); | ||||
| } | ||||
| 
 | ||||
| await startServer(); | ||||
		Reference in a new issue
	
	 Timo De Meyst
						Timo De Meyst