Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(CC-107): create patient endpoint #108

Merged
merged 7 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions libs/core-rest-api/adapters/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,6 @@
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"]
},
"test": {
"executor": "@nx/vite:test",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
},
"e2e": {
"executor": "@nx/vite:test",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { DeleteClinicController } from './use-cases/clinic/delete-clinic/delete-
import { NestjsDeleteClinicService } from './use-cases/clinic/delete-clinic/nestjs-delete-clinic.service';
import { NestjsUpdateClinicService } from './use-cases/clinic/update-clinic/nestjs-update-clinic.service';
import { UpdateClinicController } from './use-cases/clinic/update-clinic/update-clinic.controller';
import { CreatePatientController } from './use-cases/patient/create-patient/create-patient.controller';
import { NestjsCreatePatientService } from './use-cases/patient/create-patient/nestjs-create-patient.service';
import { AuthenticatePsychologistController } from './use-cases/psychologist/authenticate-psychologist/authenticate-psychologist.controller';
import { NestjsAuthenticatePsychologistService } from './use-cases/psychologist/authenticate-psychologist/nestjs-authenticate-psychologist.service';
import { CreatePsychologistController } from './use-cases/psychologist/create-psychologist/create-psychologist.controller';
Expand All @@ -43,7 +45,8 @@ import { NestjsUpdatePsychologistService } from './use-cases/psychologist/update
DeletePsychologistController,
CreateClinicController,
UpdateClinicController,
DeleteClinicController
DeleteClinicController,
CreatePatientController,
],
providers: [
BcryptHasherService,
Expand All @@ -54,7 +57,8 @@ import { NestjsUpdatePsychologistService } from './use-cases/psychologist/update
NestjsDeletePsychologistService,
NestjsCreateClinicService,
NestjsUpdateClinicService,
NestjsDeleteClinicService
NestjsDeleteClinicService,
NestjsCreatePatientService,
],
})
export class ApiModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,29 @@ import { faker } from '@faker-js/faker';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Test, TestingModule } from '@nestjs/testing';
import { ClinicFactory } from '../../../../../tests/factories/make-clinic';
import { PsychologistFactory } from '../../../../../tests/factories/make-psychologist';

import { PostgreSqlPrismaOrmService } from '../../../../database/infra/prisma/prisma.service';
import { DatabaseRepositoriesModule } from '../../../../database/repositories/repositories.module';
import { ApiModule } from '../../api.module';

import { ClinicFactory } from '../../../../../tests/factories/make-clinic';
import { PatientFactory } from '../../../../../tests/factories/make-patient';
import { PsychologistFactory } from '../../../../../tests/factories/make-psychologist';

export async function setupE2ETest() {
const moduleRef: TestingModule = await Test.createTestingModule({
imports: [ApiModule, DatabaseRepositoriesModule],
providers: [PsychologistFactory, ClinicFactory],
providers: [PsychologistFactory, ClinicFactory, PatientFactory],
}).compile();

const app: INestApplication = moduleRef.createNestApplication();

const prisma: PostgreSqlPrismaOrmService = moduleRef.get(PostgreSqlPrismaOrmService);

const clinicFactory: ClinicFactory = moduleRef.get(ClinicFactory);
const psychologistFactory: PsychologistFactory = moduleRef.get(PsychologistFactory);
const patientFactory: PatientFactory = moduleRef.get(PatientFactory);

const jwt: JwtService = moduleRef.get(JwtService);

// Necessary to validate DTO route params in the controller
Expand Down Expand Up @@ -55,18 +61,26 @@ export async function setupE2ETest() {
state: faker.location.city(),
});

// Creating a patient to use in tests
const patient = await patientFactory.makePrismaPatient({
psychologistId: psychologist.id,
clinicId: clinic.id,
});

return {
prisma,
app,
psychologistFactory,
clinicFactory,
jwt,
psychologist,
id,
hashedPassword,
password,
jwt,
access_token,
invalid_access_token,
psychologist,
clinicFactory,
clinic,
hashedPassword,
password,
patientFactory,
patient,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('[E2E] - Delete Clinic', () => {

it('[DELETE] - Should return an error when trying to delete a clinic without access_token', async () => {
const response = await request(app.getHttpServer()).delete(
`/clinic/${clinic.id}/delete`
`/clinic/${clinic.id}/delete`,
);

expect(response.status).toBe(401);
Expand Down Expand Up @@ -63,13 +63,15 @@ describe('[E2E] - Delete Clinic', () => {
expect.objectContaining({
clinic: expect.objectContaining({ name: expect.any(String) }),
deletedAt: expect.any(String),
})
}),
);

const deletedClinic = await prisma['clinic'].findUnique({
where: { id: clinic.id },
});

console.log('DELETED CLINIC ---->', deletedClinic);

expect(deletedClinic).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Body, Controller, Post } from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { postMethodDocs } from './docs';

import { GlobalAppHttpException } from '@clinicControl/core-rest-api/core/src/shared/errors/globalAppHttpException';

import { CreatePatientDto } from '@clinicControl/core-rest-api/core/src/domains/patient/use-cases/create-patient/create-patient-dto';
import { NestjsCreatePatientService } from './nestjs-create-patient.service';

@ApiTags('patient')
@ApiBearerAuth()
@Controller({
path: 'patient',
})
export class CreatePatientController {
constructor(private createPatientService: NestjsCreatePatientService) {}

@Post('create')
@ApiOperation(postMethodDocs)
async execute(@Body() createPatientDto: CreatePatientDto) {
try {
await this.createPatientService.execute(createPatientDto);

return {
message: 'Patient created successfully',
};
} catch (error: unknown) {
throw new GlobalAppHttpException(error);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import request from 'supertest';

import { PatientEntity } from '@clinicControl/core-rest-api/core/src/domains/patient/entities/patient/entity';
import { CreatePatientDto } from '@clinicControl/core-rest-api/core/src/domains/patient/use-cases/create-patient/create-patient-dto';
import { INestApplication } from '@nestjs/common';
import { makePatient } from '../../../../../../tests/factories/make-patient';
import { PostgreSqlPrismaOrmService } from '../../../../../database/infra/prisma/prisma.service';
import { setupE2ETest } from '../../../shared/utils/e2e-tests-initial-setup';

describe('[E2E] - Create New Patient', () => {
let app: INestApplication;
let prisma: PostgreSqlPrismaOrmService;

let access_token: string;

let psychologistId: string;
let clinicId: string;

let patient: PatientEntity;

beforeAll(async () => {
const setup = await setupE2ETest();

app = setup.app;
prisma = setup.prisma;

access_token = setup.access_token;

clinicId = setup.clinic.id;
psychologistId = setup.psychologist.id;

patient = setup.patient;
});

it('[POST] - Should successfully create a new patient account', async () => {
const newPatient: CreatePatientDto = makePatient({
email: '[email protected]',
psychologistId,
clinicId,
});

const response = await request(app.getHttpServer())
.post('/patient/create')
.set('Authorization', `Bearer ${access_token}`)
.send(newPatient);

console.log('CREATE PATIENT --------->', response.body);
ItaloRAmaral marked this conversation as resolved.
Show resolved Hide resolved

const patientOnDatabase = await prisma.patient.findUnique({
luanavfg marked this conversation as resolved.
Show resolved Hide resolved
where: {
email: newPatient.email,
},
});

expect(response.statusCode).toBe(201);
expect(response.body.message).toBe('Patient created successfully');
expect(patientOnDatabase).toBeTruthy();
});

it('[POST] - Should return an error when trying to create a patient that already exists', async () => {
const response = await request(app.getHttpServer())
.post('/patient/create')
.set('Authorization', `Bearer ${access_token}`)
.send(patient);

expect(response.statusCode).toBe(409);
expect(response.body.message).toBe('patient already exists');
});

it('[POST] - Should return an error when trying to create a new patient without token', async () => {
const response = await request(app.getHttpServer())
.post('/patient/create')
.send(patient);

expect(response.statusCode).toBe(401);
expect(response.body.message).toBe('Invalid JWT token');
});

it('[POST] - Should return an error when trying to create a new patient without body request', async () => {
const response = await request(app.getHttpServer())
.post('/patient/create')
.set('Authorization', `Bearer ${access_token}`);

expect(response.statusCode).toBe(400);
expect(response.body.message).deep.equals([
'name must be a string',
'email must be a string',
'cpf must be a string',
'telephone must be a phone number',
'paymentMethod must be one of the following values: CREDIT_CARD, DEBIT_CARD, PIX, MONEY, HEALTH_INSURANCE, OTHER',
'psychologistId must be a string',
'clinicId must be a string',
]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { OperationObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';

const description = `
---
\`Experimental\`
---

### Create a new Patient in the system

This endpoint help you to create a new patient.
You must at least provide one of the following body parameters
`;

export const postMethodDocs: Partial<OperationObject> = {
summary: 'Create a new patient',
description,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Injectable } from '@nestjs/common';

import { PatientDatabaseRepository } from '@clinicControl/core-rest-api/core/src/domains/patient/repositories/database-repository';
import { CreatePatientService } from '@clinicControl/core-rest-api/core/src/domains/patient/use-cases/create-patient/create-patient.service';

@Injectable()
export class NestjsCreatePatientService extends CreatePatientService {
constructor(patientDatabaseRepository: PatientDatabaseRepository) {
super(patientDatabaseRepository);
}
}
ItaloRAmaral marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@baseUrl = http://localhost:3333/core
@authToken = {{loginauthenticate.response.body.access_token}}
@psychologist_id = {{loginauthenticate.response.body.user.id}}

### Authenticate User

# @name loginauthenticate
POST http://localhost:3333/core/psychologist/login HTTP/1.1
Content-Type: application/json

{
"email": "[email protected]",
"password": "joe-password"
}

### Create a new patient
POST http://localhost:3333/core/patient/create HTTP/1.1
Content-Type: application/json
Authorization: Bearer {{authToken}}

{
"name": "Abigail Doe",
"email": "[email protected]",
"cpf": "123.456.789-00",
"telephone": "(11) 99999-9999",
"paymentMethod": "CREDIT_CARD",
"psychologistId": "{{psychologist_id}}",
"clinicId": "1fcdadda-31d1-4358-81c7-7011c70e5014"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- DropForeignKey
ALTER TABLE "patient" DROP CONSTRAINT "patient_clinic_id_fkey";

-- DropForeignKey
ALTER TABLE "patient" DROP CONSTRAINT "patient_psychologist_id_fkey";

-- DropForeignKey
ALTER TABLE "patient_appointments_registry" DROP CONSTRAINT "patient_appointments_registry_patient_id_fkey";

-- DropForeignKey
ALTER TABLE "patient_appointments_registry" DROP CONSTRAINT "patient_appointments_registry_psychologist_id_fkey";

-- AddForeignKey
ALTER TABLE "patient" ADD CONSTRAINT "patient_psychologist_id_fkey" FOREIGN KEY ("psychologist_id") REFERENCES "psychologist"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "patient" ADD CONSTRAINT "patient_clinic_id_fkey" FOREIGN KEY ("clinic_id") REFERENCES "clinic"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "patient_appointments_registry" ADD CONSTRAINT "patient_appointments_registry_patient_id_fkey" FOREIGN KEY ("patient_id") REFERENCES "patient"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "patient_appointments_registry" ADD CONSTRAINT "patient_appointments_registry_psychologist_id_fkey" FOREIGN KEY ("psychologist_id") REFERENCES "psychologist"("id") ON DELETE CASCADE ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ model Patient {
patientAppointmentsRegistry PatientAppointmentsRegistry[]

psychologistId String @map("psychologist_id")
psychologist Psychologist @relation(fields: [psychologistId], references: [id])
psychologist Psychologist @relation(fields: [psychologistId], references: [id], onDelete: Cascade)
clinicId String @map("clinic_id")
clinic Clinic @relation(fields: [clinicId], references: [id])
clinic Clinic @relation(fields: [clinicId], references: [id], onDelete: Cascade)

@@map("patient")
}
Expand Down Expand Up @@ -120,9 +120,9 @@ model PatientAppointmentsRegistry {
registry Json @db.Json

patientId String @map("patient_id")
patient Patient @relation(fields: [patientId], references: [id])
patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade)
psychologistId String @map("psychologist_id")
psychologist Psychologist @relation(fields: [psychologistId], references: [id])
psychologist Psychologist @relation(fields: [psychologistId], references: [id], onDelete: Cascade)

@@map("patient_appointments_registry")
}
Loading