Skip to content

Commit

Permalink
Merge pull request #21 from bmviniciuss/feature/issue-20
Browse files Browse the repository at this point in the history
feature(issue-20): update post
  • Loading branch information
bmviniciuss authored Jan 28, 2022
2 parents 8789878 + 379f285 commit a94736a
Show file tree
Hide file tree
Showing 19 changed files with 561 additions and 14 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "postpress",
"version": "0.3.0",
"version": "0.4.0",
"description": "1. [Database](./docs/database.md) 2. Routes 1. [User](./docs/user-routes.md)",
"main": "index.js",
"directories": {
Expand Down
5 changes: 5 additions & 0 deletions src/modules/post/models/mapper/PresentationUpdatedPost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface PresentationUpdatedPost {
title: string
content: string
userId: string
}
6 changes: 6 additions & 0 deletions src/modules/post/repos/PostRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ export interface PostRepositoryCreateDTO {
userId: string
}

export interface PostRepositoryUpdateDTO {
title: string
content: string
}

export interface PostRepository {
create(data: PostRepositoryCreateDTO): Promise<PostWithUser>
getAll(): Promise<PostWithUser[]>
loadById(id: string): Promise<PostWithUser | null>
update(postId: string, dataToUpdate: PostRepositoryUpdateDTO): Promise<PostWithUser>
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { PrismaClient } from '@prisma/client'
import faker from 'faker'
import { omit } from 'lodash'

import postFactory from '../../../../../tests/factories/postFactory'
import userFactory from '../../../../../tests/factories/userFactory'
import { PostRepository, PostRepositoryCreateDTO } from '../PostRepository'
import { PostRepository, PostRepositoryCreateDTO, PostRepositoryUpdateDTO } from '../PostRepository'
import { PrismaPostRepository } from './PrismaPostRepository'

describe('PrismaPostRepository', () => {
Expand Down Expand Up @@ -92,4 +93,32 @@ describe('PrismaPostRepository', () => {
expect(post!.user.id).toEqual(user.id)
})
})

describe('update', () => {
it('should return updated post', async () => {
const user = await prisma.user.create({
data: userFactory(1)
})
const createdPost = await prisma.post.create({
data: {
...omit(postFactory(1), 'userId'),
user: {
connect: {
id: user.id
}
}
}
})

const payload: PostRepositoryUpdateDTO = {
title: faker.random.words(),
content: faker.random.words()
}
const updatedPost = await sut.update(createdPost.id, payload)
expect(updatedPost!.id).toBeDefined()
expect(updatedPost.title).toEqual(payload.title)
expect(updatedPost.content).toEqual(payload.content)
expect(updatedPost!.user.id).toEqual(user.id)
})
})
})
12 changes: 11 additions & 1 deletion src/modules/post/repos/implementations/PrismaPostRepository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PrismaClient } from '@prisma/client'

import { PostRepository, PostRepositoryCreateDTO, PostWithUser } from '../PostRepository'
import { PostRepository, PostRepositoryCreateDTO, PostRepositoryUpdateDTO, PostWithUser } from '../PostRepository'

export class PrismaPostRepository implements PostRepository {
constructor (private readonly prisma: PrismaClient) {}
Expand Down Expand Up @@ -34,4 +34,14 @@ export class PrismaPostRepository implements PostRepository {
include: { user: true }
})
}

update (postId: string, dataToUpdate: PostRepositoryUpdateDTO): Promise<PostWithUser> {
return this.prisma.post.update({
where: { id: postId },
data: dataToUpdate,
include: {
user: true
}
})
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export namespace GetPostErrors {
export namespace PostErrors {
export class PostNotFound extends Error {
constructor () {
super('Post não existe')
this.name = 'GetPostErrors.PostNotFound'
this.name = 'PostErrors.PostNotFound'
}
}
}
4 changes: 2 additions & 2 deletions src/modules/post/useCases/getPost/GetPostController.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Controller } from '../../../../shared/infra/http/Controller'
import { HttpRequest, HttpResponse, notFound, ok, serverError } from '../../../../shared/infra/http/http'
import { PostErrors } from '../../shared/PostErrors'
import { GetPost } from './GetPost'
import { GetPostErrors } from './GetPostErrors'

export type GetPostControllerRequest = HttpRequest<any, {
postId: string
Expand All @@ -21,7 +21,7 @@ export class GetPostController extends Controller {
} catch (error) {
if (error instanceof Error) {
switch (error.constructor) {
case GetPostErrors.PostNotFound:
case PostErrors.PostNotFound:
return notFound(error)
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/modules/post/useCases/getPost/GetPostUseCase.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { PostMapper } from '../../models/mapper/PostMapper'
import { PostRepository } from '../../repos/PostRepository'
import { PostErrors } from '../../shared/PostErrors'
import { GetPost, GetPostInputDTO, GetPostReponseDTO } from './GetPost'
import { GetPostErrors } from './GetPostErrors'

export class GetPostUseCase implements GetPost {
constructor (private readonly postRepository: PostRepository) {}

async execute (data: GetPostInputDTO): Promise<GetPostReponseDTO> {
const post = await this.postRepository.loadById(data.postId)
if (!post) throw new GetPostErrors.PostNotFound()
if (!post) throw new PostErrors.PostNotFound()

const presentationPost = PostMapper.toPresentation(post)
return { post: presentationPost }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { mock } from 'jest-mock-extended'

import presentationPostFactory from '../../../../../../tests/factories/presentationPostFactory'
import { notFound, ok, serverError } from '../../../../../shared/infra/http/http'
import { PostErrors } from '../../../shared/PostErrors'
import { GetPost } from '../GetPost'
import { GetPostController, GetPostControllerRequest } from '../GetPostController'
import { GetPostErrors } from '../GetPostErrors'

const makeSut = () => {
const getPostMock = mock<GetPost>()
Expand Down Expand Up @@ -44,11 +44,11 @@ describe('GetPostController', () => {
})
})

it('should return notFound if GetUsers throws a GetPostErrors.PostNotFound errors', async () => {
it('should return notFound if GetUsers throws a PostErrors.PostNotFound errors', async () => {
const { sut, getPostMock } = makeSut()
getPostMock.execute.mockRejectedValueOnce(new GetPostErrors.PostNotFound())
getPostMock.execute.mockRejectedValueOnce(new PostErrors.PostNotFound())
const result = await sut.execute(request)
expect(result).toEqual(notFound(new GetPostErrors.PostNotFound()))
expect(result).toEqual(notFound(new PostErrors.PostNotFound()))
})

it('should return serverError if GetUsers throws a unkown error', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { mock } from 'jest-mock-extended'

import postWithUserFactory from '../../../../../../tests/factories/postWithUserFactory'
import { PostRepository } from '../../../repos/PostRepository'
import { PostErrors } from '../../../shared/PostErrors'
import { GetPostInputDTO } from '../GetPost'
import { GetPostUseCase } from '../GetPostUseCase'

Expand Down Expand Up @@ -43,7 +44,7 @@ describe('GetPostUseCase', () => {
const { sut, postRepositoryMock } = makeSut()
postRepositoryMock.loadById.mockResolvedValueOnce(null)
const promise = sut.execute(input)
expect(promise).rejects.toThrow()
expect(promise).rejects.toThrow(new PostErrors.PostNotFound())
})

it('should return post on success', async () => {
Expand Down
14 changes: 14 additions & 0 deletions src/modules/post/useCases/updatePost/UpdatePost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { User } from '@prisma/client'

import { PresentationPost } from '../../models/PresentationPost'

export interface UpdatePostInputDTO {
postId: string
title: string
content: string
userPerformingOperation: User
}

export interface UpdatePost {
execute(data: UpdatePostInputDTO): Promise<PresentationPost>
}
58 changes: 58 additions & 0 deletions src/modules/post/useCases/updatePost/UpdatePostController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Controller } from '../../../../shared/infra/http/Controller'
import { badRequest, HttpAuthenticatedRequest, HttpResponse, notFound, ok, serverError } from '../../../../shared/infra/http/http'
import { Validator } from '../../../../validation/Validator'
import { PresentationUpdatedPost } from '../../models/mapper/PresentationUpdatedPost'
import { PostErrors } from '../../shared/PostErrors'
import { UpdatePost, UpdatePostInputDTO } from './UpdatePost'
import { UpdatePostError } from './UpdatePostErrors'

export type UpdatePostControllerRequest = HttpAuthenticatedRequest<{
title: string
content: string
}, {
postId: string
}>

export class UpdatePostController extends Controller {
constructor (
private readonly validator: Validator,
private readonly updatePost: UpdatePost
) {
super()
}

async execute (request: UpdatePostControllerRequest): Promise<HttpResponse<any>> {
try {
const validationError = this.validator.validate(request.body)
if (validationError) return badRequest(validationError)

const updatePostPayload: UpdatePostInputDTO = {
title: request.body!.title,
content: request.body!.content,
postId: request.params!.postId,
userPerformingOperation: request.authenticatedUser
}

const updatedPost = await this.updatePost.execute(updatePostPayload)

const presenationUpdatedPost:PresentationUpdatedPost = {
title: updatedPost.title,
content: updatedPost.content,
userId: updatedPost.user.id
}

return ok(presenationUpdatedPost)
} catch (error) {
if (error instanceof Error) {
switch (error.constructor) {
case UpdatePostError.UnauthorizedToUpdatePostError:
return badRequest(error)
case PostErrors.PostNotFound:
return notFound(error)
}
}

return serverError(error)
}
}
}
8 changes: 8 additions & 0 deletions src/modules/post/useCases/updatePost/UpdatePostErrors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export namespace UpdatePostError {
export class UnauthorizedToUpdatePostError extends Error {
constructor () {
super('Usuário não autorizado')
this.name = 'UpdatePostError.UnauthorizedToUpdatePostError'
}
}
}
27 changes: 27 additions & 0 deletions src/modules/post/useCases/updatePost/UpdatePostUseCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { PresentationPost } from '../../models/PresentationPost'
import { PostRepository } from '../../repos/PostRepository'
import { PostErrors } from '../../shared/PostErrors'
import { UpdatePost, UpdatePostInputDTO } from './UpdatePost'
import { UpdatePostError } from './UpdatePostErrors'

export class UpdatePostUseCase implements UpdatePost {
constructor (
private readonly postRepository: PostRepository
) {}

async execute (data: UpdatePostInputDTO): Promise<PresentationPost> {
const post = await this.postRepository.loadById(data.postId)
if (!post) throw new PostErrors.PostNotFound()

if (post.user.id !== data.userPerformingOperation.id) {
throw new UpdatePostError.UnauthorizedToUpdatePostError()
}

return await this.postRepository.update(
data.postId,
{
title: data.title,
content: data.content
})
}
}
Loading

0 comments on commit a94736a

Please sign in to comment.