Skip to content

Commit

Permalink
Feat(Class, Enrollment, Route): Mark class as completed route
Browse files Browse the repository at this point in the history
  • Loading branch information
Artur-Poffo committed Feb 23, 2024
1 parent b16b2f3 commit 02e8ae0
Show file tree
Hide file tree
Showing 12 changed files with 48 additions and 45 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@

### Enrollments
- [x] POST /courses/:courseId/enroll - Enroll to course
- [x] POST /enrollments/:enrollmentId/modules/:moduleId/complete - Mark module as completed
- [ ] POST /enrollments/:enrollmentId/classes/:classId/complete - Mark class as completed
- [ ] POST /enrollments/:enrollmentId/complete - Mark enrollment as completed
- [x] GET /courses/:courseId/students/:studentId/enrollments - Get enrollment of a student on a course
- [x] DELETE /enrollments/:enrollmentId - Cancel enrollment

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export interface EnrollmentsRepository {
findManyByCourseId: (courseId: string) => Promise<Enrollment[]>
findManyByStudentId: (studentId: string) => Promise<Enrollment[]>
findManyStudentsByCourseId: (courseId: string) => Promise<Student[]>
markItemAsCompleted: (completedItemId: string, enrollment: Enrollment) => Promise<Enrollment | null>
markAsCompleted: (enrollment: Enrollment) => Promise<Enrollment | null>
countEnrollmentsByYear: (year: number) => Promise<number>
create: (enrollment: Enrollment) => Promise<Enrollment | null>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ export class MarkClassAsCompletedUseCase implements UseCase<MarkClassAsCompleted
})
await this.enrollmentCompletedItemsRepository.create(completedItem)

await this.enrollmentsRepository.markItemAsCompleted(completedItem.id.toString(), enrollment)

return right({
class: classToMarkAsCompleted
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,6 @@ describe('Mark course as completed use case', () => {
inMemoryEnrollmentCompletedItemsRepository.create(secondCompletedItem)
])

await Promise.all([
inMemoryEnrollmentsRepository.markItemAsCompleted(firstCompletedItem.id.toString(), enrollment),
inMemoryEnrollmentsRepository.markItemAsCompleted(secondCompletedItem.id.toString(), enrollment)
])

const result = await sut.exec({
enrollmentId: enrollment.id.toString(),
studentId: student.id.toString()
Expand Down Expand Up @@ -154,8 +149,6 @@ describe('Mark course as completed use case', () => {
const completedItem = makeEnrollmentCompletedItem({ enrollmentId: enrollment.id, itemId: classToAdd.id, type: 'CLASS' })
await inMemoryEnrollmentCompletedItemsRepository.create(completedItem)

await inMemoryEnrollmentsRepository.markItemAsCompleted(completedItem.id.toString(), enrollment)

const result = await sut.exec({
enrollmentId: enrollment.id.toString(),
studentId: wrongStudent.id.toString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ describe('Mark module as completed use case', () => {
const completedItem = makeEnrollmentCompletedItem({ enrollmentId: enrollment.id, itemId: classToAdd.id, type: 'CLASS' })
await inMemoryEnrollmentCompletedItemsRepository.create(completedItem)

await inMemoryEnrollmentsRepository.markItemAsCompleted(completedItem.id.toString(), enrollment)

const result = await sut.exec({
enrollmentId: enrollment.id.toString(),
moduleId: module.id.toString(),
Expand Down Expand Up @@ -147,8 +145,6 @@ describe('Mark module as completed use case', () => {
const completedItem = makeEnrollmentCompletedItem({ enrollmentId: enrollment.id, itemId: classToAdd.id, type: 'CLASS' })
await inMemoryEnrollmentCompletedItemsRepository.create(completedItem)

await inMemoryEnrollmentsRepository.markItemAsCompleted(completedItem.id.toString(), enrollment)

const result = await sut.exec({
enrollmentId: enrollment.id.toString(),
moduleId: module.id.toString(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ export class MarkModuleAsCompletedUseCase implements UseCase<MarkModuleAsComplet
})
await this.enrollmentCompletedItemsRepository.create(completedItem)

await this.enrollmentsRepository.markItemAsCompleted(completedItem.id.toString(), enrollment)

return right({
module
})
Expand Down
10 changes: 2 additions & 8 deletions src/domain/course-management/enterprise/entities/enrollment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { type Optional } from '@/core/types/optional'
export interface EnrollmentProps {
studentId: UniqueEntityID
courseId: UniqueEntityID
completedItems: UniqueEntityID[] // References for enrollment-completed-item entity
ocurredAt: Date
completedAt?: Date | null
}
Expand All @@ -19,10 +18,6 @@ export class Enrollment extends Entity<EnrollmentProps> {
return this.props.courseId
}

get completedItems() {
return this.props.completedItems
}

get ocurredAt() {
return this.props.ocurredAt
}
Expand All @@ -36,15 +31,14 @@ export class Enrollment extends Entity<EnrollmentProps> {
}

static create(
props: Optional<EnrollmentProps, 'ocurredAt' | 'completedAt' | 'completedItems'>,
props: Optional<EnrollmentProps, 'ocurredAt' | 'completedAt'>,
id?: UniqueEntityID
) {
const enrollment = new Enrollment(
{
...props,
ocurredAt: props.ocurredAt ?? new Date(),
completedAt: null,
completedItems: []
completedAt: null
},
id
)
Expand Down
10 changes: 3 additions & 7 deletions src/infra/database/prisma/mappers/enrollment-mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,19 @@ export class EnrollmentMapper {
) {}

async toDomain(raw: PrismaEnrollment): Promise<Enrollment | null> {
const completedItems = await this.enrollmentCompletedItemsRepository.findAllByEnrollmentId(raw.id)
const completedItemIds = completedItems.map(completedItem => completedItem.id)

return Enrollment.create(
{
courseId: new UniqueEntityID(raw.courseId),
studentId: new UniqueEntityID(raw.studentId),
completedAt: raw.completedAt,
ocurredAt: raw.ocurredAt,
completedItems: completedItemIds
ocurredAt: raw.ocurredAt
},
new UniqueEntityID(raw.id)
)
}

async toPrisma(enrollment: Enrollment): Promise<Prisma.EnrollmentUncheckedCreateInput> {
const completedItemIds = [...enrollment.completedItems]
const completedItemIds = await this.enrollmentCompletedItemsRepository.findAllByEnrollmentId(enrollment.id.toString())

return {
id: enrollment.id.toString(),
Expand All @@ -34,7 +30,7 @@ export class EnrollmentMapper {
completedAt: enrollment.completedAt,
ocurredAt: enrollment.ocurredAt,
enrollmentCompletedItems: {
connect: completedItemIds.map(completedItemId => ({ id: completedItemId.toString() })) as unknown as Prisma.EnrollmentCompletedItemWhereUniqueInput[]
connect: completedItemIds.map(completedItem => ({ id: completedItem.id.toString() })) as unknown as Prisma.EnrollmentCompletedItemWhereUniqueInput[]
}
}
}
Expand Down
38 changes: 38 additions & 0 deletions src/infra/http/controllers/mark-class-as-completed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { NotAllowedError } from '@/core/errors/errors/not-allowed-error'
import { ResourceNotFoundError } from '@/core/errors/errors/resource-not-found-error'
import { makeMarkClassAsCompletedUseCase } from '@/infra/use-cases/factories/make-mark-class-as-completed-use-case'
import { type FastifyReply, type FastifyRequest } from 'fastify'
import { z } from 'zod'

const markClassAsCompletedParamsSchema = z.object({
enrollmentId: z.string().uuid(),
classId: z.string().uuid()
})

export async function markClassAsCompletedController(request: FastifyRequest, reply: FastifyReply) {
const { enrollmentId, classId } = markClassAsCompletedParamsSchema.parse(request.params)
const { sub: studentId } = request.user

const markClassAsCompletedUseCase = makeMarkClassAsCompletedUseCase()

const result = await markClassAsCompletedUseCase.exec({
enrollmentId,
classId,
studentId
})

if (result.isLeft()) {
const error = result.value

switch (error.constructor) {
case ResourceNotFoundError:
return await reply.status(404).send({ message: error.message })
case NotAllowedError:
return await reply.status(401).send({ message: error.message })
default:
return await reply.status(500).send({ message: error.message })
}
}

return await reply.status(201).send()
}
2 changes: 2 additions & 0 deletions src/infra/http/routes/enrollment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { type FastifyInstance } from 'fastify'
import { cancelEnrollmentController } from '../controllers/cancel-enrollment'
import { enrollToCourseController } from '../controllers/enroll-to-course'
import { getEnrollmentDetailsController } from '../controllers/get-enrollment-details'
import { markClassAsCompletedController } from '../controllers/mark-class-as-completed'
import { verifyJwt } from '../middlewares/verify-jwt'
import { verifyUserRole } from '../middlewares/verify-user-role'

export async function enrollmentRoutes(app: FastifyInstance) {
app.get('/courses/:courseId/students/:studentId/enrollments', { onRequest: [verifyJwt] }, getEnrollmentDetailsController)

app.post('/courses/:courseId/enroll', { onRequest: [verifyJwt, verifyUserRole('STUDENT')] }, enrollToCourseController)
app.post('/enrollments/:enrollmentId/classes/:classId', { onRequest: [verifyJwt, verifyUserRole('STUDENT')] }, markClassAsCompletedController)

app.delete('/enrollments/:enrollmentId', { onRequest: [verifyJwt, verifyUserRole('STUDENT')] }, cancelEnrollmentController)
}
1 change: 0 additions & 1 deletion test/factories/make-enrollment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export function makeEnrollment(
{
studentId: override.courseId ?? new UniqueEntityID(),
courseId: override.courseId ?? new UniqueEntityID(),
completedItems: [],
ocurredAt: override.ocurredAt ?? new Date(),
completedAt: override.completedAt ?? null,
...override
Expand Down
13 changes: 0 additions & 13 deletions test/repositories/in-memory-enrollments-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,6 @@ export class InMemoryEnrollmentsRepository implements EnrollmentsRepository {
return courseStudents
}

async markItemAsCompleted(itemId: string, enrollment: Enrollment): Promise<Enrollment | null> {
const enrollmentCompletedItem = await this.inMemoryEnrollmentCompletedItemsRepository.findById(itemId)

if (!enrollmentCompletedItem) {
return null
}

enrollment.completedItems.push(enrollmentCompletedItem.id)
await this.save(enrollment)

return enrollment
}

async markAsCompleted(enrollment: Enrollment): Promise<Enrollment> {
enrollment.completedAt = new Date()
await this.save(enrollment)
Expand Down

0 comments on commit 02e8ae0

Please sign in to comment.