From 640693afe7e3c15e50ec0a6e2d9d02be9b6d4085 Mon Sep 17 00:00:00 2001 From: Juan Arroyes Date: Mon, 28 Oct 2024 19:48:29 +0100 Subject: [PATCH 1/6] New endpoints --- .../application/services/candidateService.ts | 14 +++++ .../application/services/positionService.ts | 25 +++++++++ backend/src/domain/models/Application.ts | 34 ++++++++--- backend/src/index.ts | 3 + .../controllers/candidateController.ts | 25 +++++++++ .../controllers/positionController.ts | 29 ++++++++++ backend/src/routes/candidateRoutes.ts | 3 + backend/src/routes/positionRoutes.ts | 8 +++ backend/src/tests/positionService.test.ts | 56 +++++++++++++++++++ 9 files changed, 189 insertions(+), 8 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 backend/src/tests/positionService.test.ts diff --git a/backend/src/application/services/candidateService.ts b/backend/src/application/services/candidateService.ts index 0889982..9e95d9d 100644 --- a/backend/src/application/services/candidateService.ts +++ b/backend/src/application/services/candidateService.ts @@ -3,6 +3,7 @@ import { validateCandidateData } from '../validator'; import { Education } from '../../domain/models/Education'; import { WorkExperience } from '../../domain/models/WorkExperience'; import { Resume } from '../../domain/models/Resume'; +import { Application } from '../../domain/models/Application'; export const addCandidate = async (candidateData: any) => { try { @@ -63,3 +64,16 @@ export const findCandidateById = async (id: number): Promise = throw new Error('Error al recuperar el candidato'); } }; + +export const updateCandidateStageService = async (candidateId: number, currentInterviewStep: number) => { + const application = await Application.findByCandidateId(candidateId); + + if (!application) { + return null; + } + + application.currentInterviewStep = currentInterviewStep; + await application.save(); + + return application; + }; \ No newline at end of file diff --git a/backend/src/application/services/positionService.ts b/backend/src/application/services/positionService.ts new file mode 100644 index 0000000..a7956c2 --- /dev/null +++ b/backend/src/application/services/positionService.ts @@ -0,0 +1,25 @@ +import { Application } from '../../domain/models/Application'; + +export const getCandidatesForPositionService = async (positionId: number) => { + const applications = await Application.findByPositionId(positionId); + + if (applications.length === 0) { + return null; + } + + return applications.map(application => { + if (!application.candidate) { + throw new Error(`Candidate not found for application ID ${application.id}`); + } + + const averageScore = + application.interviews.reduce((acc, interview) => acc + (interview.score || 0), 0) / + application.interviews.length; + + return { + fullName: `${application.candidate.firstName} ${application.candidate.lastName}`, + currentInterviewStep: application.currentInterviewStep, + averageScore: averageScore || 0, + }; + }); +}; \ No newline at end of file diff --git a/backend/src/domain/models/Application.ts b/backend/src/domain/models/Application.ts index 9542593..e742f85 100644 --- a/backend/src/domain/models/Application.ts +++ b/backend/src/domain/models/Application.ts @@ -1,5 +1,6 @@ import { PrismaClient } from '@prisma/client'; import { Interview } from './Interview'; +import { Candidate } from './Candidate'; const prisma = new PrismaClient(); @@ -11,6 +12,7 @@ export class Application { currentInterviewStep: number; notes?: string; interviews: Interview[]; // Added this line + candidate?: Candidate; // Added this line constructor(data: any) { this.id = data.id; @@ -20,6 +22,7 @@ export class Application { this.currentInterviewStep = data.currentInterviewStep; this.notes = data.notes; this.interviews = data.interviews || []; // Added this line + this.candidate = data.candidate; // Added this line } async save() { @@ -32,22 +35,37 @@ export class Application { }; if (this.id) { - return await prisma.application.update({ + // Update existing application + await prisma.application.update({ where: { id: this.id }, data: applicationData, }); } else { - return await prisma.application.create({ + // Create new application + const newApplication = await prisma.application.create({ data: applicationData, }); + this.id = newApplication.id; } } - static async findOne(id: number): Promise { - const data = await prisma.application.findUnique({ - where: { id: id }, + static async findByPositionId(positionId: number): Promise { + const applications = await prisma.application.findMany({ + where: { positionId }, + include: { + candidate: true, + interviews: true, + }, }); - if (!data) return null; - return new Application(data); + + return applications.map(app => new Application(app)); + } + + static async findByCandidateId(candidateId: number): Promise { + const application = await prisma.application.findFirst({ + where: { candidateId }, + }); + + return application ? new Application(application) : null; } -} +} \ No newline at end of file diff --git a/backend/src/index.ts b/backend/src/index.ts index 7efc7ec..1f7b135 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -4,6 +4,7 @@ import { PrismaClient } from '@prisma/client'; import dotenv from 'dotenv'; import candidateRoutes from './routes/candidateRoutes'; import { uploadFile } from './application/services/fileUploadService'; +import positionRoutes from './routes/positionRoutes'; import cors from 'cors'; // Extender la interfaz Request para incluir prisma @@ -42,6 +43,8 @@ app.use('/candidates', candidateRoutes); // Route for file uploads app.post('/upload', uploadFile); +app.use('/position', positionRoutes); + app.use((req, res, next) => { console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`); next(); diff --git a/backend/src/presentation/controllers/candidateController.ts b/backend/src/presentation/controllers/candidateController.ts index 9d6da72..57e9d65 100644 --- a/backend/src/presentation/controllers/candidateController.ts +++ b/backend/src/presentation/controllers/candidateController.ts @@ -1,5 +1,6 @@ import { Request, Response } from 'express'; import { addCandidate, findCandidateById } from '../../application/services/candidateService'; +import { updateCandidateStageService } from '../../application/services/candidateService'; export const addCandidateController = async (req: Request, res: Response) => { try { @@ -31,4 +32,28 @@ export const getCandidateById = async (req: Request, res: Response) => { } }; +export const updateCandidateStage = async (req: Request, res: Response) => { + const candidateId = parseInt(req.params.id); + const { currentInterviewStep } = req.body; + + if (isNaN(candidateId)) { + return res.status(400).json({ error: 'Invalid candidate ID format' }); + } + + if (!currentInterviewStep) { + return res.status(400).json({ error: 'currentInterviewStep is required' }); + } + + try { + const updatedCandidate = await updateCandidateStageService(candidateId, currentInterviewStep); + if (!updatedCandidate) { + return res.status(404).json({ error: 'Candidate not found' }); + } + res.json(updatedCandidate); + } catch (error) { + console.error(`Error updating candidate stage for candidate ID ${candidateId}:`, error); + res.status(500).json({ error: 'Internal Server Error' }); + } + }; + export { addCandidate }; \ No newline at end of file diff --git a/backend/src/presentation/controllers/positionController.ts b/backend/src/presentation/controllers/positionController.ts new file mode 100644 index 0000000..0061956 --- /dev/null +++ b/backend/src/presentation/controllers/positionController.ts @@ -0,0 +1,29 @@ +import { Request, Response } from 'express'; +import { getCandidatesForPositionService } from '../../application/services/positionService'; + +export const getCandidatesForPosition = async (req: Request, res: Response) => { + const positionId = parseInt(req.params.id); + if (isNaN(positionId)) { + return res.status(400).json({ error: 'Invalid position ID format' }); + } + + try { + const candidates = await getCandidatesForPositionService(positionId); + if (!candidates) { + return res.status(404).json({ error: 'Position not found' }); + } + res.json(candidates); + } catch (error) { + console.error(`Error fetching candidates for position ID ${positionId}:`, error); + + if (error instanceof Error) { + if (error.message.includes('Candidate not found')) { + res.status(404).json({ error: error.message }); + } else { + res.status(500).json({ error: 'Internal Server Error' }); + } + } else { + res.status(500).json({ error: 'Unknown error occurred' }); + } + } +}; \ No newline at end of file diff --git a/backend/src/routes/candidateRoutes.ts b/backend/src/routes/candidateRoutes.ts index 0c6bca6..634042e 100644 --- a/backend/src/routes/candidateRoutes.ts +++ b/backend/src/routes/candidateRoutes.ts @@ -1,5 +1,6 @@ import { Router } from 'express'; import { addCandidate, getCandidateById } from '../presentation/controllers/candidateController'; +import { updateCandidateStage } from '../presentation/controllers/candidateController'; const router = Router(); @@ -17,6 +18,8 @@ router.post('/', async (req, res) => { } }); +router.put('/:id', updateCandidateStage); + router.get('/:id', getCandidateById); export default router; diff --git a/backend/src/routes/positionRoutes.ts b/backend/src/routes/positionRoutes.ts new file mode 100644 index 0000000..ba73f0f --- /dev/null +++ b/backend/src/routes/positionRoutes.ts @@ -0,0 +1,8 @@ +import express from 'express'; +import { getCandidatesForPosition } from '../presentation/controllers/positionController'; + +const router = express.Router(); + +router.get('/:id/candidates', getCandidatesForPosition); + +export default router; \ No newline at end of file diff --git a/backend/src/tests/positionService.test.ts b/backend/src/tests/positionService.test.ts new file mode 100644 index 0000000..9b55bdc --- /dev/null +++ b/backend/src/tests/positionService.test.ts @@ -0,0 +1,56 @@ +import { getCandidatesForPositionService } from '../application/services/positionService'; +import { Application } from '../domain/models/Application'; + +jest.mock('../domain/models/Application'); + +describe('getCandidatesForPositionService', () => { + it('should return candidates with their full name, current interview step, and average score', async () => { + const mockApplications = [ + { + id: 1, + positionId: 1, + candidateId: 1, + applicationDate: new Date(), + currentInterviewStep: 'Phone Screen', + notes: 'Some notes', + interviews: [{ score: 4 }, { score: 5 }], + candidate: { firstName: 'John', lastName: 'Doe' }, + }, + ]; + (Application.findByPositionId as jest.Mock).mockResolvedValue(mockApplications); + + const result = await getCandidatesForPositionService(1); + expect(result).toEqual([ + { + fullName: 'John Doe', + currentInterviewStep: 'Phone Screen', + averageScore: 4.5, + }, + ]); + }); + + it('should return null if no applications are found', async () => { + (Application.findByPositionId as jest.Mock).mockResolvedValue([]); + + const result = await getCandidatesForPositionService(1); + expect(result).toBeNull(); + }); + + it('should throw an error if candidate is not found', async () => { + const mockApplications = [ + { + id: 1, + positionId: 1, + candidateId: 1, + applicationDate: new Date(), + currentInterviewStep: 'Phone Screen', + notes: 'Some notes', + interviews: [{ score: 4 }, { score: 5 }], + candidate: undefined, + }, + ]; + (Application.findByPositionId as jest.Mock).mockResolvedValue(mockApplications); + + await expect(getCandidatesForPositionService(1)).rejects.toThrow('Candidate not found for application ID 1'); + }); +}); \ No newline at end of file From d4b92a18e94f4e8988f2ae1b4b502591c8366577 Mon Sep 17 00:00:00 2001 From: Juan Arroyes Date: Mon, 28 Oct 2024 19:49:52 +0100 Subject: [PATCH 2/6] New endpoints --- prompts/prompts-jar.md | 63 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 prompts/prompts-jar.md diff --git a/prompts/prompts-jar.md b/prompts/prompts-jar.md new file mode 100644 index 0000000..c08f1be --- /dev/null +++ b/prompts/prompts-jar.md @@ -0,0 +1,63 @@ +#Prompt 1 +@workspace + +Hello, you're an amazing backend expert. + +With the context of the backend only, I want to create a new GET endpoint with the path /position/:id/candidates + +This endpoint 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. Remember that each interview (interview) conducted with the candidate has a score. +The application uses a Hexagonal architecture with three layers: application, domain, and presentation. All the logic must be included in a new service in the application layer. + +Follow the good practices and SOLID principles. Of course, add unit tests! + +Explain to me the different steps that you will apply and not generate the code for the moment. If it's necessary, ask me everything that you need. + +#Prompt 2 +1. No, we don't want to add additional fields. +2. No, you must create a new one. +3. If the position doesn't exist, return a 404, if the id is an string and not an integer, return a 400 bad request. + +#Prompt 3 +Give me the code of this step: + +Update Domain Models if Necessary Ensure that the domain models have the necessary methods to support the service logic. For example, the Application model should be able to fetch related Candidate and Interview data. + +#Prompt 4 +#file:positionService.ts is using prisma directly, should use the domain entities + +#prompt 5 +I have an error when I tested the endpoint http://localhost:3010/position/1/candidates ewith Error, could you improve this error? + +#Prompt 6 +I have this error: 'error' is of type 'unknown'.ts(18046) + +#Prompt 7 +It's like the route wasn't registered properly in the system, the application returns a 404 not found error + +#Prompt 8 +Could you add unit tests for #file:positionService.ts using jest? I want to add the tests in a specific folder called tests inside src + +#Prompt 9 +Now, I want to create a new PUT endpoint with the path /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. + +Remember, the context of the application and the different layers that we have + +#Prompt 10 +I have this error in #file:Application.ts 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; }'.ts(2322) index.d.ts(13162, 5): The expected type comes from property 'where' which is declared here on type '{ select?: ApplicationSelect | null | undefined; include?: ApplicationInclude | null | undefined; where: ApplicationWhereUniqueInput; }' + +#Prompt 11 +Now, I have the following error in the file #file:candidateService.ts Type 'string' is not assignable to type 'number'.ts(2322) + +#Prompt 12 +Sorry but I think you're wrong, if we're receiving a string for the currentInterviewStep, we need to translate the name of the step to the id in the #file:candidateService.js in the line 75. Could yo fix it? + +#Prompt 13 +I still have the same error in the file #file:candidateService.js in the line 75: Type 'string' is not assignable to type 'number'.ts(2322) + +#Prompt 14 +It seems the PUT endpoint doesn't exist for the application perspective + From 49cf2f7390d647b13925af4305e44596e837a520 Mon Sep 17 00:00:00 2001 From: Juan Arroyes Date: Tue, 29 Oct 2024 19:53:47 +0100 Subject: [PATCH 3/6] New endpoints --- prompts/prompts-jar.md | 62 +++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/prompts/prompts-jar.md b/prompts/prompts-jar.md index c08f1be..8e5ec5d 100644 --- a/prompts/prompts-jar.md +++ b/prompts/prompts-jar.md @@ -1,63 +1,63 @@ -#Prompt 1 -@workspace - -Hello, you're an amazing backend expert. - -With the context of the backend only, I want to create a new GET endpoint with the path /position/:id/candidates - -This endpoint 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. Remember that each interview (interview) conducted with the candidate has a score. -The application uses a Hexagonal architecture with three layers: application, domain, and presentation. All the logic must be included in a new service in the application layer. - -Follow the good practices and SOLID principles. Of course, add unit tests! - -Explain to me the different steps that you will apply and not generate the code for the moment. If it's necessary, ask me everything that you need. - -#Prompt 2 +# Prompt 1 +>@workspace +> +>Hello, you're an amazing backend expert. +> +>With the context of the backend only, I want to create a new GET endpoint with the path /position/:id/candidates +> +>This endpoint 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. Remember that each interview (interview) conducted with the candidate has a score. +>The application uses a Hexagonal architecture with three layers: application, domain, and presentation. All the logic must be included in a new service in the >application layer. +> +>Follow the good practices and SOLID principles. Of course, add unit tests! +> +>Explain to me the different steps that you will apply and not generate the code for the moment. If it's necessary, ask me everything that you need. + +# Prompt 2 1. No, we don't want to add additional fields. 2. No, you must create a new one. 3. If the position doesn't exist, return a 404, if the id is an string and not an integer, return a 400 bad request. -#Prompt 3 +# Prompt 3 Give me the code of this step: Update Domain Models if Necessary Ensure that the domain models have the necessary methods to support the service logic. For example, the Application model should be able to fetch related Candidate and Interview data. -#Prompt 4 +# Prompt 4 #file:positionService.ts is using prisma directly, should use the domain entities -#prompt 5 +# Prompt 5 I have an error when I tested the endpoint http://localhost:3010/position/1/candidates ewith Error, could you improve this error? -#Prompt 6 +# Prompt 6 I have this error: 'error' is of type 'unknown'.ts(18046) -#Prompt 7 +# Prompt 7 It's like the route wasn't registered properly in the system, the application returns a 404 not found error -#Prompt 8 +# Prompt 8 Could you add unit tests for #file:positionService.ts using jest? I want to add the tests in a specific folder called tests inside src -#Prompt 9 +# Prompt 9 Now, I want to create a new PUT endpoint with the path /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. Remember, the context of the application and the different layers that we have -#Prompt 10 +# Prompt 10 I have this error in #file:Application.ts 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; }'.ts(2322) index.d.ts(13162, 5): The expected type comes from property 'where' which is declared here on type '{ select?: ApplicationSelect | null | undefined; include?: ApplicationInclude | null | undefined; where: ApplicationWhereUniqueInput; }' -#Prompt 11 +# Prompt 11 Now, I have the following error in the file #file:candidateService.ts Type 'string' is not assignable to type 'number'.ts(2322) -#Prompt 12 +# Prompt 12 Sorry but I think you're wrong, if we're receiving a string for the currentInterviewStep, we need to translate the name of the step to the id in the #file:candidateService.js in the line 75. Could yo fix it? -#Prompt 13 +# Prompt 13 I still have the same error in the file #file:candidateService.js in the line 75: Type 'string' is not assignable to type 'number'.ts(2322) -#Prompt 14 +# Prompt 14 It seems the PUT endpoint doesn't exist for the application perspective From af51131a1efc0dd048c1b7053551081700fe4a63 Mon Sep 17 00:00:00 2001 From: Juan Arroyes Date: Tue, 29 Oct 2024 20:17:11 +0100 Subject: [PATCH 4/6] New endpoints --- .../application/services/candidateService.ts | 17 ++++++--- backend/src/domain/models/InterviewStep.ts | 8 ++++ prompts/prompts-jar.md | 38 +++++++++---------- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/backend/src/application/services/candidateService.ts b/backend/src/application/services/candidateService.ts index 9e95d9d..83dfc94 100644 --- a/backend/src/application/services/candidateService.ts +++ b/backend/src/application/services/candidateService.ts @@ -4,6 +4,7 @@ import { Education } from '../../domain/models/Education'; import { WorkExperience } from '../../domain/models/WorkExperience'; import { Resume } from '../../domain/models/Resume'; import { Application } from '../../domain/models/Application'; +import { InterviewStep } from '../../domain/models/InterviewStep'; export const addCandidate = async (candidateData: any) => { try { @@ -65,15 +66,21 @@ export const findCandidateById = async (id: number): Promise = } }; -export const updateCandidateStageService = async (candidateId: number, currentInterviewStep: number) => { +export const updateCandidateStageService = async (candidateId: number, currentInterviewStepName: string) => { const application = await Application.findByCandidateId(candidateId); if (!application) { - return null; + return null; } - - application.currentInterviewStep = currentInterviewStep; + + const interviewStep = await InterviewStep.findOneBy({ name: currentInterviewStepName }); + + if (!interviewStep || interviewStep.id === undefined) { + throw new Error('Interview step not found or ID is undefined'); + } + + application.currentInterviewStep = interviewStep.id; await application.save(); return application; - }; \ No newline at end of file +}; \ No newline at end of file diff --git a/backend/src/domain/models/InterviewStep.ts b/backend/src/domain/models/InterviewStep.ts index 6b56690..0c1bc82 100644 --- a/backend/src/domain/models/InterviewStep.ts +++ b/backend/src/domain/models/InterviewStep.ts @@ -44,5 +44,13 @@ export class InterviewStep { if (!data) return null; return new InterviewStep(data); } + + static async findOneBy(params: { name: string }): Promise { + const data = await prisma.interviewStep.findFirst({ + where: { name: params.name }, + }); + if (!data) return null; + return new InterviewStep(data); + } } diff --git a/prompts/prompts-jar.md b/prompts/prompts-jar.md index 8e5ec5d..0db0217 100644 --- a/prompts/prompts-jar.md +++ b/prompts/prompts-jar.md @@ -17,47 +17,47 @@ >Explain to me the different steps that you will apply and not generate the code for the moment. If it's necessary, ask me everything that you need. # Prompt 2 -1. No, we don't want to add additional fields. -2. No, you must create a new one. -3. If the position doesn't exist, return a 404, if the id is an string and not an integer, return a 400 bad request. +>1. No, we don't want to add additional fields. +>2. No, you must create a new one. +>3. If the position doesn't exist, return a 404, if the id is an string and not an integer, return a 400 bad request. # Prompt 3 -Give me the code of this step: - -Update Domain Models if Necessary Ensure that the domain models have the necessary methods to support the service logic. For example, the Application model should be able to fetch related Candidate and Interview data. +>Give me the code of this step: +> +>Update Domain Models if Necessary Ensure that the domain models have the necessary methods to support the service logic. For example, the Application model should be able to fetch related Candidate and Interview data. # Prompt 4 -#file:positionService.ts is using prisma directly, should use the domain entities +>#file:positionService.ts is using prisma directly, should use the domain entities # Prompt 5 -I have an error when I tested the endpoint http://localhost:3010/position/1/candidates ewith Error, could you improve this error? +>I have an error when I tested the endpoint http://localhost:3010/position/1/candidates ewith Error, could you improve this error? # Prompt 6 -I have this error: 'error' is of type 'unknown'.ts(18046) +>I have this error: 'error' is of type 'unknown'.ts(18046) # Prompt 7 -It's like the route wasn't registered properly in the system, the application returns a 404 not found error +>It's like the route wasn't registered properly in the system, the application returns a 404 not found error # Prompt 8 -Could you add unit tests for #file:positionService.ts using jest? I want to add the tests in a specific folder called tests inside src +>Could you add unit tests for #file:positionService.ts using jest? I want to add the tests in a specific folder called tests inside src # Prompt 9 -Now, I want to create a new PUT endpoint with the path /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. - -Remember, the context of the application and the different layers that we have +>Now, I want to create a new PUT endpoint with the path /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. +> +>Remember, the context of the application and the different layers that we have # Prompt 10 -I have this error in #file:Application.ts 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; }'.ts(2322) index.d.ts(13162, 5): The expected type comes from property 'where' which is declared here on type '{ select?: ApplicationSelect | null | undefined; include?: ApplicationInclude | null | undefined; where: ApplicationWhereUniqueInput; }' +>I have this error in #file:Application.ts 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; }'.ts(2322) index.d.ts(13162, 5): The expected type comes from property 'where' which is declared here on type '{ select?: ApplicationSelect | null | undefined; include?: ApplicationInclude | null | undefined; where: ApplicationWhereUniqueInput; }' # Prompt 11 -Now, I have the following error in the file #file:candidateService.ts Type 'string' is not assignable to type 'number'.ts(2322) +>Now, I have the following error in the file #file:candidateService.ts Type 'string' is not assignable to type 'number'.ts(2322) # Prompt 12 -Sorry but I think you're wrong, if we're receiving a string for the currentInterviewStep, we need to translate the name of the step to the id in the #file:candidateService.js in the line 75. Could yo fix it? +>Sorry but I think you're wrong, if we're receiving a string for the currentInterviewStep, we need to translate the name of the step to the id in the #file:candidateService.js in the line 75. Could yo fix it? # Prompt 13 -I still have the same error in the file #file:candidateService.js in the line 75: Type 'string' is not assignable to type 'number'.ts(2322) +>I still have the same error in the file #file:candidateService.js in the line 75: Type 'string' is not assignable to type 'number'.ts(2322) # Prompt 14 -It seems the PUT endpoint doesn't exist for the application perspective +>It seems the PUT endpoint doesn't exist for the application perspective From dbe9ef4721894c379a8f86c2327b4a01b24268e1 Mon Sep 17 00:00:00 2001 From: Juan Arroyes Date: Tue, 29 Oct 2024 20:23:37 +0100 Subject: [PATCH 5/6] New endpoints --- backend/src/tests/positionService.test.ts | 91 ++++++++++++++++------- 1 file changed, 63 insertions(+), 28 deletions(-) diff --git a/backend/src/tests/positionService.test.ts b/backend/src/tests/positionService.test.ts index 9b55bdc..861d82c 100644 --- a/backend/src/tests/positionService.test.ts +++ b/backend/src/tests/positionService.test.ts @@ -1,56 +1,91 @@ import { getCandidatesForPositionService } from '../application/services/positionService'; import { Application } from '../domain/models/Application'; -jest.mock('../domain/models/Application'); +jest.mock('../domain/models/Application', () => ({ + Application: { + findByPositionId: jest.fn(), + }, +})); describe('getCandidatesForPositionService', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should return null if no applications are found', async () => { + (Application.findByPositionId as jest.Mock).mockResolvedValue([]); + + const result = await getCandidatesForPositionService(1); + + expect(result).toBeNull(); + }); + + it('should throw an error if an application has no candidate', async () => { + const applications = [ + { + id: 1, + candidate: null, + interviews: [], + currentInterviewStep: 'Step 1', + }, + ]; + (Application.findByPositionId as jest.Mock).mockResolvedValue(applications); + + await expect(getCandidatesForPositionService(1)).rejects.toThrow('Candidate not found for application ID 1'); + }); + it('should return candidates with their full name, current interview step, and average score', async () => { - const mockApplications = [ + const applications = [ { id: 1, - positionId: 1, - candidateId: 1, - applicationDate: new Date(), - currentInterviewStep: 'Phone Screen', - notes: 'Some notes', - interviews: [{ score: 4 }, { score: 5 }], candidate: { firstName: 'John', lastName: 'Doe' }, + interviews: [{ score: 4 }, { score: 5 }], + currentInterviewStep: 'Step 1', + }, + { + id: 2, + candidate: { firstName: 'Jane', lastName: 'Smith' }, + interviews: [{ score: 3 }, { score: 4 }], + currentInterviewStep: 'Step 2', }, ]; - (Application.findByPositionId as jest.Mock).mockResolvedValue(mockApplications); + (Application.findByPositionId as jest.Mock).mockResolvedValue(applications); const result = await getCandidatesForPositionService(1); + expect(result).toEqual([ { fullName: 'John Doe', - currentInterviewStep: 'Phone Screen', + currentInterviewStep: 'Step 1', averageScore: 4.5, }, + { + fullName: 'Jane Smith', + currentInterviewStep: 'Step 2', + averageScore: 3.5, + }, ]); }); - it('should return null if no applications are found', async () => { - (Application.findByPositionId as jest.Mock).mockResolvedValue([]); - - const result = await getCandidatesForPositionService(1); - expect(result).toBeNull(); - }); - - it('should throw an error if candidate is not found', async () => { - const mockApplications = [ + it('should handle applications with no interviews gracefully', async () => { + const applications = [ { id: 1, - positionId: 1, - candidateId: 1, - applicationDate: new Date(), - currentInterviewStep: 'Phone Screen', - notes: 'Some notes', - interviews: [{ score: 4 }, { score: 5 }], - candidate: undefined, + candidate: { firstName: 'John', lastName: 'Doe' }, + interviews: [], + currentInterviewStep: 'Step 1', }, ]; - (Application.findByPositionId as jest.Mock).mockResolvedValue(mockApplications); + (Application.findByPositionId as jest.Mock).mockResolvedValue(applications); - await expect(getCandidatesForPositionService(1)).rejects.toThrow('Candidate not found for application ID 1'); + const result = await getCandidatesForPositionService(1); + + expect(result).toEqual([ + { + fullName: 'John Doe', + currentInterviewStep: 'Step 1', + averageScore: 0, + }, + ]); }); }); \ No newline at end of file From 3bca2ac60ddd857be2553b086f7431a7bcd51129 Mon Sep 17 00:00:00 2001 From: Juan Arroyes Date: Tue, 29 Oct 2024 20:24:36 +0100 Subject: [PATCH 6/6] New endpoints --- prompts/prompts-jar.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/prompts/prompts-jar.md b/prompts/prompts-jar.md index 0db0217..8fd66a8 100644 --- a/prompts/prompts-jar.md +++ b/prompts/prompts-jar.md @@ -61,3 +61,27 @@ # Prompt 14 >It seems the PUT endpoint doesn't exist for the application perspective +# Prompt 15 +>@workspace Focus only in the backend folder. +> +>When the PUT endpoint candidates/1 receives the string currentInterviewStep, the application fails because it expects to use the value to find the InterviewStep with an integer in the file #file:candidateService.ts +> +>Could you fix the code to: +> +>first, find the InterviewStep by step name +>update the currentInterviewStep with the id of the interviewStep in the model #file:Application.ts +>Explain the steps before giving me the code, I will let you know when I want the code. + +# Prompt 16 +>Go ahead! + +# Prompt 17 +>I have this error in the #file:candidateService.ts Argument of type '{ where: { name: string; }; }' is not assignable to parameter of type 'number'.ts(2345) + +# Prompt 18 +>The method findOneBy doesn't exist, could you add in the #file:InterviewStep.ts ? + +# Prompt 19 +>Now I have this error in the #file:candidateService.ts Type 'number | undefined' is not assignable to type 'number'. Type 'undefined' is not assignable to type 'number'.ts(2322) + +