From 08f6f7a4f6b9b681387233fa25fc83d19f822520 Mon Sep 17 00:00:00 2001 From: Remy Salazard Date: Wed, 30 Oct 2024 17:57:08 +0100 Subject: [PATCH] can't finish (did in pair prog with Lucile) --- backend/package.json | 5 +-- .../application/services/positionService.ts | 27 +++++++++++++++ backend/src/index.ts | 3 ++ .../controllers/positionController.ts | 33 +++++++++++++++++++ backend/src/routes/positionRoutes.ts | 10 ++++++ prompts/prompts-rsa.md | 28 ++++++++++++++++ 6 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 backend/src/application/services/positionService.ts create mode 100644 backend/src/presentation/controllers/positionController.ts create mode 100644 backend/src/routes/positionRoutes.ts create mode 100644 prompts/prompts-rsa.md diff --git a/backend/package.json b/backend/package.json index 9e93f2d..eea0b9b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,6 +17,7 @@ "license": "ISC", "dependencies": { "@prisma/client": "^5.13.0", + "arg": "^5.0.2", "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.19.2", @@ -37,8 +38,8 @@ "prettier": "^3.2.5", "prisma": "^5.13.0", "ts-jest": "^29.1.2", - "ts-node": "^9.1.1", + "ts-node": "^10.9.2", "ts-node-dev": "^1.1.6", - "typescript": "^4.9.5" + "typescript": "^5.6.3" } } diff --git a/backend/src/application/services/positionService.ts b/backend/src/application/services/positionService.ts new file mode 100644 index 0000000..757b4ba --- /dev/null +++ b/backend/src/application/services/positionService.ts @@ -0,0 +1,27 @@ +// backend/src/application/positionService.ts +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +export const getCandidatesByPositionIdService = async (positionId: number) => { + return prisma.application.findMany({ + where: { positionId }, + include: { + candidate: true, + interviews: true, + interviewStep: true, + }, + }).then(applications => applications.map(application => ({ + fullName: `${application.candidate.firstName} ${application.candidate.lastName}`, + current_interview_step: application.interviewStep.name, + average_score: application.interviews.reduce((acc, interview) => acc + (interview.score || 0), 0) / application.interviews.length, + }))); +}; + +export const updateCandidateStageService = async (candidateId: number, newStage: string) => { + return prisma.application.update({ + where: { id: candidateId }, + data: { interviewStep: { update: { name: newStage } } }, + include: { candidate: true, interviewStep: true }, + }); +}; \ No newline at end of file diff --git a/backend/src/index.ts b/backend/src/index.ts index 7efc7ec..1483d13 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -3,6 +3,7 @@ import express from 'express'; import { PrismaClient } from '@prisma/client'; import dotenv from 'dotenv'; import candidateRoutes from './routes/candidateRoutes'; +import positionRoutes from './routes/positionRoutes'; import { uploadFile } from './application/services/fileUploadService'; import cors from 'cors'; @@ -38,6 +39,8 @@ app.use(cors({ // Import and use candidateRoutes app.use('/candidates', candidateRoutes); +// Import and use positionRoutes +app.use('/positions', positionRoutes); // Route for file uploads app.post('/upload', uploadFile); diff --git a/backend/src/presentation/controllers/positionController.ts b/backend/src/presentation/controllers/positionController.ts new file mode 100644 index 0000000..05c6c0a --- /dev/null +++ b/backend/src/presentation/controllers/positionController.ts @@ -0,0 +1,33 @@ +// backend/src/presentation/positionController.ts +import { Request, Response } from 'express'; +import { getCandidatesByPositionIdService, updateCandidateStageService } from '../../application/services/positionService'; + +export const getCandidatesByPositionId = async (req: Request, res: Response) => { + const positionId = parseInt(req.params.id, 10); + if (isNaN(positionId)) { + return res.status(400).json({ error: 'Invalid position ID' }); + } + + try { + const candidates = await getCandidatesByPositionIdService(positionId); + res.json(candidates); + } catch (error) { + res.status(500).json({ error: 'Internal server error' }); + } +}; + +export const updateCandidateStage = async (req: Request, res: Response) => { + const candidateId = parseInt(req.params.id, 10); + const stage = req.body.stage; + + if (isNaN(candidateId) || !stage) { + return res.status(400).json({ error: 'Invalid request data' }); + } + + try { + const updatedCandidate = await updateCandidateStageService(candidateId, stage); + res.json(updatedCandidate); + } catch (error) { + res.status(500).json({ error: 'Internal server error' }); + } +}; \ No newline at end of file diff --git a/backend/src/routes/positionRoutes.ts b/backend/src/routes/positionRoutes.ts new file mode 100644 index 0000000..8923f51 --- /dev/null +++ b/backend/src/routes/positionRoutes.ts @@ -0,0 +1,10 @@ +// backend/src/routes/positionRoutes.ts +import { Router } from 'express'; +import { getCandidatesByPositionId, updateCandidateStage } from '../presentation/controllers/positionController'; + +const router = Router(); + +router.get('/:id/candidates', getCandidatesByPositionId); +router.put('/candidate/:id', updateCandidateStage); + +export default router; \ No newline at end of file diff --git a/prompts/prompts-rsa.md b/prompts/prompts-rsa.md new file mode 100644 index 0000000..86af828 --- /dev/null +++ b/prompts/prompts-rsa.md @@ -0,0 +1,28 @@ +#prompt 1 +@workspace +Hello how are you? + +As a backend expert developer with TypeScript Node, I need you to add a new endpoint in the backend context (directory "/backend"). +The path of this endpoint is "/position/:id/candidates" and it will retrieve all the candidates in progress for a specific position, meaning all the applications for a particular positionID. It should provide the following basic information: + +- Candidate’s full name (from the candidate table). +- current_interview_step: the phase of the process the candidate is in (from the application table). +- The candidate's average score. That means we retrieve the scores of each interview of the candidate to process the average score. + +Of course you'll must respect the project structure (you can find it in README.md). + +I don't need you to code immediatly. I first need to know if you need other explanations. + +#prompt 2 +Ok, please proceed + +#prompt 3 +In order to respect the current project structure, I'de prefer that positionController.ts be located in src/presentation/controllers and positionService.ts be in src/application/services/ . And I prefer that we add a generic route for "position" operations as for candidates in index.ts ("app.use('/candidates', candidateRoutes)"). I do the modification, I don't need you to generate code. + +#prompt 4 +@workspace Cool it works. Now I need a new endpoint: /position/candidate/:id This endpoint will update the stage of the moved candidate. It allows modification of the current interview process phase for a specific candidate. Before generating code do you have any question? + +#prompt 5 +I've got this error when I build the backend context: src/application/services/positionService.ts:23:5 - error TS2322: Type '{ candidateId: number; }' is not assignable to type 'ApplicationWhereUniqueInput'. Type '{ candidateId: number; }' is not assignable to type '{ id: number; } & { id?: number | undefined; AND?: ApplicationWhereInput | ApplicationWhereInput[] | undefined; OR?: ApplicationWhereInput[] | undefined; ... 9 more ...; interviews?: InterviewListRelationFilter | undefined; }'. Property 'id' is missing in type '{ candidateId: number; }' but required in type '{ id: number; }'. 23 where: { candidateId }, ~~~~~ node_modules/.prisma/client/index.d.ts:13144:5 13144 where: ApplicationWhereUniqueInput ~~~~~ The expected type comes from property 'where' which is declared here on type '{ select?: ApplicationSelect | null | undefined; include?: ApplicationInclude | null | undefined; data: (Without<...> & ApplicationUncheckedUpdateInput) | (Without<...> & ApplicationUpdateInput); where: ApplicationWhereUniqueInput; }' Found 1 error in src/application/services/positionService.ts:23 + +#prompt 6