diff --git a/src/domain/course-management/application/use-cases/get-class-details.spec.ts b/src/domain/course-management/application/use-cases/get-class-details.spec.ts new file mode 100644 index 0000000..95c4a48 --- /dev/null +++ b/src/domain/course-management/application/use-cases/get-class-details.spec.ts @@ -0,0 +1,39 @@ +import { ResourceNotFoundError } from '@/core/errors/errors/resource-not-found-error' +import { makeClass } from '../../../../../test/factories/make-class' +import { InMemoryClassesRepository } from '../../../../../test/repositories/in-memory-classes-repository' +import { GetClassDetailsUseCase } from './get-class-details' + +let inMemoryClassesRepository: InMemoryClassesRepository +let sut: GetClassDetailsUseCase + +describe('Get module details use case', () => { + beforeEach(() => { + inMemoryClassesRepository = new InMemoryClassesRepository() + sut = new GetClassDetailsUseCase(inMemoryClassesRepository) + }) + + it('should be able to get class details', async () => { + const classToFind = makeClass({ name: 'John Doe Class' }) + await inMemoryClassesRepository.create(classToFind) + + const result = await sut.exec({ + classId: classToFind.id.toString() + }) + + expect(result.isRight()).toBe(true) + expect(result.value).toMatchObject({ + class: expect.objectContaining({ + name: 'John Doe Class' + }) + }) + }) + + it('should not be able to get class details of a inexistent class', async () => { + const result = await sut.exec({ + classId: 'inexistentClassId' + }) + + expect(result.isLeft()).toBe(true) + expect(result.value).toBeInstanceOf(ResourceNotFoundError) + }) +}) diff --git a/src/domain/course-management/application/use-cases/get-class-details.ts b/src/domain/course-management/application/use-cases/get-class-details.ts new file mode 100644 index 0000000..85c4621 --- /dev/null +++ b/src/domain/course-management/application/use-cases/get-class-details.ts @@ -0,0 +1,36 @@ +import { left, right, type Either } from '@/core/either' +import { ResourceNotFoundError } from '@/core/errors/errors/resource-not-found-error' +import { type UseCase } from '@/core/use-cases/use-case' +import { type Class } from '../../enterprise/entities/class' +import { type ClassesRepository } from '../repositories/classes-repository' + +interface GetClassDetailsUseCaseRequest { + classId: string +} + +type GetClassDetailsUseCaseResponse = Either< +ResourceNotFoundError, +{ + class: Class +} +> + +export class GetClassDetailsUseCase implements UseCase { + constructor( + private readonly classesRepository: ClassesRepository + ) { } + + async exec({ + classId + }: GetClassDetailsUseCaseRequest): Promise { + const classToFind = await this.classesRepository.findById(classId) + + if (!classToFind) { + return left(new ResourceNotFoundError()) + } + + return right({ + class: classToFind + }) + } +} diff --git a/src/domain/course-management/application/use-cases/get-module-details.spec.ts b/src/domain/course-management/application/use-cases/get-module-details.spec.ts index de03a84..12f094b 100644 --- a/src/domain/course-management/application/use-cases/get-module-details.spec.ts +++ b/src/domain/course-management/application/use-cases/get-module-details.spec.ts @@ -34,7 +34,7 @@ describe('Get module details use case', () => { it('should not be able to get module details of a inexistent module', async () => { const result = await sut.exec({ - moduleId: 'inexistentCourseId' + moduleId: 'inexistentModuleId' }) expect(result.isLeft()).toBe(true) diff --git a/src/infra/http/controllers/get-class-details.ts b/src/infra/http/controllers/get-class-details.ts new file mode 100644 index 0000000..68dd957 --- /dev/null +++ b/src/infra/http/controllers/get-class-details.ts @@ -0,0 +1,38 @@ +import { ResourceNotFoundError } from '@/core/errors/errors/resource-not-found-error' +import { makeClassMapper } from '@/infra/database/prisma/mappers/factories/make-class-mapper' +import { makeGetClassDetailsUseCase } from '@/infra/use-cases/factories/make-get-class-details-use-case' +import { type FastifyReply, type FastifyRequest } from 'fastify' +import { z } from 'zod' +import { ClassPresenter } from '../presenters/class-presenter' + +const getClassDetailsParamsSchema = z.object({ + classId: z.string().uuid() +}) + +export async function getClassDetailsController(request: FastifyRequest, reply: FastifyReply) { + const { classId } = getClassDetailsParamsSchema.parse(request.params) + + const getClassDetailsUseCase = makeGetClassDetailsUseCase() + + const result = await getClassDetailsUseCase.exec({ + classId + }) + + if (result.isLeft()) { + const error = result.value + + switch (error.constructor) { + case ResourceNotFoundError: + return await reply.status(404).send({ message: error.message }) + default: + return await reply.status(500).send({ message: error.message }) + } + } + + const classMapper = makeClassMapper() + const classToSend = await classMapper.toPrisma(result.value.class) + + return await reply.status(200).send({ + class: ClassPresenter.toHTTP(classToSend) + }) +} diff --git a/src/infra/http/routes/class.ts b/src/infra/http/routes/class.ts index 71a48aa..a444bc0 100644 --- a/src/infra/http/routes/class.ts +++ b/src/infra/http/routes/class.ts @@ -3,10 +3,12 @@ import { addClassToModuleController } from '../controllers/add-class-to-module' import { deleteClassController } from '../controllers/delete-class' import { editClassDetailsController } from '../controllers/edit-class-details' import { fetchCourseClassesController } from '../controllers/fetch-course-classes' +import { getClassDetailsController } from '../controllers/get-class-details' import { verifyJwt } from '../middlewares/verify-jwt' import { verifyUserRole } from '../middlewares/verify-user-role' export async function classRoutes(app: FastifyInstance) { + app.get('/classes/:classId', { onRequest: [verifyJwt] }, getClassDetailsController) app.get('/courses/:courseId/classes', { onRequest: [verifyJwt] }, fetchCourseClassesController) app.post('/modules/:moduleId/classes/video/:videoId', { onRequest: [verifyJwt, verifyUserRole('INSTRUCTOR')] }, addClassToModuleController) diff --git a/src/infra/use-cases/factories/make-get-class-details-use-case.ts b/src/infra/use-cases/factories/make-get-class-details-use-case.ts new file mode 100644 index 0000000..f1468d2 --- /dev/null +++ b/src/infra/use-cases/factories/make-get-class-details-use-case.ts @@ -0,0 +1,12 @@ +import { GetClassDetailsUseCase } from '@/domain/course-management/application/use-cases/get-class-details' +import { makePrismaClassesRepository } from '@/infra/database/prisma/repositories/factories/make-prisma-classes-repository' + +export function makeGetClassDetailsUseCase() { + const prismaClassesRepository = makePrismaClassesRepository() + + const getClassDetailsUseCase = new GetClassDetailsUseCase( + prismaClassesRepository + ) + + return getClassDetailsUseCase +}