Skip to content

Commit

Permalink
Merge pull request #123 from ItaloRAmaral/120-criacao-de-endpoint-de-…
Browse files Browse the repository at this point in the history
…criacao-de-consultas

120 criacao de endpoint de criacao de consultas
  • Loading branch information
ItaloRAmaral authored Feb 5, 2024
2 parents 5fcb812 + 299afed commit eb45ed8
Show file tree
Hide file tree
Showing 19 changed files with 377 additions and 24 deletions.
21 changes: 21 additions & 0 deletions apps/core-rest-api/http/appointment-client.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@authToken = {{$dotenv AUTH_TOKEN}}
@apiKey = {{$dotenv API_KEY}}

# @appointmentDate = {{require "./appointmentDate.js"}}
####

# @name create_appointment
POST http://localhost:3333/core/appointment/create HTTP/1.1
Content-Type: application/json
Authorization: Bearer {{authToken}}

{
"psychologistId": "{{$dotenv PSYCHOLOGIST_ID}}",
"clinicId": "{{$dotenv CLINIC_ID}}",
"patientId": "{{$dotenv PATIENT_ID}}",
"paymentMethod": "CREDIT_CARD",
"online": false,
"confirmed": true,
"cancelled": false,
"date": "2024-01-19T08:30:54"
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { AuthenticatePsychologistController } from './use-cases/psychologist/aut
import { CreatePsychologistController } from './use-cases/psychologist/create-psychologist/create-psychologist.controller';
import { DeletePsychologistController } from './use-cases/psychologist/delete-psychologist/delete-psychologist.controller';

import { CreateAppointmentController } from './use-cases/appointment/create-appointment/create-appointment.controller';
import { NestjsCreateAppointmentService } from './use-cases/appointment/create-appointment/nestjs-create-appointment.service';
import { NestjsCreateClinicService } from './use-cases/clinic/create-clinic/nestjs-create-clinic.service';
import { NestjsDeleteClinicService } from './use-cases/clinic/delete-clinic/nestjs-delete-clinic.service';
import { NestjsUpdateClinicService } from './use-cases/clinic/update-clinic/nestjs-update-clinic.service';
Expand Down Expand Up @@ -53,7 +55,8 @@ import { NestjsUpdatePsychologistService } from './use-cases/psychologist/update
DeleteClinicController,
CreatePatientController,
DeletePatientController,
CreatePatientAppointmentRegistryController
CreatePatientAppointmentRegistryController,
CreateAppointmentController
],
providers: [
BcryptHasherService,
Expand All @@ -67,7 +70,8 @@ import { NestjsUpdatePsychologistService } from './use-cases/psychologist/update
NestjsDeleteClinicService,
NestjsCreatePatientService,
NestjsDeletePatientService,
NestjsCreatePatientAppointmentRegistryService
NestjsCreatePatientAppointmentRegistryService,
NestjsCreateAppointmentService
],
})
export class ApiModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Body, Controller, Post } from "@nestjs/common";
import { ApiBearerAuth, ApiTags } from "@nestjs/swagger";
import { GlobalAppHttpException } from '../../../../../../shared/errors/globalAppHttpException';
import { CreateSingleAppointmentInputDto } from "./dto";
import { NestjsCreateAppointmentService } from "./nestjs-create-appointment.service";

interface CreateAppointmentResponse {
message: string;
}
@ApiTags('Appointment')
@ApiBearerAuth()
@Controller({
path: 'appointment',
})
export class CreateAppointmentController {
constructor(private createAppointmentService: NestjsCreateAppointmentService) {}

@Post('create')
// @ApiOperation(postMethodDocs)
async execute(@Body() createAppointmentDto: CreateSingleAppointmentInputDto): Promise<CreateAppointmentResponse>{
try {
await this.createAppointmentService.execute({...createAppointmentDto, date: new Date(createAppointmentDto.date)});

return {
message: 'Appointment created successfully',
}
} catch (error) {
throw new GlobalAppHttpException(error);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { faker } from "@faker-js/faker";
import { INestApplication } from "@nestjs/common";
import request from 'supertest';
import { makeAppointment } from '../../../../../../../../tests/factories/make-appointment';
import { setupE2ETest } from '../../../../../../../../tests/utils/e2e-tests-initial-setup';
import { AppointmentEntity } from '../../../../../../core/domains/appointment/entities/appointment/entity';
import { PaymentMethod } from '../../../../../../core/shared/interfaces/payments';
import { PostgreSqlPrismaOrmService } from '../../../../../database/infra/prisma/prisma.service';

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

let access_token: string;

let psychologistId: string;
let clinicId: string;
let patientId: string;


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;

patientId = setup.patient.id;
});

it('[POST] - Should successfully create a new appointment', async () => {
const newAppointment: AppointmentEntity = makeAppointment({
psychologistId,
clinicId,
patientId,
date: faker.date.recent({ days: 10 }),
cancelled: false,
confirmationDate: null,
confirmed: true,
online: false,
paymentMethod: PaymentMethod.CREDIT_CARD
});
const response = await request(app.getHttpServer())
.post('/appointment/create')
.set('Authorization', `Bearer ${access_token}`)
.send(newAppointment);

const appointmentOnDatabase = await prisma.appointment.findFirst({
where: {
patientId: newAppointment.patientId,
},
});

expect(response.statusCode).toBe(201);
expect(response.body.message).toBe('Appointment created successfully');
expect(appointmentOnDatabase).toBeTruthy();
});
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { IsBoolean, IsDate, IsEnum, IsOptional, IsString } from 'class-validator';
import { PaymentMethod } from '../../../../../../core/shared/interfaces/payments';

export class CreateSingleAppointmentInputDto {
@IsString()
psychologistId!: string;

@IsString()
patientId!: string;

@IsString()
date!: string;

@IsBoolean()
online!: boolean;

@IsString()
clinicId!: string;

@IsBoolean()
confirmed!: boolean;

@IsDate()
@IsOptional()
confirmationDate?: Date | null;

@IsBoolean()
cancelled!: boolean;

@IsEnum(PaymentMethod)
paymentMethod!: PaymentMethod;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Injectable } from '@nestjs/common';
import { AppointmentDatabaseRepository } from '../../../../../../core/domains/appointment/repositories/database-repository';
import { CreateSingleAppointmentService } from '../../../../../../core/domains/appointment/use-cases/create-single-appointment/create-single-appointment.service';

@Injectable()
export class NestjsCreateAppointmentService extends CreateSingleAppointmentService {
constructor(appointmentDatabaseRepository: AppointmentDatabaseRepository) {
super(appointmentDatabaseRepository);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ describe('[E2E] - Delete Clinic', () => {
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,42 @@
import {
Prisma,
Appointment as PrismaAppointmentDto,
PaymentMethod as PrismaPaymentMethod,
} from '@prisma/client';

import { AppointmentEntity } from '../../../core/domains/appointment/entities/appointment/entity';
import { CreateSingleAppointmentDto } from '../../../core/domains/appointment/use-cases/create-single-appointment/create-single-appointment-dto';
import { UpdateAppointmentInfoDto } from '../../../core/domains/appointment/use-cases/update-appointment-info/update-appointment-info-dto';
import { PaymentMethod } from '../../../core/shared/interfaces/payments';

export class PostgresqlPrismaAppointmentMapper {
static toDomain(raw: PrismaAppointmentDto): AppointmentEntity {
return new AppointmentEntity({
...raw,
paymentMethod: raw.paymentMethod as unknown as PaymentMethod,
date: (raw.date).toString()
});
}

static toPrismaCreate(raw: CreateSingleAppointmentDto): Prisma.AppointmentCreateArgs {
return {
data: {
...raw,
paymentMethod: raw.paymentMethod as unknown as PrismaPaymentMethod,
date: new Date(raw.date)
},
};
}

static toPrismaUpdate(raw: UpdateAppointmentInfoDto): Prisma.AppointmentUpdateArgs {
return {
data: {
...raw,
done: raw.done as boolean
},
where: {
id: raw.id,
},
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { ConflictException, Injectable } from '@nestjs/common';
import { AppointmentEntity } from '../../../../core/domains/appointment/entities/appointment/entity';
import { AppointmentDatabaseRepository } from '../../../../core/domains/appointment/repositories/database-repository';
import { CreateSingleAppointmentDto } from '../../../../core/domains/appointment/use-cases/create-single-appointment/create-single-appointment-dto';
import { UpdatedAppointmentDateDto } from '../../../../core/domains/appointment/use-cases/update-appointment-date/update-appointment-date-dto';
import { UpdateAppointmentInfoDto } from '../../../../core/domains/appointment/use-cases/update-appointment-info/update-appointment-info-dto';
import { APPOINTMENT_ERROR_MESSAGES } from '../../../../shared/errors/error-messages';
import { PostgreSqlPrismaOrmService } from "../../infra/prisma/prisma.service";
import { PostgresqlPrismaAppointmentMapper } from '../../mappers/postgresql-prisma-appointment-mapper';

@Injectable()
export class PostgresqlPrismaOrmAppointmentRepository implements AppointmentDatabaseRepository {
constructor(private postgresqlPrismaOrmService: PostgreSqlPrismaOrmService) {}

async findSingleAppointmentById(appointmentId: string): Promise<AppointmentEntity | null> {
const appointment = await this.postgresqlPrismaOrmService['appointment'].findUnique({where: {
id: appointmentId
}})
if (!appointment) {
return null;
}

return PostgresqlPrismaAppointmentMapper.toDomain(appointment);
}

async findSingleAppointmentByDate(appointmentDate: Date): Promise<AppointmentEntity | null> {
const appointment = await this.postgresqlPrismaOrmService['appointment'].findFirst({
where: {
date: appointmentDate,
}
})

if (!appointment) {
return null;
}

return PostgresqlPrismaAppointmentMapper.toDomain(appointment);
}

async createSingleAppointment(appointment: CreateSingleAppointmentDto): Promise<AppointmentEntity> {
const isAppointmentExists = await this.findSingleAppointmentByDate(new Date(appointment.date));

if (isAppointmentExists) {
throw new ConflictException(APPOINTMENT_ERROR_MESSAGES['CONFLICTING_DATE_TIME']);
}

const toPrismaEntity = PostgresqlPrismaAppointmentMapper.toPrismaCreate({
...appointment,
});

const newAppointment =
await this.postgresqlPrismaOrmService['appointment'].create(toPrismaEntity);

return PostgresqlPrismaAppointmentMapper.toDomain(newAppointment);
}

async getAppointments(): Promise<AppointmentEntity[]> {
const appointments = await this.postgresqlPrismaOrmService['appointment'].findMany();

return appointments.map((appointment) => PostgresqlPrismaAppointmentMapper.toDomain(appointment))
}

async updateAppointmentInfo(newAppointmentInfo: UpdateAppointmentInfoDto): Promise<void> {
const oldAppointmentInfo = await this.findSingleAppointmentById(newAppointmentInfo.id);

if (!oldAppointmentInfo) {
throw new ConflictException(APPOINTMENT_ERROR_MESSAGES['APPOINTMENT_NOT_FOUND']);
}

const toPrismaEntity = PostgresqlPrismaAppointmentMapper.toPrismaUpdate({
...newAppointmentInfo,
});

await this.postgresqlPrismaOrmService['appointment'].update(toPrismaEntity);
}

async updateAppointmentDate(newAppointmentInfo: UpdatedAppointmentDateDto): Promise<void> {
const oldAppointmentInfo = await this.findSingleAppointmentById(newAppointmentInfo.id);

if (!oldAppointmentInfo) {
throw new ConflictException(APPOINTMENT_ERROR_MESSAGES['APPOINTMENT_NOT_FOUND']);
}

const toPrismaEntity = PostgresqlPrismaAppointmentMapper.toPrismaUpdate({
...newAppointmentInfo,
});

await this.postgresqlPrismaOrmService['appointment'].update(toPrismaEntity);
}

async deleteSingleAppointment(appointmentId: string): Promise<void> {
const isAppointmentExists = await this.findSingleAppointmentById(appointmentId);

if (!isAppointmentExists) {
throw new ConflictException(APPOINTMENT_ERROR_MESSAGES['APPOINTMENT_NOT_FOUND']);
}

await this.postgresqlPrismaOrmService['appointment'].delete({
where: {
id: appointmentId,
},
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { ClinicDatabaseRepository } from '../../../core/domains/clinic/repositor
import { PatientDatabaseRepository } from '../../../core/domains/patient/repositories/database-repository';
import { PsychologistDatabaseRepository } from '../../../core/domains/psychologist/repositories/database-repository';

import { AppointmentDatabaseRepository } from '../../../core/domains/appointment/repositories/database-repository';
import { PatientAppointmentRegistryDatabaseRepository } from '../../../core/domains/patient-appointment-registry/repositories/database-repository';
import { PostgreSqlPrismaOrmService } from '../infra/prisma/prisma.service';
import { PostgresqlPrismaOrmAppointmentRepository } from './appointment/postgres-prisma-orm-appointment-repository';
import { PostgresqlPrismaOrmClinicRepository } from './clinic/postgres-prisma-orm-clinic-repository';
import { PostgresqlPrismaOrmPatientAppointmentRegistryRepository } from './patient-appointment-registry/postgresql-prisma-orm-registry-repository';
import { PostgresqlPrismaOrmPatientRepository } from './patient/postgres-prisma-orm-patient-repository';
Expand All @@ -32,6 +34,10 @@ import { PostgresqlPrismaOrmPsychologistRepository } from './psychologist/postgr
provide: PatientAppointmentRegistryDatabaseRepository,
useClass: PostgresqlPrismaOrmPatientAppointmentRegistryRepository,
},
{
provide: AppointmentDatabaseRepository,
useClass: PostgresqlPrismaOrmAppointmentRepository,
},
],
exports: [
PostgreSqlPrismaOrmService,
Expand All @@ -51,6 +57,10 @@ import { PostgresqlPrismaOrmPsychologistRepository } from './psychologist/postgr
provide: PatientAppointmentRegistryDatabaseRepository,
useClass: PostgresqlPrismaOrmPatientAppointmentRegistryRepository,
},
{
provide: AppointmentDatabaseRepository,
useClass: PostgresqlPrismaOrmAppointmentRepository,
},
],
})
export class DatabaseRepositoriesModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { APPOINTMENT_ERROR_MESSAGES } from '../../../../shared/errors/error-mess
import { AppointmentEntity } from '../entities/appointment/entity';
import { CreateSingleAppointmentDto } from '../use-cases/create-single-appointment/create-single-appointment-dto';
import { UpdatedAppointmentDateDto } from '../use-cases/update-appointment-date/update-appointment-date-dto';
import { UpdatedAppointmentInfoDto } from '../use-cases/update-appointment-info/update-appointment-info-dto';
import { UpdateAppointmentInfoDto } from '../use-cases/update-appointment-info/update-appointment-info-dto';
import { AppointmentDatabaseRepository } from './database-repository';

export class InMemoryAppointmentDatabaseRepository
Expand Down Expand Up @@ -49,7 +49,7 @@ export class InMemoryAppointmentDatabaseRepository
}

async updateAppointmentInfo(
newAppointmentInfo: UpdatedAppointmentInfoDto,
newAppointmentInfo: UpdateAppointmentInfoDto,
): Promise<void> {
const oldAppointmentInfo = await this.findSingleAppointmentById(
newAppointmentInfo.id,
Expand Down
Loading

0 comments on commit eb45ed8

Please sign in to comment.