From 2fcf61e675ced69ec0ab6ea8710c890b772f9030 Mon Sep 17 00:00:00 2001 From: Matheus Sanchez Date: Sat, 3 Feb 2024 12:48:23 -0300 Subject: [PATCH 1/2] Fixing prisma types --- .../prisma/prisma-project-repository.ts | 52 ++++++++++++------- .../prisma-project-with-user-data-type.ts | 13 +++++ src/repositories/project-repository.ts | 5 +- 3 files changed, 50 insertions(+), 20 deletions(-) create mode 100644 src/repositories/prisma/prisma-project-with-user-data-type.ts diff --git a/src/repositories/prisma/prisma-project-repository.ts b/src/repositories/prisma/prisma-project-repository.ts index 24fc4c8..e8fcb9f 100644 --- a/src/repositories/prisma/prisma-project-repository.ts +++ b/src/repositories/prisma/prisma-project-repository.ts @@ -1,6 +1,7 @@ import { Prisma, Project } from '@prisma/client' import { ProjectRepository } from '../project-repository' import { prisma } from '../../lib/prisma' +import { ProjectWithUserData } from './prisma-project-with-user-data-type' export class PrismaProjectRepository implements ProjectRepository { async create(data: Prisma.ProjectUncheckedCreateInput): Promise { @@ -21,17 +22,6 @@ export class PrismaProjectRepository implements ProjectRepository { return projects } - async fetchProjectById(projectId: string): Promise { - const project = await prisma.project.findUnique({ - where: { - id: projectId, - }, - }) - - return project - } - - async addPhotoUrl(projectId: string, photoUrl: string): Promise { const project = await prisma.project.update({ where: { @@ -45,27 +35,53 @@ export class PrismaProjectRepository implements ProjectRepository { return project } - async fetchProjectByTags(tags: string[]): Promise { + async fetchProjectByTags(tags: string[]): Promise { const project = await prisma.project.findMany({ where: { tags: { hasEvery: tags }, - + }, + include: { + user: { + select: { + avatar_url: true, + name: true, + surname: true, + }, + }, }, }) return project } - async deleteProjectByID(projectId: string):Promise { - await prisma.project.delete({ + async fetchProjectById( + projectId: string, + ): Promise { + const project = await prisma.project.findUnique({ where: { - id: projectId - } + id: projectId, + }, + include: { + user: { + select: { + avatar_url: true, + name: true, + surname: true, + }, + }, + }, }) - return + return project } + async deleteProjectByID(projectId: string): Promise { + await prisma.project.delete({ + where: { + id: projectId, + }, + }) + } async edit(data: Prisma.ProjectUncheckedCreateInput): Promise { const project = await prisma.project.update({ diff --git a/src/repositories/prisma/prisma-project-with-user-data-type.ts b/src/repositories/prisma/prisma-project-with-user-data-type.ts new file mode 100644 index 0000000..6e5f295 --- /dev/null +++ b/src/repositories/prisma/prisma-project-with-user-data-type.ts @@ -0,0 +1,13 @@ +import { Prisma } from '@prisma/client' + +const userAvatarUrlAndNameData = Prisma.validator()({ + select: { avatar_url: true, name: true, surname: true }, +}) + +const projectsWithUserData = Prisma.validator()({ + include: { user: userAvatarUrlAndNameData }, +}) + +export type ProjectWithUserData = Prisma.ProjectGetPayload< + typeof projectsWithUserData +> diff --git a/src/repositories/project-repository.ts b/src/repositories/project-repository.ts index 6796689..5f6ac90 100644 --- a/src/repositories/project-repository.ts +++ b/src/repositories/project-repository.ts @@ -1,11 +1,12 @@ import { Prisma, Project } from '@prisma/client' +import { ProjectWithUserData } from './prisma/prisma-project-with-user-data-type' export interface ProjectRepository { create(data: Prisma.ProjectUncheckedCreateInput): Promise fetchProjectsByUserId(userId: string): Promise - fetchProjectById(projectId: string): Promise + fetchProjectById(projectId: string): Promise addPhotoUrl(projectId: string, photoUrl: string): Promise - fetchProjectByTags(tags: string[]): Promise + fetchProjectByTags(tags: string[]): Promise edit(data: Prisma.ProjectUncheckedCreateInput): Promise deleteProjectByID(projectID: string): Promise } From 09f521b39beff48288684c3c2e3dddcecc5c4540 Mon Sep 17 00:00:00 2001 From: Matheus Sanchez Date: Sat, 3 Feb 2024 14:20:59 -0300 Subject: [PATCH 2/2] Checking if the user data is coming. --- src/controller/project/getProjectById.spec.ts | 10 +- .../project/getProjectsByTags.spec.ts | 18 +++- .../in-memory-db/inMemoryProjectRepository.ts | 72 ++++++++++++-- .../in-memory-db/inMemoryUserRepository.ts | 33 +------ .../project/editProjectUseCase.spec.ts | 15 ++- .../project/getProjectsByIdUseCase.spec.ts | 12 ++- .../project/getProjectsByIdUseCase.ts | 5 +- .../project/getProjetsByTagsUseCase.spec.ts | 97 ++++++++++++++----- .../project/getProjetsByTagsUseCase.ts | 6 +- 9 files changed, 185 insertions(+), 83 deletions(-) diff --git a/src/controller/project/getProjectById.spec.ts b/src/controller/project/getProjectById.spec.ts index 1191e05..e00a447 100644 --- a/src/controller/project/getProjectById.spec.ts +++ b/src/controller/project/getProjectById.spec.ts @@ -50,11 +50,11 @@ describe('Get Projets By ID E2E', () => { expect(getProjectByIdResponse.statusCode).toEqual(200) expect(getProjectByIdResponse.body.project).toEqual( - expect.objectContaining({ title }), - ) - - expect(getProjectByIdResponse.body.project).toEqual( - expect.objectContaining({ tags }), + expect.objectContaining({ + title, + user: { name: 'John', surname: 'Doe', avatar_url: null }, + tags, + }), ) }) diff --git a/src/controller/project/getProjectsByTags.spec.ts b/src/controller/project/getProjectsByTags.spec.ts index 93fc851..1565eaf 100644 --- a/src/controller/project/getProjectsByTags.spec.ts +++ b/src/controller/project/getProjectsByTags.spec.ts @@ -60,13 +60,22 @@ describe('Get Projets By Tags E2E', () => { .post(`/projects/tags`) .send({ tags }) + console.log('getProjectsByTagsResponse') + console.log(getProjectsByTagsResponse.body.projects[0]) + expect(getProjectsByTagsResponse.statusCode).toEqual(200) expect(getProjectsByTagsResponse.body.projects).toHaveLength(2) expect(getProjectsByTagsResponse.body.projects[0]).toEqual( - expect.objectContaining({ title: 'Project 01' }), + expect.objectContaining({ + title: 'Project 01', + user: { name: 'John', surname: 'Doe', avatar_url: null }, + }), ) expect(getProjectsByTagsResponse.body.projects[1]).toEqual( - expect.objectContaining({ title: 'Project 02' }), + expect.objectContaining({ + title: 'Project 02', + user: { name: 'John', surname: 'Doe', avatar_url: null }, + }), ) }) @@ -94,7 +103,10 @@ describe('Get Projets By Tags E2E', () => { expect(getProjectsByTagsResponse.statusCode).toEqual(200) expect(getProjectsByTagsResponse.body.projects).toHaveLength(1) expect(getProjectsByTagsResponse.body.projects[0]).toEqual( - expect.objectContaining({ title: 'Project 03' }), + expect.objectContaining({ + title: 'Project 03', + user: { name: 'John', surname: 'Doe', avatar_url: null }, + }), ) }) }) diff --git a/src/repositories/in-memory-db/inMemoryProjectRepository.ts b/src/repositories/in-memory-db/inMemoryProjectRepository.ts index aeda533..8a18573 100644 --- a/src/repositories/in-memory-db/inMemoryProjectRepository.ts +++ b/src/repositories/in-memory-db/inMemoryProjectRepository.ts @@ -2,9 +2,12 @@ import { randomUUID } from 'crypto' import { Prisma, Project } from '@prisma/client' import { ProjectRepository } from '../project-repository' +import { ProjectWithUserData } from '../prisma/prisma-project-with-user-data-type' +import { InMemoryUserRepository } from './inMemoryUserRepository' export class InMemoryProjectRepository implements ProjectRepository { public dbProject: Project[] = [] + public dbUser: InMemoryUserRepository = new InMemoryUserRepository() constructor() {} @@ -18,6 +21,7 @@ export class InMemoryProjectRepository implements ProjectRepository { user_id: data.user_id, created_at: new Date(), updated_at: new Date(), + photo_url: null, } this.dbProject.push(project) @@ -43,34 +47,82 @@ export class InMemoryProjectRepository implements ProjectRepository { return projects } - async fetchProjectById(projectId: string): Promise { + async fetchProjectById( + projectId: string, + ): Promise { const project = this.dbProject.find((project) => project.id === projectId) + if (!project) { return null } - return project - } + const { + created_at, + description, + id, + link, + photo_url, + tags, + title, + updated_at, + user_id, + } = project + const foundUser = this.dbUser.db.find((user) => user.id === project.user_id) + + if (!foundUser) { + return null + } + + return { + created_at, + description, + id, + link, + photo_url, + tags, + title, + updated_at, + user_id, + user: { + name: foundUser?.name, + surname: foundUser?.surname, + avatar_url: foundUser?.avatar_url, + }, + } + } async addPhotoUrl(projectId: string, photoUrl: string): Promise { throw new Error('Method not implemented.') } async deleteProjectByID(projectId: string): Promise { - const index = this.dbProject.findIndex((project) => project.id === projectId); + const index = this.dbProject.findIndex( + (project) => project.id === projectId, + ) if (index !== -1) { - this.dbProject.splice(index, 1); + this.dbProject.splice(index, 1) } - - return } - - async fetchProjectByTags(tags: string[]): Promise { + + async fetchProjectByTags(tags: string[]): Promise { const projects = this.dbProject.filter((project) => project.tags.some((tag) => tags.includes(tag)), ) - return projects + const projectPromises = projects.map(async (project) => { + const user = await this.dbUser.findById(project.user_id) + + return { + ...project, + user: { + name: user.name, + surname: user.surname, + avatar_url: user.avatar_url, + }, + } + }) + + return Promise.all(projectPromises) } } diff --git a/src/repositories/in-memory-db/inMemoryUserRepository.ts b/src/repositories/in-memory-db/inMemoryUserRepository.ts index 4558073..b8c6ce9 100644 --- a/src/repositories/in-memory-db/inMemoryUserRepository.ts +++ b/src/repositories/in-memory-db/inMemoryUserRepository.ts @@ -3,33 +3,10 @@ import { UserRepository } from '../user-repository' import { randomUUID } from 'crypto' export class InMemoryUserRepository implements UserRepository { - private db: User[] = [] + public db: User[] = [] - - // This is a way to test our controllers without necessartralyy add the - // db repository; Once the program starts, one user is added to User[] and - // you can get http://localhost:3333/user/9600de4f-8d18-4e69-ba7a-ed7fa210618d - // to check the routes; + constructor() {} - // this constructor will be delete later; - constructor(){ - - const email = 'johndoe2@email.com' - const name = 'John' - const surname = 'Doe' - const password_hash = 'password_hash' - const id = '9600de4f-8d18-4e69-ba7a-ed7fa210618d' - - this.create({ - id, - name, - surname, - email, - password_hash, - }) - - } - async findByEmail(email: string): Promise { const User = this.db.find((User) => User.email === email) @@ -50,8 +27,6 @@ export class InMemoryUserRepository implements UserRepository { return User } - // create in a in-memory database is just used to help us on unit tests; - // that's why is not in our interface :) async create({ id, name, @@ -60,7 +35,7 @@ export class InMemoryUserRepository implements UserRepository { password_hash, }: Prisma.UserCreateInput) { const user: User = { - id: (id == undefined) ? randomUUID() : id, + id: id === undefined ? randomUUID() : id, name, surname, @@ -69,11 +44,11 @@ export class InMemoryUserRepository implements UserRepository { created_at: new Date(), updated_at: new Date(), + avatar_url: null, } this.db.push(user) return user } - } diff --git a/src/use-cases/project/editProjectUseCase.spec.ts b/src/use-cases/project/editProjectUseCase.spec.ts index 384b17f..f2181cd 100644 --- a/src/use-cases/project/editProjectUseCase.spec.ts +++ b/src/use-cases/project/editProjectUseCase.spec.ts @@ -1,17 +1,24 @@ import { expect, describe, it, beforeEach } from 'vitest' import { InMemoryProjectRepository } from '../../repositories/in-memory-db/inMemoryProjectRepository' -import { ProjectRepository } from '../../repositories/project-repository' import { ResourceNotFoundError } from '../errors/ResourceNotFoundError' import { EditProjectUseCase } from './editProjectUseCase' +import { User } from '@prisma/client' -let projectRepository: ProjectRepository +let projectRepository: InMemoryProjectRepository let editProjectUseCase: EditProjectUseCase +let newUser: User describe('Edit Project By Id Use Case', () => { - beforeEach(() => { + beforeEach(async () => { projectRepository = new InMemoryProjectRepository() editProjectUseCase = new EditProjectUseCase(projectRepository) + newUser = await projectRepository.dbUser.create({ + name: 'John', + surname: 'Doe', + email: 'johndoe@email.com', + password_hash: '123456', + }) }) it('should be able edit one project by ID', async () => { @@ -20,7 +27,7 @@ describe('Edit Project By Id Use Case', () => { description: 'Best Project', tags: ['react', 'node'], link: 'https://github.com/luiseduardo3/nodets-petcanil', - user_id: 'user_id', + user_id: newUser.id, }) const { project: projectEdited } = await editProjectUseCase.execute({ diff --git a/src/use-cases/project/getProjectsByIdUseCase.spec.ts b/src/use-cases/project/getProjectsByIdUseCase.spec.ts index f3eb8dc..2cdd923 100644 --- a/src/use-cases/project/getProjectsByIdUseCase.spec.ts +++ b/src/use-cases/project/getProjectsByIdUseCase.spec.ts @@ -1,11 +1,10 @@ import { expect, describe, it, beforeEach } from 'vitest' import { InMemoryProjectRepository } from '../../repositories/in-memory-db/inMemoryProjectRepository' -import { ProjectRepository } from '../../repositories/project-repository' import { ResourceNotFoundError } from '../errors/ResourceNotFoundError' import { GetProjectsByIdUseCase } from './getProjectsByIdUseCase' -let projectRepository: ProjectRepository +let projectRepository: InMemoryProjectRepository let getProjectByIdUseCase: GetProjectsByIdUseCase describe('Get Project By Id Use Case', () => { @@ -15,12 +14,18 @@ describe('Get Project By Id Use Case', () => { }) it('should be able get project by ID', async () => { + const newUser = await projectRepository.dbUser.create({ + name: 'John', + surname: 'Doe', + email: 'johndoe@email.com', + password_hash: '123456', + }) const newProject = await projectRepository.create({ title: 'React Typescript 1', description: 'Best Project', tags: ['react', 'node'], link: 'https://github.com/luiseduardo3/nodets-petcanil', - user_id: 'user_id', + user_id: newUser.id, }) const { project } = await getProjectByIdUseCase.execute({ @@ -32,6 +37,7 @@ describe('Get Project By Id Use Case', () => { title: 'React Typescript 1', id: newProject.id, tags: ['react', 'node'], + user: { name: 'John', surname: 'Doe', avatar_url: null }, }), ) }) diff --git a/src/use-cases/project/getProjectsByIdUseCase.ts b/src/use-cases/project/getProjectsByIdUseCase.ts index be6719d..7606819 100644 --- a/src/use-cases/project/getProjectsByIdUseCase.ts +++ b/src/use-cases/project/getProjectsByIdUseCase.ts @@ -1,15 +1,14 @@ -import { Project } from '@prisma/client' - import { ProjectRepository } from '../../repositories/project-repository' import { ResourceNotFoundError } from '../errors/ResourceNotFoundError' +import { ProjectWithUserData } from '../../repositories/prisma/prisma-project-with-user-data-type' interface GetProjectByIdRequest { projectId: string } interface GetProjectByIdResponse { - project: Project + project: ProjectWithUserData } export class GetProjectsByIdUseCase { diff --git a/src/use-cases/project/getProjetsByTagsUseCase.spec.ts b/src/use-cases/project/getProjetsByTagsUseCase.spec.ts index cd5447d..95396b6 100644 --- a/src/use-cases/project/getProjetsByTagsUseCase.spec.ts +++ b/src/use-cases/project/getProjetsByTagsUseCase.spec.ts @@ -2,16 +2,23 @@ import { expect, describe, it, beforeEach } from 'vitest' import { InMemoryProjectRepository } from '../../repositories/in-memory-db/inMemoryProjectRepository' -import { ProjectRepository } from '../../repositories/project-repository' import { GetProjectsByTagsUseCase } from './getProjetsByTagsUseCase' +import { User } from '@prisma/client' -let projectRepository: ProjectRepository +let projectRepository: InMemoryProjectRepository let getProjectsByTagsUseCase: GetProjectsByTagsUseCase +let newUser: User describe('Get Project By Tags', () => { - beforeEach(() => { + beforeEach(async () => { projectRepository = new InMemoryProjectRepository() getProjectsByTagsUseCase = new GetProjectsByTagsUseCase(projectRepository) + newUser = await projectRepository.dbUser.create({ + name: 'John', + surname: 'Doe', + email: 'johndoe@email.com', + password_hash: '123456', + }) }) it('should be able get projects that include a tag', async () => { @@ -20,7 +27,7 @@ describe('Get Project By Tags', () => { description: 'Best Project', tags: ['react', 'node'], link: 'https://github.com/luiseduardo3/nodets-petcanil', - user_id: 'user_id', + user_id: newUser.id, }) await projectRepository.create({ @@ -28,7 +35,7 @@ describe('Get Project By Tags', () => { description: 'Best Project 2', tags: ['react', 'node', 'typescript'], link: 'https://github.com/luiseduardo3/nodets-petcanil', - user_id: 'user_id', + user_id: newUser.id, }) const { projects } = await getProjectsByTagsUseCase.execute({ @@ -37,11 +44,15 @@ describe('Get Project By Tags', () => { expect(projects).toHaveLength(1) expect(projects[0]).toEqual( - expect.objectContaining({ title: 'React Typescript 2' }), - ) - - expect(projects[0]).toEqual( - expect.objectContaining({ tags: ['react', 'node', 'typescript'] }), + expect.objectContaining({ + title: 'React Typescript 2', + user: { + name: newUser.name, + surname: newUser.surname, + avatar_url: null, + }, + tags: ['react', 'node', 'typescript'], + }), ) }) @@ -51,7 +62,7 @@ describe('Get Project By Tags', () => { description: 'Best Project', tags: ['react', 'node'], link: 'https://github.com/luiseduardo3/nodets-petcanil', - user_id: 'user_id', + user_id: newUser.id, }) await projectRepository.create({ @@ -59,7 +70,7 @@ describe('Get Project By Tags', () => { description: 'Best Project 2', tags: ['react', 'node', 'typescript'], link: 'https://github.com/luiseduardo3/nodets-petcanil', - user_id: 'user_id', + user_id: newUser.id, }) await projectRepository.create({ @@ -67,7 +78,7 @@ describe('Get Project By Tags', () => { description: 'Best Project 2', tags: ['react', 'node'], link: 'https://github.com/luiseduardo3/nodets-petcanil', - user_id: 'user_id', + user_id: newUser.id, }) const { projects } = await getProjectsByTagsUseCase.execute({ @@ -76,15 +87,36 @@ describe('Get Project By Tags', () => { expect(projects).toHaveLength(3) expect(projects[0]).toEqual( - expect.objectContaining({ title: 'React Typescript 1' }), + expect.objectContaining({ + title: 'React Typescript 1', + user: { + name: newUser.name, + surname: newUser.surname, + avatar_url: null, + }, + }), ) expect(projects[1]).toEqual( - expect.objectContaining({ title: 'React Typescript 2' }), + expect.objectContaining({ + title: 'React Typescript 2', + user: { + name: newUser.name, + surname: newUser.surname, + avatar_url: null, + }, + }), ) expect(projects[2]).toEqual( - expect.objectContaining({ title: 'React Typescript 3' }), + expect.objectContaining({ + title: 'React Typescript 3', + user: { + name: newUser.name, + surname: newUser.surname, + avatar_url: null, + }, + }), ) }) @@ -94,7 +126,7 @@ describe('Get Project By Tags', () => { description: 'Best Project', tags: ['react', 'node'], link: 'https://github.com/luiseduardo3/nodets-petcanil', - user_id: 'user_id', + user_id: newUser.id, }) await projectRepository.create({ @@ -102,7 +134,7 @@ describe('Get Project By Tags', () => { description: 'Best Project 2', tags: ['react', 'node', 'typescript'], link: 'https://github.com/luiseduardo3/nodets-petcanil', - user_id: 'user_id', + user_id: newUser.id, }) await projectRepository.create({ @@ -110,7 +142,7 @@ describe('Get Project By Tags', () => { description: 'Best Project 2', tags: ['react', 'node'], link: 'https://github.com/luiseduardo3/nodets-petcanil', - user_id: 'user_id', + user_id: newUser.id, }) const { projects } = await getProjectsByTagsUseCase.execute({ @@ -119,15 +151,36 @@ describe('Get Project By Tags', () => { expect(projects).toHaveLength(3) expect(projects[0]).toEqual( - expect.objectContaining({ title: 'React Typescript 1' }), + expect.objectContaining({ + title: 'React Typescript 1', + user: { + name: newUser.name, + surname: newUser.surname, + avatar_url: null, + }, + }), ) expect(projects[1]).toEqual( - expect.objectContaining({ title: 'React Typescript 2' }), + expect.objectContaining({ + title: 'React Typescript 2', + user: { + name: newUser.name, + surname: newUser.surname, + avatar_url: null, + }, + }), ) expect(projects[2]).toEqual( - expect.objectContaining({ title: 'React Typescript 3' }), + expect.objectContaining({ + title: 'React Typescript 3', + user: { + name: newUser.name, + surname: newUser.surname, + avatar_url: null, + }, + }), ) }) diff --git a/src/use-cases/project/getProjetsByTagsUseCase.ts b/src/use-cases/project/getProjetsByTagsUseCase.ts index 85b4d7e..ff816b8 100644 --- a/src/use-cases/project/getProjetsByTagsUseCase.ts +++ b/src/use-cases/project/getProjetsByTagsUseCase.ts @@ -1,15 +1,13 @@ -import { Project } from '@prisma/client' - import { ProjectRepository } from '../../repositories/project-repository' -import { ResourceNotFoundError } from '../errors/ResourceNotFoundError' +import { ProjectWithUserData } from '../../repositories/prisma/prisma-project-with-user-data-type' interface GetProjectsByTagsRequest { projectTags: string[] } interface GetProjectsByTagsResponse { - projects: Project[] + projects: ProjectWithUserData[] } export class GetProjectsByTagsUseCase {