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

120 criacao de endpoint de criacao de consultas #123

Merged
merged 20 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
43e7920
adjust: change UpdateAppointmentInfoDto name
luanavfg Jan 16, 2024
5aaa605
feat: add create appointment controller
luanavfg Jan 16, 2024
51b65cb
feat: add create appointment nestjs service
luanavfg Jan 16, 2024
ca55bde
feat: add postgres prisma orm repository
luanavfg Jan 16, 2024
8ae8d81
feat: add appointment mapper
luanavfg Jan 16, 2024
266f723
feat: add appointment e2e tests file
luanavfg Jan 16, 2024
3de5a9d
feat: add create appointment route in api module
luanavfg Jan 16, 2024
37e1eb6
feat(CC-120): add appointment factory
luanavfg Jan 18, 2024
8202ec7
feat(CC-120): add appointment E2E test
luanavfg Jan 18, 2024
e1d6571
chore(CC-120): remove console log
luanavfg Jan 18, 2024
0adfae9
feat(CC-120): add repository in repositories module
luanavfg Jan 18, 2024
0ce1160
feat(CC-120): add appointment fake client
luanavfg Jan 30, 2024
171cd5f
chore(CC-120): adjust service name
luanavfg Jan 30, 2024
7c0af01
refactor(CC-120): change date to string
luanavfg Jan 30, 2024
1f1222a
refactor(CC-120): change date to string in repositories and mappers
luanavfg Jan 30, 2024
9b2cedd
refactor(CC-120): change date to string in service and controller
luanavfg Jan 30, 2024
ca381de
refactor(CC-120): revert date transformation into date
luanavfg Feb 1, 2024
a9ac72e
refactor(CC-120): transform date string into date format in controlle…
luanavfg Feb 1, 2024
1f1ab00
feat(CC-120): create input dto for appointment controller
luanavfg Feb 1, 2024
299afed
Merge branch 'main' of github.com:ItaloRAmaral/cliniccontrol into 120…
luanavfg Feb 5, 2024
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
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
Loading