✨ feat: Update recruitment database schema with Prisma - NGVM#2
✨ feat: Update recruitment database schema with Prisma - NGVM#2nigivamu wants to merge 1 commit intoLIDR-academy:mainfrom
Conversation
…frastructure - Add comprehensive Prisma schema with recruitment entities (Application, Position, Interview, etc.) - Create database migration for new recruitment workflow - Add seed data for development environment - Configure Jest and testing dependencies - Include database documentation and ER diagram
WalkthroughThis pull request introduces a comprehensive recruitment domain expansion to the database schema with new entities (Company, Employee, Position, Application, Interview-related models), implements a Prisma seed script for sample data initialization, updates development dependencies for testing tooling, documents both current and new entity models, and activates .env file exclusion in git. Changes
Sequence Diagram(s)sequenceDiagram
participant npm as npm Script
participant seed as seed.ts
participant prisma as Prisma Client
participant db as PostgreSQL
npm->>seed: npm run prisma:seed
seed->>prisma: new PrismaClient()
prisma->>db: connect()
Note over seed,db: Initialize Data
seed->>prisma: createMany InterviewTypes
prisma->>db: INSERT interview_types
seed->>prisma: createMany InterviewFlows<br/>+ InterviewSteps
prisma->>db: INSERT interview_flows<br/>INSERT interview_steps
seed->>prisma: create Company
prisma->>db: INSERT companies
seed->>prisma: createMany Employees
prisma->>db: INSERT employees
seed->>prisma: createMany Positions
prisma->>db: INSERT positions
Note over seed,db: Complete
seed->>prisma: Log summary counts
seed->>prisma: $disconnect()
prisma->>db: close connection
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
prompts/new_entities.md (1)
1-88: Verify naming convention consistency between diagram and implementation.The ER diagram uses mixed naming conventions:
- Foreign keys use snake_case (e.g.,
company_id,interview_flow_id)- CANDIDATE fields use camelCase (e.g.,
firstName,lastName)- Other entity fields use lowercase (e.g.,
name,The actual Prisma schema implementation uses camelCase for all fields (e.g.,
companyId,firstName). While this is documentation and doesn't affect runtime behavior, maintaining consistency between documentation and implementation helps prevent confusion.Consider updating the diagram to match the Prisma schema's camelCase convention:
COMPANY { int id PK string name } EMPLOYEE { int id PK - int company_id FK + int companyId FK string name ...backend/prisma/migrations/20251111022517_add_recruitment_entities/migration.sql (1)
2-11: Consider VARCHAR length for phone numbers.The
phonefield is defined asVARCHAR(15)in both Candidate (line 7) and Employee (line 72) tables. International phone numbers with country codes and extensions can exceed 15 characters (e.g.,+1-555-0123 ext. 4567is 23 characters).Consider increasing to
VARCHAR(20)orVARCHAR(25)to accommodate international formats:"phone" VARCHAR(25),Also applies to: 66-81
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
backend/package-lock.jsonis excluded by!**/package-lock.jsonprompts/LTIdb - dbeaver final diagram.pngis excluded by!**/*.png
📒 Files selected for processing (8)
.gitignore(1 hunks)backend/package.json(2 hunks)backend/prisma/migrations/20251111022517_add_recruitment_entities/migration.sql(1 hunks)backend/prisma/schema.prisma(3 hunks)backend/prisma/seed.ts(1 hunks)prompts/current_entities.md(1 hunks)prompts/new_entities.md(1 hunks)prompts/prompts.md(1 hunks)
🧰 Additional context used
🪛 LanguageTool
prompts/current_entities.md
[grammar] ~1-~1: Aquí puede haber un error.
Context: # Análisis de Entidades del Backend - AI4Devs Candidate System ...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~1-~1: Aquí puede haber un error.
Context: # Análisis de Entidades del Backend - AI4Devs Candidate System ## Resumen ...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~1-~1: Aquí puede haber un error.
Context: ...s del Backend - AI4Devs Candidate System ## Resumen Ejecutivo El backend del sistem...
(QB_NEW_ES)
[grammar] ~3-~3: Aquí puede haber un error.
Context: ... - AI4Devs Candidate System ## Resumen Ejecutivo El backend del sistema AI4Devs está impl...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~5-~5: Aquí puede haber un error.
Context: ...ción, experiencia laboral y currículums. ## Entidades Principales ### 1. Candidate ...
(QB_NEW_ES)
[grammar] ~7-~7: Aquí puede haber un error.
Context: ...ia laboral y currículums. ## Entidades Principales ### 1. Candidate (Candidato) **Entidad centr...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~10-~10: Aquí puede haber un error.
Context: ...l sistema que representa a un postulante** Atributos: - id: Identificador único...
(QB_NEW_ES)
[grammar] ~18-~18: Aquí puede haber un error.
Context: ...: Dirección opcional (String, 100 chars) Relaciones: - Uno a muchos con Educati...
(QB_NEW_ES)
[grammar] ~23-~23: Aquí puede haber un error.
Context: ...WorkExperience - Uno a muchos con Resume ### 2. Education (Educación) **Registra la f...
(QB_NEW_ES)
[grammar] ~26-~26: Aquí puede haber un error.
Context: ...la formación académica de los candidatos** Atributos: - id: Identificador único...
(QB_NEW_ES)
[grammar] ~34-~34: Aquí puede haber un error.
Context: ...didateId`: Referencia al candidato (Int) Relaciones: - Muchos a uno con Candida...
(QB_NEW_ES)
[grammar] ~37-~37: Aquí puede haber un error.
Context: ...laciones:** - Muchos a uno con Candidate ### 3. WorkExperience (Experiencia Laboral) ...
(QB_NEW_ES)
[grammar] ~40-~40: Aquí puede haber un error.
Context: ...xperiencia profesional de los candidatos** Atributos: - id: Identificador único...
(QB_NEW_ES)
[grammar] ~49-~49: Aquí puede haber un error.
Context: ...didateId`: Referencia al candidato (Int) Relaciones: - Muchos a uno con Candida...
(QB_NEW_ES)
[grammar] ~52-~52: Aquí puede haber un error.
Context: ...laciones:** - Muchos a uno con Candidate ### 4. Resume (Currículum) **Almacena los ar...
(QB_NEW_ES)
[grammar] ~55-~55: Aquí puede haber un error.
Context: ...ena los archivos de CV de los candidatos** Atributos: - id: Identificador único...
(QB_NEW_ES)
[grammar] ~62-~62: Aquí puede haber un error.
Context: ...didateId`: Referencia al candidato (Int) Relaciones: - Muchos a uno con Candida...
(QB_NEW_ES)
[grammar] ~65-~65: Aquí puede haber un error.
Context: ...laciones:** - Muchos a uno con Candidate ## Diagrama de Relaciones de Entidades (ERD...
(QB_NEW_ES)
[grammar] ~67-~67: Aquí puede haber un error.
Context: ...iagrama de Relaciones de Entidades (ERD) mermaid erDiagram Candidate { int id PK string firstName string lastName string email UK string phone string address } Education { int id PK string institution string title datetime startDate datetime endDate int candidateId FK } WorkExperience { int id PK string company string position string description datetime startDate datetime endDate int candidateId FK } Resume { int id PK string filePath string fileType datetime uploadDate int candidateId FK } Candidate ||--o{ Education : "tiene" Candidate ||--o{ WorkExperience : "tiene" Candidate ||--o{ Resume : "tiene" ## Arquitectura del Dominio ### Implementa...
(QB_NEW_ES)
[grammar] ~112-~112: Aquí puede haber un error.
Context: ...sume : "tiene" ``` ## Arquitectura del Dominio ### Implementación en TypeScript El sistema...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~118-~118: Aquí puede haber un error.
Context: ...n las siguientes características: 1. Modelos de Dominio: Cada entidad tiene su pro...
(QB_NEW_ES_OTHER_ERROR_IDS_MISSING_OTHER)
[grammar] ~118-~118: Aquí puede haber un error.
Context: ...entes características: 1. Modelos de Dominio: Cada entidad tiene su propia clase T...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~118-~118: Aquí puede haber un error.
Context: ...e TypeScript con métodos de persistencia 2. Validaciones: Implementadas en el serv...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_OTHER)
[grammar] ~119-~119: Aquí puede haber un error.
Context: ... servicio de validación (validator.ts) 3. Servicios de Aplicación: Orquestan las...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_OTHER)
[grammar] ~120-~120: Aquí puede haber un error.
Context: ...ción (validator.ts) 3. Servicios de Aplicación: Orquestan las operaciones de negocio...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~120-~120: Aquí puede haber un error.
Context: ...**: Orquestan las operaciones de negocio 4. Controladores: Exponen los endpoints R...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_OTHER)
[grammar] ~121-~121: Aquí puede haber un error.
Context: ...adores**: Exponen los endpoints REST API ### Patrones de Persistencia - **Active Rec...
(QB_NEW_ES)
[grammar] ~123-~123: Aquí puede haber un error.
Context: ...los endpoints REST API ### Patrones de Persistencia - Active Record Pattern: Los modelos de ...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~125-~125: Aquí puede haber un error.
Context: ... API ### Patrones de Persistencia - Active Record Pattern: Los modelos de domini...
(QB_NEW_ES_OTHER_ERROR_IDS_MISSING_OTHER)
[grammar] ~125-~125: Aquí puede haber un error.
Context: ...luyen métodos save() para persistencia - Prisma Client: Utilizado como ORM para...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_OTHER)
[grammar] ~126-~126: Aquí puede haber un error.
Context: ...como ORM para interactuar con PostgreSQL - Transacciones Implícitas: Prisma manej...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_OTHER)
[grammar] ~127-~127: Aquí puede haber un error.
Context: ...maneja las transacciones automáticamente ### Operaciones Principales 1. **Creación d...
(QB_NEW_ES)
[grammar] ~129-~129: Aquí puede haber un error.
Context: ...ciones automáticamente ### Operaciones Principales 1. Creación de Candidatos: - Validaci...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~131-~131: Aquí puede haber un error.
Context: ...eraciones Principales 1. Creación de Candidatos: - Validación de datos de entrada...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~134-~134: Aquí puede haber un error.
Context: ...elacionados (educación, experiencia, CV) 2. Consulta de Candidatos: - Búsqueda ...
(QB_NEW_ES)
[grammar] ~136-~136: Aquí puede haber un error.
Context: ...ión, experiencia, CV) 2. Consulta de Candidatos: - Búsqueda por ID con relaciones ...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~138-~138: Aquí puede haber un error.
Context: ...uidas - Manejo de errores de conexión 3. Gestión de Archivos: - Upload de ar...
(QB_NEW_ES)
[grammar] ~140-~140: Aquí puede haber un error.
Context: ... - Manejo de errores de conexión 3. Gestión de Archivos: - Upload de archivos ...
(QB_NEW_ES_OTHER_ERROR_IDS_MISSING_OTHER)
[grammar] ~140-~140: Aquí puede haber un error.
Context: ...de errores de conexión 3. Gestión de Archivos: - Upload de archivos CV (PDF, DOC...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~142-~142: Aquí puede haber un error.
Context: ...cenamiento de metadatos en base de datos ## Características Técnicas ### Base de Da...
(QB_NEW_ES)
[grammar] ~144-~144: Aquí puede haber un error.
Context: ...os en base de datos ## Características Técnicas ### Base de Datos - PostgreSQL: Sistema ...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~146-~146: Aquí puede haber un error.
Context: ...# Características Técnicas ### Base de Datos - PostgreSQL: Sistema de base de datos r...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_OTHER)
[grammar] ~147-~147: Aquí puede haber un error.
Context: ...PostgreSQL*: Sistema de base de datos relacional - Prisma Migrations: Control de versione...
(QB_NEW_ES)
[grammar] ~148-~148: Aquí puede haber un error.
Context: ... Migrations**: Control de versiones del esquema - Constraints: Validaciones a nivel de b...
(QB_NEW_ES)
[style] ~149-~149: En sentido figurado es preferible usar otras expresiones.
Context: ...esquema - Constraints: Validaciones a nivel de base de datos (unique, not null, foreig...
(A_NIVEL_DE)
[grammar] ~149-~149: Aquí puede haber un error.
Context: ...e datos (unique, not null, foreign keys) ### API REST - Express.js: Framework web...
(QB_NEW_ES)
[grammar] ~156-~156: Aquí puede haber un error.
Context: ... - POST /upload: Subir archivos de CV ### Validaciones - Email único: Constrai...
(QB_NEW_ES)
[style] ~159-~159: En sentido figurado es preferible usar otras expresiones.
Context: ...idaciones - Email único: Constraint a nivel de base de datos - **Validaciones de forma...
(A_NIVEL_DE)
[grammar] ~161-~161: Aquí puede haber un error.
Context: ...e longitud**: Límites en campos de texto ## Consideraciones de Diseño 1. **Escalabi...
(QB_NEW_ES)
[grammar] ~163-~163: Aquí puede haber un error.
Context: ... campos de texto ## Consideraciones de Diseño 1. Escalabilidad: El diseño permite manej...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~165-~165: Aquí puede haber un error.
Context: ...ar múltiples registros relacionados por candidato 2. Integridad Referencial: Foreign ...
(QB_NEW_ES_OTHER_ERROR_IDS_MISSING_PUNCTUATION)
[grammar] ~166-~166: Aquí puede haber un error.
Context: ...acionados por candidato 2. Integridad Referencial: Foreign keys aseguran consistencia d...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~166-~166: Aquí puede haber un error.
Context: ...: Foreign keys aseguran consistencia de datos 3. Flexibilidad: Campos opcionales ...
(QB_NEW_ES_OTHER_ERROR_IDS_MISSING_PUNCTUATION)
[grammar] ~167-~167: Aquí puede haber un error.
Context: ...ionales permiten diferentes perfiles de candidatos 4. Auditoría: Fechas en registros p...
(QB_NEW_ES_OTHER_ERROR_IDS_MISSING_PUNCTUATION)
[grammar] ~168-~168: Aquí puede haber un error.
Context: ... registros permiten seguimiento temporal ## Posibles Mejoras 1. Indexación: Agr...
(QB_NEW_ES)
[grammar] ~170-~170: Aquí puede haber un error.
Context: ...miten seguimiento temporal ## Posibles Mejoras 1. Indexación: Agregar índices en campos ...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_ORTHOGRAPHY_UPPERCASE)
[grammar] ~172-~172: Aquí puede haber un error.
Context: ...nto temporal ## Posibles Mejoras 1. Indexación: Agregar índices en campos frecuentem...
(QB_NEW_ES_OTHER_ERROR_IDS_MISSING_OTHER)
[grammar] ~172-~172: Aquí puede haber un error.
Context: ...ces en campos frecuentemente consultados 2. Soft Deletes: Implementar eliminación ...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_OTHER)
[grammar] ~173-~173: Aquí puede haber un error.
Context: ...mentar eliminación lógica para auditoría 3. Caching: Implementar caché para consul...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_OTHER)
[grammar] ~174-~174: Aquí puede haber un error.
Context: ...lementar caché para consultas frecuentes 4. Paginación: Para listados de candidato...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_OTHER)
[grammar] ~175-~175: Aquí puede haber un error.
Context: ...aginación**: Para listados de candidatos 5. Validaciones Adicionales: Reglas de ne...
(QB_NEW_ES_OTHER_ERROR_IDS_REPLACEMENT_OTHER)
[grammar] ~176-~176: Aquí puede haber un error.
Context: ...nales**: Reglas de negocio más complejas ## Conclusión El sistema presenta una arqu...
(QB_NEW_ES)
prompts/prompts.md
[grammar] ~9-~9: Aquí puede haber un error.
Context: ...ra expandir el sistema` --- 🚀 Estado Final El sistema AI4Devs ha sido transformado...
(QB_NEW_ES)
🪛 markdownlint-cli2 (0.18.1)
prompts/current_entities.md
10-10: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
26-26: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
40-40: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
55-55: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
🔇 Additional comments (8)
.gitignore (1)
3-3: LGTM! Essential security practice.Activating the
.envexclusion pattern is the correct approach to prevent accidentally committing sensitive environment variables (such as DATABASE_URL) to version control.backend/prisma/migrations/20251111022517_add_recruitment_entities/migration.sql (1)
357-400: Well-designed foreign key constraints with appropriate ON DELETE behaviors.The migration uses appropriate referential integrity actions:
ON DELETE RESTRICTfor critical relationships (prevents accidental data loss)ON DELETE SET NULLfor optional relationships (Position.interviewFlowId)ON UPDATE CASCADEthroughout for ID updatesThis design protects data integrity while maintaining flexibility.
backend/package.json (2)
12-18: Prisma seed configuration looks good.The seed script is properly wired in both the
scriptssection (line 12) and theprismaconfiguration block (lines 16-18), which is the recommended approach for Prisma seeding.
14-14: The Jest configuration already exists and is properly configured; no action needed.The review comment's premise is incorrect. The verification reveals that
jest.config.jsdoes exist atbackend/jest.config.jsand contains a valid configuration with the ts-jest preset, node test environment, and appropriate test patterns. Additionally, all required dependencies (jest,ts-jest, and@types/jest) are present in thepackage.jsondevDependencies. The suggestion to create a Jest configuration file is unnecessary and the original concern is unfounded.Likely an incorrect or invalid review comment.
backend/prisma/seed.ts (2)
10-40: Seed data is well-structured and comprehensive.The seed script creates realistic, well-formed test data with:
- Proper email formats and phone numbers
- Comprehensive position details including salary ranges, deadlines, and requirements
- Logical relationships between entities
- Appropriate data for both technical and non-technical roles
Also applies to: 123-247
259-266: Error handling and cleanup properly implemented.The script correctly handles errors and ensures Prisma client disconnection in the
finallyblock, preventing connection leaks.backend/prisma/schema.prisma (2)
21-36: Excellent indexing strategy for existing entities.The updated Candidate, Education, WorkExperience, and Resume models now include well-designed indexes that will optimize common query patterns:
- Name-based searches on candidates
- Email lookups
- Candidate-related record retrieval
- Institution and company filtering
Also applies to: 38-50, 52-65, 67-78
84-321: Comprehensive and well-normalized recruitment domain model.The new entities demonstrate strong database design:
- Proper normalization with separate entities for Companies, Employees, Positions, Applications, and Interviews
- Support entities (ApplicationAttachment, ApplicationStatusHistory) enable audit trails
- Flexible interview workflow system (InterviewFlow → InterviewStep → InterviewType)
- Extensive composite indexes for optimizing multi-column queries
- Appropriate use of unique constraints to prevent duplicates
- Strategic use of nullable foreign keys where optionality makes sense
| CREATE TABLE "Interview" ( | ||
| "id" SERIAL NOT NULL, | ||
| "applicationId" INTEGER NOT NULL, | ||
| "interviewStepId" INTEGER NOT NULL, | ||
| "employeeId" INTEGER NOT NULL, | ||
| "interviewDate" TIMESTAMP(3) NOT NULL, | ||
| "duration" INTEGER, | ||
| "result" VARCHAR(50), | ||
| "score" INTEGER, | ||
| "notes" TEXT, | ||
| "feedback" TEXT, | ||
| "scheduledBy" INTEGER, | ||
| "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| "updatedAt" TIMESTAMP(3) NOT NULL, | ||
|
|
||
| CONSTRAINT "Interview_pkey" PRIMARY KEY ("id") | ||
| ); |
There was a problem hiding this comment.
Missing foreign key constraint for scheduledBy field.
Line 178 defines scheduledBy as an integer field that likely references an Employee, but no foreign key constraint is added. This can lead to orphaned references if the scheduling employee is deleted or if invalid IDs are inserted.
Add a foreign key constraint for data integrity:
-- AddForeignKey
ALTER TABLE "Interview" ADD CONSTRAINT "Interview_scheduledBy_fkey" FOREIGN KEY ("scheduledBy") REFERENCES "Employee"("id") ON DELETE SET NULL ON UPDATE CASCADE;Note: Use ON DELETE SET NULL since scheduledBy is nullable and the interview should remain valid even if the scheduling employee is removed from the system.
🤖 Prompt for AI Agents
In
backend/prisma/migrations/20251111022517_add_recruitment_entities/migration.sql
around lines 167-183, the "Interview" table defines a nullable integer column
"scheduledBy" but does not add a foreign key constraint; add an ALTER TABLE
statement to create a foreign key constraint named "Interview_scheduledBy_fkey"
that references "Employee"(id) and use ON DELETE SET NULL and ON UPDATE CASCADE
so the column is nulled when the referenced employee is removed and updated on
employee id changes.
| CREATE TABLE "ApplicationAttachment" ( | ||
| "id" SERIAL NOT NULL, | ||
| "applicationId" INTEGER NOT NULL, | ||
| "fileName" VARCHAR(255) NOT NULL, | ||
| "filePath" VARCHAR(500) NOT NULL, | ||
| "fileSize" INTEGER NOT NULL, | ||
| "mimeType" VARCHAR(100) NOT NULL, | ||
| "description" VARCHAR(500), | ||
| "uploadedBy" INTEGER, | ||
| "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
|
|
||
| CONSTRAINT "ApplicationAttachment_pkey" PRIMARY KEY ("id") | ||
| ); |
There was a problem hiding this comment.
Missing foreign key constraint for uploadedBy field.
Line 194 defines uploadedBy as an integer field that likely references an Employee, but no foreign key constraint is added. This creates a risk of orphaned references.
Add a foreign key constraint:
-- AddForeignKey
ALTER TABLE "ApplicationAttachment" ADD CONSTRAINT "ApplicationAttachment_uploadedBy_fkey" FOREIGN KEY ("uploadedBy") REFERENCES "Employee"("id") ON DELETE SET NULL ON UPDATE CASCADE;🤖 Prompt for AI Agents
In
backend/prisma/migrations/20251111022517_add_recruitment_entities/migration.sql
around lines 186-198, the "ApplicationAttachment" table declares an integer
uploadedBy column but lacks a foreign key constraint; add an ALTER TABLE
statement to add constraint "ApplicationAttachment_uploadedBy_fkey" making
uploadedBy reference "Employee"(id) with ON DELETE SET NULL and ON UPDATE
CASCADE so orphaned references are prevented and updates cascade.
| -- CreateTable | ||
| CREATE TABLE "ApplicationStatusHistory" ( | ||
| "id" SERIAL NOT NULL, | ||
| "applicationId" INTEGER NOT NULL, | ||
| "oldStatus" VARCHAR(50), | ||
| "newStatus" VARCHAR(50) NOT NULL, | ||
| "changedBy" INTEGER, | ||
| "notes" TEXT, | ||
| "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
|
|
||
| CONSTRAINT "ApplicationStatusHistory_pkey" PRIMARY KEY ("id") | ||
| ); |
There was a problem hiding this comment.
Missing foreign key constraint for changedBy field.
Line 206 defines changedBy as an integer field that likely references an Employee, but no foreign key constraint is added.
Add a foreign key constraint to maintain referential integrity:
-- AddForeignKey
ALTER TABLE "ApplicationStatusHistory" ADD CONSTRAINT "ApplicationStatusHistory_changedBy_fkey" FOREIGN KEY ("changedBy") REFERENCES "Employee"("id") ON DELETE SET NULL ON UPDATE CASCADE;🤖 Prompt for AI Agents
In
backend/prisma/migrations/20251111022517_add_recruitment_entities/migration.sql
around lines 200 to 211 the "changedBy" column on ApplicationStatusHistory is
defined as an INTEGER but lacks a foreign key constraint; add an ALTER TABLE
statement to add constraint "ApplicationStatusHistory_changedBy_fkey" that
references Employee(id) with ON DELETE SET NULL and ON UPDATE CASCADE to
maintain referential integrity and allow nulling when the referenced employee is
removed.
| model Interview { | ||
| id Int @id @default(autoincrement()) | ||
| applicationId Int | ||
| interviewStepId Int | ||
| employeeId Int | ||
| interviewDate DateTime | ||
| duration Int? // Duración real en minutos | ||
| result String? @db.VarChar(50) // pass, fail, pending, no_show | ||
| score Int? // Puntuación 1-10 | ||
| notes String? @db.Text | ||
| feedback String? @db.Text | ||
| scheduledBy Int? // Employee who scheduled the interview | ||
| createdAt DateTime @default(now()) | ||
| updatedAt DateTime @updatedAt | ||
|
|
||
| // Relaciones | ||
| application Application @relation(fields: [applicationId], references: [id]) | ||
| interviewStep InterviewStep @relation(fields: [interviewStepId], references: [id]) | ||
| employee Employee @relation(fields: [employeeId], references: [id]) | ||
|
|
||
| // Índices para rendimiento | ||
| @@unique([applicationId, interviewStepId], map: "uniq_interview_application_step") | ||
| @@index([applicationId], map: "idx_interview_application") | ||
| @@index([interviewStepId], map: "idx_interview_step") | ||
| @@index([employeeId], map: "idx_interview_employee") | ||
| @@index([interviewDate], map: "idx_interview_date") | ||
| @@index([interviewDate, employeeId], map: "idx_interview_date_employee") | ||
| @@index([result], map: "idx_interview_result") | ||
| } |
There was a problem hiding this comment.
Missing relation definition for scheduledBy field.
Line 263 defines scheduledBy as Int? to store the employee who scheduled the interview, but there's no corresponding @relation attribute. This prevents Prisma from enforcing referential integrity and makes it impossible to query the scheduling employee through Prisma's type-safe API.
Add the relation definition:
model Interview {
id Int @id @default(autoincrement())
applicationId Int
interviewStepId Int
employeeId Int
interviewDate DateTime
duration Int?
result String? @db.VarChar(50)
score Int?
notes String? @db.Text
feedback String? @db.Text
scheduledBy Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relaciones
application Application @relation(fields: [applicationId], references: [id])
interviewStep InterviewStep @relation(fields: [interviewStepId], references: [id])
employee Employee @relation(fields: [employeeId], references: [id])
+ scheduler Employee? @relation("ScheduledInterviews", fields: [scheduledBy], references: [id], onDelete: SetNull)Also update the Employee model to include the back-relation:
model Employee {
// ... existing fields ...
company Company @relation(fields: [companyId], references: [id])
interviews Interview[]
+ scheduledInterviews Interview[] @relation("ScheduledInterviews")Committable suggestion skipped: line range outside the PR's diff.
| model ApplicationAttachment { | ||
| id Int @id @default(autoincrement()) | ||
| applicationId Int | ||
| fileName String @db.VarChar(255) | ||
| filePath String @db.VarChar(500) | ||
| fileSize Int // Tamaño en bytes | ||
| mimeType String @db.VarChar(100) | ||
| description String? @db.VarChar(500) | ||
| uploadedBy Int? // Employee who uploaded the file | ||
| createdAt DateTime @default(now()) | ||
|
|
||
| // Relaciones | ||
| application Application @relation(fields: [applicationId], references: [id]) | ||
|
|
||
| // Índices para rendimiento | ||
| @@index([applicationId], map: "idx_attachment_application") | ||
| @@index([mimeType], map: "idx_attachment_mime_type") | ||
| } |
There was a problem hiding this comment.
Missing relation definition for uploadedBy field.
Line 294 defines uploadedBy as Int? but lacks a @relation attribute to link it to the Employee model.
Add the relation:
model ApplicationAttachment {
id Int @id @default(autoincrement())
applicationId Int
fileName String @db.VarChar(255)
filePath String @db.VarChar(500)
fileSize Int
mimeType String @db.VarChar(100)
description String? @db.VarChar(500)
uploadedBy Int?
createdAt DateTime @default(now())
// Relaciones
application Application @relation(fields: [applicationId], references: [id])
+ uploader Employee? @relation("UploadedAttachments", fields: [uploadedBy], references: [id], onDelete: SetNull)Update Employee model:
model Employee {
// ... existing fields ...
interviews Interview[]
+ uploadedAttachments ApplicationAttachment[] @relation("UploadedAttachments")Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In backend/prisma/schema.prisma around lines 286 to 303, the
ApplicationAttachment model defines uploadedBy Int? but lacks a @relation to
Employee; add a nullable relation field to ApplicationAttachment (e.g., employee
UploadedBy? or employee Employee? ) that uses @relation(fields: [uploadedBy],
references: [id]) and keep uploadedBy as the FK Int?; also add the reciprocal
relation array field on the Employee model (e.g., applicationAttachments
ApplicationAttachment[]) and optionally add @@index([uploadedBy]) to
ApplicationAttachment for query performance so the FK is properly modeled and
navigable from both sides.
| model ApplicationStatusHistory { | ||
| id Int @id @default(autoincrement()) | ||
| applicationId Int | ||
| oldStatus String? @db.VarChar(50) | ||
| newStatus String @db.VarChar(50) | ||
| changedBy Int? // Employee who made the change | ||
| notes String? @db.Text | ||
| createdAt DateTime @default(now()) | ||
|
|
||
| // Relaciones | ||
| application Application @relation(fields: [applicationId], references: [id]) | ||
|
|
||
| // Índices para rendimiento | ||
| @@index([applicationId], map: "idx_status_history_application") | ||
| @@index([newStatus], map: "idx_status_history_new_status") | ||
| @@index([createdAt], map: "idx_status_history_created") | ||
| } No newline at end of file |
There was a problem hiding this comment.
Missing relation definition for changedBy field.
Line 310 defines changedBy as Int? but doesn't include a @relation to Employee.
Add the relation:
model ApplicationStatusHistory {
id Int @id @default(autoincrement())
applicationId Int
oldStatus String? @db.VarChar(50)
newStatus String @db.VarChar(50)
changedBy Int?
notes String? @db.Text
createdAt DateTime @default(now())
// Relaciones
application Application @relation(fields: [applicationId], references: [id])
+ changedByEmployee Employee? @relation("StatusChanges", fields: [changedBy], references: [id], onDelete: SetNull)Update Employee model:
model Employee {
// ... existing fields ...
interviews Interview[]
+ statusChanges ApplicationStatusHistory[] @relation("StatusChanges")Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In backend/prisma/schema.prisma around lines 305 to 321, the
ApplicationStatusHistory.changedBy field is declared as Int? but lacks a
@relation to the Employee model; add a relation on changedBy that references
Employee.id (e.g., changedBy Int? @relation(fields: [changedBy], references:
[id]) or using a distinct relation name if needed) and add the corresponding
relation field on the Employee model (e.g., statusChangeHistories
ApplicationStatusHistory[] with the same relation name) so Prisma understands
the two-way foreign key relationship and generates the proper foreign key
constraint and navigation properties.
| async function main() { | ||
| console.log('🌱 Starting database seeding...'); | ||
|
|
||
| // Crear tipos de entrevista básicos | ||
| console.log('📝 Creating interview types...'); | ||
| const phoneScreening = await prisma.interviewType.create({ | ||
| data: { | ||
| name: 'Phone Screening', | ||
| description: 'Initial phone interview to assess basic qualifications', | ||
| duration: 30, | ||
| }, | ||
| }); | ||
|
|
||
| const technicalInterview = await prisma.interviewType.create({ | ||
| data: { | ||
| name: 'Technical Interview', | ||
| description: 'In-depth technical assessment', | ||
| duration: 60, | ||
| }, | ||
| }); | ||
|
|
||
| const behavioralInterview = await prisma.interviewType.create({ | ||
| data: { | ||
| name: 'Behavioral Interview', | ||
| description: 'Assessment of cultural fit and soft skills', | ||
| duration: 45, | ||
| }, | ||
| }); | ||
|
|
||
| const finalInterview = await prisma.interviewType.create({ | ||
| data: { | ||
| name: 'Final Interview', | ||
| description: 'Final decision-making interview with management', | ||
| duration: 60, | ||
| }, | ||
| }); | ||
|
|
||
| // Crear flujo de entrevista estándar para posiciones técnicas | ||
| console.log('🔄 Creating technical interview flow...'); | ||
| const technicalFlow = await prisma.interviewFlow.create({ | ||
| data: { | ||
| name: 'Standard Technical Flow', | ||
| description: 'Standard interview process for technical positions', | ||
| }, | ||
| }); | ||
|
|
||
| // Agregar pasos al flujo técnico | ||
| await prisma.interviewStep.createMany({ | ||
| data: [ | ||
| { | ||
| interviewFlowId: technicalFlow.id, | ||
| interviewTypeId: phoneScreening.id, | ||
| name: 'Initial Phone Screen', | ||
| description: 'Initial screening to verify basic qualifications and interest', | ||
| orderIndex: 1, | ||
| }, | ||
| { | ||
| interviewFlowId: technicalFlow.id, | ||
| interviewTypeId: technicalInterview.id, | ||
| name: 'Technical Assessment', | ||
| description: 'Technical interview with development team', | ||
| orderIndex: 2, | ||
| }, | ||
| { | ||
| interviewFlowId: technicalFlow.id, | ||
| interviewTypeId: behavioralInterview.id, | ||
| name: 'Behavioral Interview', | ||
| description: 'Cultural fit and behavioral assessment', | ||
| orderIndex: 3, | ||
| }, | ||
| { | ||
| interviewFlowId: technicalFlow.id, | ||
| interviewTypeId: finalInterview.id, | ||
| name: 'Final Interview', | ||
| description: 'Final interview with hiring manager', | ||
| orderIndex: 4, | ||
| }, | ||
| ], | ||
| }); | ||
|
|
||
| // Crear flujo de entrevista para posiciones no técnicas | ||
| console.log('🔄 Creating non-technical interview flow...'); | ||
| const nonTechnicalFlow = await prisma.interviewFlow.create({ | ||
| data: { | ||
| name: 'Standard Non-Technical Flow', | ||
| description: 'Standard interview process for non-technical positions', | ||
| }, | ||
| }); | ||
|
|
||
| // Agregar pasos al flujo no técnico | ||
| await prisma.interviewStep.createMany({ | ||
| data: [ | ||
| { | ||
| interviewFlowId: nonTechnicalFlow.id, | ||
| interviewTypeId: phoneScreening.id, | ||
| name: 'Initial Phone Screen', | ||
| description: 'Initial screening to verify basic qualifications and interest', | ||
| orderIndex: 1, | ||
| }, | ||
| { | ||
| interviewFlowId: nonTechnicalFlow.id, | ||
| interviewTypeId: behavioralInterview.id, | ||
| name: 'HR Interview', | ||
| description: 'HR interview to assess cultural fit and experience', | ||
| orderIndex: 2, | ||
| }, | ||
| { | ||
| interviewFlowId: nonTechnicalFlow.id, | ||
| interviewTypeId: finalInterview.id, | ||
| name: 'Final Interview', | ||
| description: 'Final interview with department head', | ||
| orderIndex: 3, | ||
| }, | ||
| ], | ||
| }); | ||
|
|
||
| // Crear empresa de ejemplo | ||
| console.log('🏢 Creating sample company...'); | ||
| const sampleCompany = await prisma.company.create({ | ||
| data: { | ||
| name: 'Tech Solutions Inc.', | ||
| description: 'Leading technology company specializing in software development and consulting', | ||
| website: 'https://techsolutions.example.com', | ||
| industry: 'Technology', | ||
| size: '201-500', | ||
| location: 'San Francisco, CA', | ||
| }, | ||
| }); | ||
|
|
||
| // Crear empleados de ejemplo | ||
| console.log('👥 Creating sample employees...'); | ||
| const hrManager = await prisma.employee.create({ | ||
| data: { | ||
| companyId: sampleCompany.id, | ||
| firstName: 'Sarah', | ||
| lastName: 'Johnson', | ||
| email: 'sarah.johnson@techsolutions.example.com', | ||
| phone: '+1-555-0101', | ||
| role: 'HR Manager', | ||
| department: 'Human Resources', | ||
| }, | ||
| }); | ||
|
|
||
| const techLead = await prisma.employee.create({ | ||
| data: { | ||
| companyId: sampleCompany.id, | ||
| firstName: 'Michael', | ||
| lastName: 'Chen', | ||
| email: 'michael.chen@techsolutions.example.com', | ||
| phone: '+1-555-0102', | ||
| role: 'Technical Lead', | ||
| department: 'Engineering', | ||
| }, | ||
| }); | ||
|
|
||
| const seniorDev = await prisma.employee.create({ | ||
| data: { | ||
| companyId: sampleCompany.id, | ||
| firstName: 'Emily', | ||
| lastName: 'Rodriguez', | ||
| email: 'emily.rodriguez@techsolutions.example.com', | ||
| phone: '+1-555-0103', | ||
| role: 'Senior Developer', | ||
| department: 'Engineering', | ||
| }, | ||
| }); | ||
|
|
||
| // Crear posiciones de ejemplo | ||
| console.log('💼 Creating sample positions...'); | ||
| const seniorPosition = await prisma.position.create({ | ||
| data: { | ||
| companyId: sampleCompany.id, | ||
| interviewFlowId: technicalFlow.id, | ||
| title: 'Senior Software Engineer', | ||
| description: 'We are looking for an experienced Senior Software Engineer to join our dynamic team.', | ||
| jobDescription: 'As a Senior Software Engineer, you will be responsible for designing, developing, and maintaining high-quality software solutions. You will work closely with cross-functional teams to deliver innovative products.', | ||
| requirements: '5+ years of experience in software development, proficiency in JavaScript/TypeScript, experience with React and Node.js, strong problem-solving skills.', | ||
| responsibilities: 'Design and implement software solutions, mentor junior developers, participate in code reviews, collaborate with product managers.', | ||
| benefits: 'Competitive salary, health insurance, 401(k) matching, flexible work hours, remote work options, professional development budget.', | ||
| companyDescription: 'Tech Solutions Inc. is a fast-growing technology company that values innovation, collaboration, and continuous learning.', | ||
| location: 'San Francisco, CA / Remote', | ||
| employmentType: 'full-time', | ||
| salaryMin: 120000, | ||
| salaryMax: 160000, | ||
| currency: 'USD', | ||
| applicationDeadline: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days from now | ||
| contactInfo: 'careers@techsolutions.example.com', | ||
| status: 'active', | ||
| isVisible: true, | ||
| openings: 2, | ||
| experienceLevel: 'senior', | ||
| }, | ||
| }); | ||
|
|
||
| const juniorPosition = await prisma.position.create({ | ||
| data: { | ||
| companyId: sampleCompany.id, | ||
| interviewFlowId: technicalFlow.id, | ||
| title: 'Junior Software Engineer', | ||
| description: 'Great opportunity for recent graduates to start their career in software development.', | ||
| jobDescription: 'As a Junior Software Engineer, you will work with senior developers to build and maintain software applications. This is an excellent opportunity to learn and grow in a supportive environment.', | ||
| requirements: 'Bachelor degree in Computer Science or related field, basic programming knowledge, eagerness to learn, good communication skills.', | ||
| responsibilities: 'Assist in software development, participate in code reviews, learn new technologies, collaborate with team members.', | ||
| benefits: 'Mentorship program, training budget, health insurance, flexible hours, casual work environment.', | ||
| companyDescription: 'Tech Solutions Inc. believes in nurturing talent and providing growth opportunities for new professionals.', | ||
| location: 'San Francisco, CA', | ||
| employmentType: 'full-time', | ||
| salaryMin: 70000, | ||
| salaryMax: 90000, | ||
| currency: 'USD', | ||
| applicationDeadline: new Date(Date.now() + 45 * 24 * 60 * 60 * 1000), // 45 days from now | ||
| contactInfo: 'careers@techsolutions.example.com', | ||
| status: 'active', | ||
| isVisible: true, | ||
| openings: 3, | ||
| experienceLevel: 'junior', | ||
| }, | ||
| }); | ||
|
|
||
| const hrPosition = await prisma.position.create({ | ||
| data: { | ||
| companyId: sampleCompany.id, | ||
| interviewFlowId: nonTechnicalFlow.id, | ||
| title: 'HR Coordinator', | ||
| description: 'We are seeking an organized HR Coordinator to support our Human Resources department.', | ||
| jobDescription: 'The HR Coordinator will assist with various HR functions including recruitment, employee relations, and administrative tasks.', | ||
| requirements: '2+ years of HR experience, knowledge of HR practices, strong organizational skills, excellent communication.', | ||
| responsibilities: 'Coordinate recruitment processes, maintain employee records, assist with onboarding, support HR initiatives.', | ||
| benefits: 'Competitive salary, health insurance, paid time off, professional development opportunities.', | ||
| companyDescription: 'Join our growing HR team and help us build a great workplace culture.', | ||
| location: 'San Francisco, CA', | ||
| employmentType: 'full-time', | ||
| salaryMin: 55000, | ||
| salaryMax: 70000, | ||
| currency: 'USD', | ||
| applicationDeadline: new Date(Date.now() + 20 * 24 * 60 * 60 * 1000), // 20 days from now | ||
| contactInfo: 'careers@techsolutions.example.com', | ||
| status: 'active', | ||
| isVisible: true, | ||
| openings: 1, | ||
| experienceLevel: 'mid', | ||
| }, | ||
| }); | ||
|
|
||
| console.log('✅ Database seeding completed successfully!'); | ||
| console.log('📊 Summary:'); | ||
| console.log(` - Interview Types: 4`); | ||
| console.log(` - Interview Flows: 2`); | ||
| console.log(` - Interview Steps: 7`); | ||
| console.log(` - Companies: 1`); | ||
| console.log(` - Employees: 3`); | ||
| console.log(` - Positions: 3`); | ||
| } |
There was a problem hiding this comment.
Seed script lacks idempotency - running multiple times will fail.
The seed script creates interview types with unique names (lines 10-40), but doesn't check if they already exist. Running npm run prisma:seed multiple times will cause unique constraint violations on InterviewType.name.
Consider using upsert operations or clearing existing data first to make the seed script idempotent:
Option 1: Use upsert operations
const phoneScreening = await prisma.interviewType.upsert({
where: { name: 'Phone Screening' },
update: {},
create: {
name: 'Phone Screening',
description: 'Initial phone interview to assess basic qualifications',
duration: 30,
},
});Option 2: Clear data before seeding
async function main() {
console.log('🌱 Starting database seeding...');
// Clear existing data in correct order (respecting foreign keys)
console.log('🗑️ Clearing existing data...');
await prisma.interview.deleteMany();
await prisma.applicationAttachment.deleteMany();
await prisma.applicationStatusHistory.deleteMany();
await prisma.application.deleteMany();
await prisma.position.deleteMany();
await prisma.employee.deleteMany();
await prisma.company.deleteMany();
await prisma.interviewStep.deleteMany();
await prisma.interviewFlow.deleteMany();
await prisma.interviewType.deleteMany();
// ... rest of seed logic
}🤖 Prompt for AI Agents
In backend/prisma/seed.ts around lines 5-257 the seed script is not idempotent
and will hit unique constraint errors (e.g., InterviewType.name) when run
multiple times; fix by either replacing direct create calls with upsert
operations for entities with unique keys (interview types, flows, company,
positions, etc.) so they update-or-create, or add a pre-seed cleanup that calls
deleteMany on tables in FK-safe order (children first, parents last) to clear
existing data before recreating; apply the same approach consistently to
interviewTypes, interviewFlows, interviewSteps, companies, employees and
positions so repeated runs succeed.
Resumen de la evaluación✅ Puntos fuertes
🔧 Áreas de mejora
📌 Prioridades sugeridas
¡Buen trabajo hasta ahora! Con estas mejoras el modelo será aún más sólido y mantenible. |
…frastructure
Summary by CodeRabbit
New Features
Chores
Documentation