Skip to content

Commit

Permalink
Merge pull request #18 from Artur-Poffo/feat-get-course-with-modules-…
Browse files Browse the repository at this point in the history
…and-classes

Feat(Course, Module, Class): Get course with modules and classes details
  • Loading branch information
Artur-Poffo authored Feb 5, 2024
2 parents 86cd2c3 + 797e4e9 commit 728cf59
Show file tree
Hide file tree
Showing 25 changed files with 216 additions and 102 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
- [x] Return information of an instructor with their courses.

- [x] Return information about a course.
- [ ] Return information about a course with its modules and classes.
- [x] Return information about a course with its modules and classes.
- [x] Classes and modules must have a field informing their position, e.g. this class is number one, so this is the first class of the course.
- [ ] Video streaming to watch the classes.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { type Class } from '../../enterprise/entities/class'

export interface ClassesRepository {
findById: (id: string) => Promise<Class | null>
findManyByCourseId: (courseId: string) => Promise<Class[]>
findManyByModuleId: (moduleId: string) => Promise<Class[]>
create: (classToAdd: Class) => Promise<Class>
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { type Course } from '../../enterprise/entities/course'
import { type CompleteCourseDTO } from '../../enterprise/entities/dtos/complete-course'
import { type CourseWithStudentsDTO } from '../../enterprise/entities/dtos/course-with-students'
import { type InstructorWithCoursesDTO } from '../../enterprise/entities/dtos/instructor-with-courses'

export interface CoursesRepository {
findById: (id: string) => Promise<Course | null>
findManyByInstructorId: (instructorId: string) => Promise<Course[]>
findCourseWithStudentsById: (id: string) => Promise<CourseWithStudentsDTO | null>
findCompleteCourseEntityById: (id: string) => Promise<CompleteCourseDTO | null>
findInstructorWithCoursesByInstructorId: (instructorId: string) => Promise<InstructorWithCoursesDTO | null>
create: (course: Course) => Promise<Course>
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { type Instructor } from '../../enterprise/entities/instructor'
import { type InstructorWithCoursesDTO } from './../../enterprise/entities/dtos/instructor-with-courses'

export interface InstructorsRepository {
findById: (id: string) => Promise<Instructor | null>
findByEmail: (email: string) => Promise<Instructor | null>
findByCpf: (cpf: string) => Promise<Instructor | null>
findInstructorWithCoursesById: (id: string) => Promise<InstructorWithCoursesDTO | null>
create: (instructor: Instructor) => Promise<Instructor>
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { type Class } from '../../enterprise/entities/class'
import { type Module } from '../../enterprise/entities/module'
import { type ModuleWithClassesDTO } from './../../enterprise/entities/dtos/module-with-classes'

export interface ModulesRepository {
findById: (id: string) => Promise<Module | null>
findManyByCourseId: (id: string) => Promise<Module[]>
findManyClassesByCourseId: (courseId: string) => Promise<Class[] | null>
findModuleWithClassesById: (id: string) => Promise<ModuleWithClassesDTO | null>
create: (module: Module) => Promise<Module>
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ import { ClassVideoRequiredError } from './errors/class-video-required-error'

let inMemoryVideosRepository: InMemoryVideosRepository
let inMemoryClassesRepository: InMemoryClassesRepository
let inMemoryInstructorsRepository: InMemoryInstructorRepository
let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let inMemoryInstructorsRepository: InMemoryInstructorRepository
let sut: AddClassToModuleUseCase

describe('Add class to a module use case', () => {
beforeEach(() => {
inMemoryVideosRepository = new InMemoryVideosRepository()
inMemoryClassesRepository = new InMemoryClassesRepository()
inMemoryInstructorsRepository = new InMemoryInstructorRepository()

inMemoryClassesRepository = new InMemoryClassesRepository(inMemoryModulesRepository)
inMemoryModulesRepository = new InMemoryModulesRepository(inMemoryClassesRepository)
inMemoryInstructorsRepository = new InMemoryInstructorRepository(inMemoryCoursesRepository)

inMemoryCoursesRepository = new InMemoryCoursesRepository(
inMemoryModulesRepository, inMemoryInstructorsRepository
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,22 @@ import { TagAlreadyAttachedError } from './errors/tag-already-attached-error'

let inMemoryCourseTagsRepository: InMemoryCourseTagsRepository
let inMemoryTagsRepository: InMemoryTagsRepository
let inMemoryClassesRepository: InMemoryClassesRepository
let inMemoryInstructorsRepository: InMemoryInstructorRepository
let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let inMemoryInstructorsRepository: InMemoryInstructorRepository
let inMemoryClassesRepository: InMemoryClassesRepository
let sut: AttachTagToCourseUseCase

describe('Attach tag to course use case', () => {
beforeEach(() => {
inMemoryCourseTagsRepository = new InMemoryCourseTagsRepository()
inMemoryTagsRepository = new InMemoryTagsRepository()
inMemoryClassesRepository = new InMemoryClassesRepository()
inMemoryInstructorsRepository = new InMemoryInstructorRepository()

inMemoryModulesRepository = new InMemoryModulesRepository(inMemoryClassesRepository)
inMemoryClassesRepository = new InMemoryClassesRepository(inMemoryModulesRepository)
inMemoryInstructorsRepository = new InMemoryInstructorRepository(inMemoryCoursesRepository)
inMemoryCoursesRepository = new InMemoryCoursesRepository(inMemoryModulesRepository, inMemoryInstructorsRepository)

sut = new AttachTagToCourseUseCase(inMemoryCourseTagsRepository, inMemoryTagsRepository, inMemoryCoursesRepository)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,28 @@ import { InMemoryModulesRepository } from './../../../../../test/repositories/in
import { InMemoryUsersRepository } from './../../../../../test/repositories/in-memory-users-repository'
import { AuthenticateUserUseCase } from './authenticate-user'

let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryClassesRepository: InMemoryClassesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let inMemoryInstructorsRepository: InMemoryInstructorRepository
let inMemoryStudentsRepository: InMemoryStudentsRepository
let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let inMemoryUsersRepository: InMemoryUsersRepository
let fakeHasher: FakeHasher
let fakeEncrypter: FakeEncrypter
let sut: AuthenticateUserUseCase

describe('Authenticate user use case', () => {
beforeEach(() => {
inMemoryModulesRepository = new InMemoryModulesRepository(inMemoryClassesRepository)
inMemoryClassesRepository = new InMemoryClassesRepository(inMemoryModulesRepository)
inMemoryCoursesRepository = new InMemoryCoursesRepository(inMemoryModulesRepository, inMemoryInstructorsRepository)
inMemoryInstructorsRepository = new InMemoryInstructorRepository(inMemoryCoursesRepository)
inMemoryClassesRepository = new InMemoryClassesRepository()
inMemoryInstructorsRepository = new InMemoryInstructorRepository()
inMemoryStudentsRepository = new InMemoryStudentsRepository()
inMemoryUsersRepository = new InMemoryUsersRepository(inMemoryInstructorsRepository, inMemoryStudentsRepository)
fakeHasher = new FakeHasher()
fakeEncrypter = new FakeEncrypter()

inMemoryModulesRepository = new InMemoryModulesRepository(inMemoryClassesRepository)
inMemoryCoursesRepository = new InMemoryCoursesRepository(inMemoryModulesRepository, inMemoryInstructorsRepository)
inMemoryUsersRepository = new InMemoryUsersRepository(inMemoryInstructorsRepository, inMemoryStudentsRepository)

sut = new AuthenticateUserUseCase(inMemoryUsersRepository, fakeHasher, fakeEncrypter)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ import { InMemoryInstructorRepository } from '../../../../../test/repositories/i
import { InMemoryModulesRepository } from '../../../../../test/repositories/in-memory-modules-repository'
import { GetCourseDetailsUseCase } from './get-course-details'

let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryClassesRepository: InMemoryClassesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let inMemoryInstructorsRepository: InMemoryInstructorRepository
let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let sut: GetCourseDetailsUseCase

describe('Get course details use case', () => {
beforeEach(() => {
inMemoryClassesRepository = new InMemoryClassesRepository()
inMemoryInstructorsRepository = new InMemoryInstructorRepository()

inMemoryModulesRepository = new InMemoryModulesRepository(inMemoryClassesRepository)
inMemoryClassesRepository = new InMemoryClassesRepository(inMemoryModulesRepository)
inMemoryCoursesRepository = new InMemoryCoursesRepository(inMemoryModulesRepository, inMemoryInstructorsRepository)
inMemoryInstructorsRepository = new InMemoryInstructorRepository(inMemoryCoursesRepository)

sut = new GetCourseDetailsUseCase(inMemoryCoursesRepository)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { ResourceNotFoundError } from '@/core/errors/errors/resource-not-found-error'
import { makeCourse } from '../../../../../test/factories/make-course'
import { makeInstructor } from '../../../../../test/factories/make-instructor'
import { makeModule } from '../../../../../test/factories/make-module'
import { InMemoryClassesRepository } from '../../../../../test/repositories/in-memory-classes-repository'
import { InMemoryCoursesRepository } from '../../../../../test/repositories/in-memory-courses-repository'
import { InMemoryInstructorRepository } from '../../../../../test/repositories/in-memory-instructors-repository'
import { InMemoryModulesRepository } from '../../../../../test/repositories/in-memory-modules-repository'
import { GetCourseWithModulesAndClassesUseCase } from './get-course-with-modules-and-classes'

let inMemoryClassesRepository: InMemoryClassesRepository
let inMemoryInstructorsRepository: InMemoryInstructorRepository
let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let sut: GetCourseWithModulesAndClassesUseCase

describe('Get course details with modules and classes use case', () => {
beforeEach(() => {
inMemoryClassesRepository = new InMemoryClassesRepository()
inMemoryInstructorsRepository = new InMemoryInstructorRepository()

inMemoryModulesRepository = new InMemoryModulesRepository(inMemoryClassesRepository)
inMemoryCoursesRepository = new InMemoryCoursesRepository(inMemoryModulesRepository, inMemoryInstructorsRepository)

sut = new GetCourseWithModulesAndClassesUseCase(inMemoryCoursesRepository)
})

it('should be able to get course details with modules and classes', async () => {
const instructor = makeInstructor()
await inMemoryInstructorsRepository.create(instructor)

const course = makeCourse({ name: 'John Doe Course', instructorId: instructor.id })
await inMemoryCoursesRepository.create(course)

const firstModule = makeModule({ name: 'First Module', courseId: course.id })
const secondModule = makeModule({ name: 'Second Module', courseId: course.id })

await Promise.all([
inMemoryModulesRepository.create(firstModule),
inMemoryModulesRepository.create(secondModule)
])

const result = await sut.exec({
courseId: course.id.toString()
})

expect(result.isRight()).toBe(true)
})

it('should not be able to get course details with modules and classes of a inexistent course', async () => {
const result = await sut.exec({
courseId: 'inexistentCourseId'
})

expect(result.isLeft()).toBe(true)
expect(result.value).toBeInstanceOf(ResourceNotFoundError)
})
})
Original file line number Diff line number Diff line change
@@ -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 CompleteCourseDTO } from '../../enterprise/entities/dtos/complete-course'
import { type CoursesRepository } from '../repositories/courses-repository'

interface GetCourseWithModulesAndClassesUseCaseRequest {
courseId: string
}

type GetCourseWithModulesAndClassesUseCaseResponse = Either<
ResourceNotFoundError,
{
course: CompleteCourseDTO
}
>

export class GetCourseWithModulesAndClassesUseCase implements UseCase<GetCourseWithModulesAndClassesUseCaseRequest, GetCourseWithModulesAndClassesUseCaseResponse> {
constructor(
private readonly coursesRepository: CoursesRepository
) { }

async exec({
courseId
}: GetCourseWithModulesAndClassesUseCaseRequest): Promise<GetCourseWithModulesAndClassesUseCaseResponse> {
const courseWithModulesAndClasses = await this.coursesRepository.findCompleteCourseEntityById(courseId)

if (!courseWithModulesAndClasses) {
return left(new ResourceNotFoundError())
}

return right({
course: courseWithModulesAndClasses
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ import { InMemoryInstructorRepository } from '../../../../../test/repositories/i
import { InMemoryModulesRepository } from '../../../../../test/repositories/in-memory-modules-repository'
import { GetCourseDetailsUseCase } from './get-course-details'

let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryClassesRepository: InMemoryClassesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let inMemoryInstructorsRepository: InMemoryInstructorRepository
let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let sut: GetCourseDetailsUseCase

describe('Get course details with students use case', () => {
beforeEach(() => {
inMemoryClassesRepository = new InMemoryClassesRepository()
inMemoryInstructorsRepository = new InMemoryInstructorRepository()

inMemoryModulesRepository = new InMemoryModulesRepository(inMemoryClassesRepository)
inMemoryClassesRepository = new InMemoryClassesRepository(inMemoryModulesRepository)
inMemoryCoursesRepository = new InMemoryCoursesRepository(inMemoryModulesRepository, inMemoryInstructorsRepository)
inMemoryInstructorsRepository = new InMemoryInstructorRepository(inMemoryCoursesRepository)

sut = new GetCourseDetailsUseCase(inMemoryCoursesRepository)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@ import { InMemoryInstructorRepository } from '../../../../../test/repositories/i
import { InMemoryModulesRepository } from '../../../../../test/repositories/in-memory-modules-repository'
import { GetInstructorWithCoursesUseCase } from './get-instructor-with-courses'

let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryClassesRepository: InMemoryClassesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let inMemoryInstructorsRepository: InMemoryInstructorRepository
let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let sut: GetInstructorWithCoursesUseCase

describe('Get instructors with their courses', () => {
beforeEach(() => {
inMemoryClassesRepository = new InMemoryClassesRepository()
inMemoryInstructorsRepository = new InMemoryInstructorRepository()

inMemoryModulesRepository = new InMemoryModulesRepository(inMemoryClassesRepository)
inMemoryClassesRepository = new InMemoryClassesRepository(inMemoryModulesRepository)
inMemoryCoursesRepository = new InMemoryCoursesRepository(inMemoryModulesRepository, inMemoryInstructorsRepository)
inMemoryInstructorsRepository = new InMemoryInstructorRepository(inMemoryCoursesRepository)

sut = new GetInstructorWithCoursesUseCase(inMemoryInstructorsRepository)
sut = new GetInstructorWithCoursesUseCase(inMemoryCoursesRepository)
})

it('should be able to get instructor info with their courses', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ 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 InstructorWithCoursesDTO } from '../../enterprise/entities/dtos/instructor-with-courses'
import { type InstructorsRepository } from '../repositories/instructors-repository'
import { type CoursesRepository } from '../repositories/courses-repository'

interface GetInstructorWithCoursesUseCaseRequest {
instructorId: string
Expand All @@ -17,13 +17,13 @@ ResourceNotFoundError,

export class GetInstructorWithCoursesUseCase implements UseCase<GetInstructorWithCoursesUseCaseRequest, GetInstructorWithCoursesUseCaseResponse> {
constructor(
private readonly instructorsRepository: InstructorsRepository
private readonly coursesRepository: CoursesRepository
) { }

async exec({
instructorId
}: GetInstructorWithCoursesUseCaseRequest): Promise<GetInstructorWithCoursesUseCaseResponse> {
const instructorWithCourses = await this.instructorsRepository.findInstructorWithCoursesById(instructorId)
const instructorWithCourses = await this.coursesRepository.findInstructorWithCoursesByInstructorId(instructorId)

if (!instructorWithCourses) {
return left(new ResourceNotFoundError())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,24 @@ import { InMemoryUsersRepository } from '../../../../../test/repositories/in-mem
import { InMemoryModulesRepository } from './../../../../../test/repositories/in-memory-modules-repository'
import { GetUserInfoUseCase } from './get-user-info'

let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryClassesRepository: InMemoryClassesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let inMemoryInstructorsRepository: InMemoryInstructorRepository
let inMemoryStudentsRepository: InMemoryStudentsRepository
let inMemoryModulesRepository: InMemoryModulesRepository
let inMemoryCoursesRepository: InMemoryCoursesRepository
let inMemoryUsersRepository: InMemoryUsersRepository
let sut: GetUserInfoUseCase

describe('Get user info use case', async () => {
beforeEach(() => {
inMemoryClassesRepository = new InMemoryClassesRepository()
inMemoryInstructorsRepository = new InMemoryInstructorRepository()
inMemoryStudentsRepository = new InMemoryStudentsRepository()

inMemoryModulesRepository = new InMemoryModulesRepository(inMemoryClassesRepository)
inMemoryClassesRepository = new InMemoryClassesRepository(inMemoryModulesRepository)
inMemoryCoursesRepository = new InMemoryCoursesRepository(inMemoryModulesRepository, inMemoryInstructorsRepository)
inMemoryInstructorsRepository = new InMemoryInstructorRepository(inMemoryCoursesRepository)
inMemoryStudentsRepository = new InMemoryStudentsRepository()
inMemoryUsersRepository = new InMemoryUsersRepository(inMemoryInstructorsRepository, inMemoryStudentsRepository)

sut = new GetUserInfoUseCase(inMemoryUsersRepository)
})

Expand Down
Loading

0 comments on commit 728cf59

Please sign in to comment.