Skip to content

Commit

Permalink
feat: add review ui logic
Browse files Browse the repository at this point in the history
  • Loading branch information
sgomez committed May 9, 2024
1 parent 65bcc64 commit a4d8813
Show file tree
Hide file tree
Showing 13 changed files with 245 additions and 1 deletion.
13 changes: 13 additions & 0 deletions src/core/common/domain/value-objects/description.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { DomainError } from '@/core/common/domain/errors/domain-error'

export class Description {
constructor(public readonly value: string) {}

public static create(author: string): Description {
if (!author || author.length < 3) {
throw DomainError.cause('La descripción es demasiado corta')
}

return new Description(author)
}
}
3 changes: 3 additions & 0 deletions src/core/common/domain/value-objects/review-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Id } from '@/core/common/domain/value-objects/id'

export class ReviewId extends Id {}
17 changes: 17 additions & 0 deletions src/core/common/domain/value-objects/score.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { DomainError } from '@/core/common/domain/errors/domain-error'

export class Score {
constructor(public readonly value: number) {}

public static create(value: number): Score {
if (!Number.isInteger(value)) {
throw DomainError.cause('La puntuación debe ser un entero')
}

if (value < 1 || value > 5) {
throw DomainError.cause('La puntuación debe ser un entero entre 1 y 5')
}

return new Score(value)
}
}
12 changes: 12 additions & 0 deletions src/core/review/application/create-review.use-case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ReviewFactory } from '@/core/review/domain/model/review.factory'
import { Reviews } from '@/core/review/domain/services/reviews.repository'
import { CreateReviewRequest } from '@/core/review/dto/requests/create-review.request'

export class CreateReviewUseCase {
constructor(private readonly reviews: Reviews) {}

async with(command: CreateReviewRequest) {
const review = ReviewFactory.create(command)
return this.reviews.save(review)
}
}
44 changes: 44 additions & 0 deletions src/core/review/domain/model/review.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { AggregateRoot } from '@/core/common/domain/model/aggregate-root'
import { BookId } from '@/core/common/domain/value-objects/book-id'
import { Description } from '@/core/common/domain/value-objects/description'
import { ReviewId } from '@/core/common/domain/value-objects/review-id'
import { Score } from '@/core/common/domain/value-objects/score'
import { Title } from '@/core/common/domain/value-objects/title'
import { UserId } from '@/core/common/domain/value-objects/user-id'

export class Review extends AggregateRoot {
constructor(
protected _id: ReviewId,
protected _bookId: BookId,
protected _userId: UserId,
protected _title: Title,
protected _description: Description,
protected _score: Score,
) {
super()
}

get id(): ReviewId {
return this._id
}

get bookId(): BookId {
return this._bookId
}

get userId(): UserId {
return this._userId
}

get title(): Title {
return this._title
}

get description(): Description {
return this._description
}

get score(): Score {
return this._score
}
}
21 changes: 21 additions & 0 deletions src/core/review/domain/model/review.factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { BookId } from '@/core/common/domain/value-objects/book-id'
import { Description } from '@/core/common/domain/value-objects/description'
import { ReviewId } from '@/core/common/domain/value-objects/review-id'
import { Score } from '@/core/common/domain/value-objects/score'
import { Title } from '@/core/common/domain/value-objects/title'
import { UserId } from '@/core/common/domain/value-objects/user-id'
import { Review } from '@/core/review/domain/model/review.entity'
import { CreateReviewRequest } from '@/core/review/dto/requests/create-review.request'

export const ReviewFactory = {
create: (reviewResponse: CreateReviewRequest): Review => {
const reviewId = ReviewId.create(reviewResponse.id)
const bookId = BookId.create(reviewResponse.bookId)
const userId = UserId.create(reviewResponse.userId)
const title = Title.create(reviewResponse.title)
const description = Description.create(reviewResponse.description)
const score = Score.create(reviewResponse.score)

return new Review(reviewId, bookId, userId, title, description, score)
},
}
5 changes: 5 additions & 0 deletions src/core/review/domain/services/reviews.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Review } from '@/core/review/domain/model/review.entity'

export interface Reviews {
save(review: Review): Promise<void>
}
14 changes: 14 additions & 0 deletions src/core/review/dto/requests/create-review.request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
type CreateReviewRequest = {
bookId: string
description: string
id: string
score: number
title: string
userId: string
}

const CreateReviewRequest = {
with: (properties: CreateReviewRequest): CreateReviewRequest => properties,
}

export { CreateReviewRequest }
44 changes: 43 additions & 1 deletion src/core/review/infrastructure/actions/create-review.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
'use server'

import { revalidateTag } from 'next/cache'
import { ulid } from 'ulid'
import { z } from 'zod'

import { CreateReviewRequest } from '@/core/review/dto/requests/create-review.request'
import { me } from '@/core/user/infrastructure/actions/me'
import { container } from '@/lib/container'
import { FormResponse } from '@/lib/zod/form-response'

export interface CreateReviewForm {
Expand All @@ -11,8 +17,17 @@ export interface CreateReviewForm {
title: string
}

const CreateReviewSchema = z.object({
bookId: z.string(),
description: z.string().min(3, 'El contenido es demasiado corto'),
id: z.string(),
score: z.string(),
title: z.string().min(3, 'El título es demasiado corto'),
})

export async function createReview(
previousState: FormResponse<CreateReviewForm>,
formData: FormData,
): Promise<FormResponse<CreateReviewForm>> {
const { id: userId } = (await me()) || {}

Expand All @@ -24,5 +39,32 @@ export async function createReview(
)
}

return FormResponse.custom(['general'], 'No implementado', previousState.data)
const id = ulid()
const result = CreateReviewSchema.safeParse(Object.fromEntries(formData))

console.debug('createReview', result)

if (!result.success) {
return FormResponse.withError(result.error, previousState.data)
}

await container.createReview.with(
CreateReviewRequest.with({
...result.data,
id,
score: +result.data.score,
userId,
}),
)

revalidateTag('books')
revalidateTag(`book-${previousState.data.bookId}`)

return FormResponse.success(
{
...result.data,
id,
},
'Gracias por tu reseña.',
)
}
14 changes: 14 additions & 0 deletions src/core/review/infrastructure/persistence/review.data-mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Review } from '@/core/review/domain/model/review.entity'

const ReviewDataMapper = {
toPrisma: (review: Review) => ({
bookId: review.bookId.value,
description: review.description.value,
id: review.id.value,
score: review.score.value,
title: review.title.value,
userId: review.userId.value,
}),
}

export { ReviewDataMapper }
38 changes: 38 additions & 0 deletions src/core/review/infrastructure/persistence/review.publisher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { PrismaClient } from '@prisma/client'

import { ApplicationError } from '@/core/common/domain/errors/application-error'
import { Publisher } from '@/core/common/domain/publisher/publisher'
import { Review } from '@/core/review/domain/model/review.entity'
import { ReviewDataMapper } from '@/core/review/infrastructure/persistence/review.data-mapper'

export class ReviewPublisher extends Publisher<Review> {
constructor(private readonly prisma: PrismaClient) {
super()
}

async create(review: Review): Promise<void> {
const data = ReviewDataMapper.toPrisma(review)

try {
await this.prisma.review.create({ data })
} catch (error) {
throw new ApplicationError((error as Error).toString())
}
}

protected async update(review: Review, version: number): Promise<void> {
const { id, ...data } = ReviewDataMapper.toPrisma(review)

try {
await this.prisma.review.update({
data,
where: {
id,
version,
},
})
} catch (error) {
throw new ApplicationError((error as Error).toString())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { PrismaClient } from '@prisma/client'

import { Review } from '@/core/review/domain/model/review.entity'
import { Reviews } from '@/core/review/domain/services/reviews.repository'
import { ReviewPublisher } from '@/core/review/infrastructure/persistence/review.publisher'

export class ReviewsPrisma implements Reviews {
private publisher: ReviewPublisher

constructor(private readonly prisma: PrismaClient) {
this.publisher = new ReviewPublisher(prisma)
}

async save(review: Review): Promise<void> {
return this.publisher.mergeObjectContext(review).commit()
}
}
4 changes: 4 additions & 0 deletions src/lib/container/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import { LoanBookService } from '@/core/loan/domain/services/loan-book.service'
import { ReturnBookService } from '@/core/loan/domain/services/return-book.service'
import { GetHistoricalLoansQuery } from '@/core/loan/infrastructure/queries/get-historical-loans.query'
import { LoansPrisma } from '@/core/loan/infrastructure/services/loans-prisma.repository'
import { CreateReviewUseCase } from '@/core/review/application/create-review.use-case'
import { GetReviewStatsQuery } from '@/core/review/infrastructure/queries/get-review-stats.query'
import { GetReviewsQuery } from '@/core/review/infrastructure/queries/get-reviews.query'
import { ReviewsPrisma } from '@/core/review/infrastructure/services/reviews-prisma.repository'
import { EnableUserUseCase } from '@/core/user/application/enable-user.use-case'
import { FindUserUseCase } from '@/core/user/application/find-user.use-case'
import { UpdateSettingUseCase } from '@/core/user/application/update-setting.use-case'
Expand All @@ -25,10 +27,12 @@ const Container = {
const books = new BooksPrisma(prisma)
const loans = new LoansPrisma(prisma)
const loanBookService = new LoanBookService(loans)
const reviews = new ReviewsPrisma(prisma)
const returnBookService = new ReturnBookService(loans)

return {
createBook: new CreateBookUseCase(books),
createReview: new CreateReviewUseCase(reviews),
editBook: new EditBookUseCase(books),
enableUser: new EnableUserUseCase(users),
findBook: new FindBookQuery(prisma),
Expand Down

0 comments on commit a4d8813

Please sign in to comment.