Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove neverthrow #6

Merged
merged 1 commit into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
"amazon-buddy": "^2.2.45",
"date-fns": "^3.6.0",
"framer-motion": "^11.1.7",
"neverthrow": "^6.2.1",
"next": "14.2.3",
"next-auth": "5.0.0-beta.9",
"next-themes": "^0.3.0",
Expand Down
7 changes: 0 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 9 additions & 20 deletions src/core/book/application/__tests__/create-book.use-case.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { describe, expect, it } from 'vitest'
import { ApplicationError } from '@/core/common/domain/errors/application-error'
import { container } from '@/lib/container'
import { prisma } from '@/lib/prisma/prisma'
import { unexpected } from '@/lib/utils/unexpected'
import { bookRequestExamples } from '@/tests/examples/books-request.examples'
import { createAvailableBook } from '@/tests/examples/factories'

Expand All @@ -13,21 +12,16 @@ describe('CreateBookUseCase', () => {
const command = bookRequestExamples.create()

// Act
const result = await container.createBook.with(command)
await container.createBook.with(command)

// Assert
result.match(
async () => {
const savedBook = await prisma.book.findFirst({
where: {
id: command.id,
},
})
expect(savedBook?.version).toEqual(0)
expect(savedBook?.state).toEqual('AVAILABLE')
const savedBook = await prisma.book.findFirst({
where: {
id: command.id,
},
(error) => unexpected.error(error),
)
})
expect(savedBook?.version).toEqual(0)
expect(savedBook?.state).toEqual('AVAILABLE')
})

it('should rejects to create a book with the same id', async () => {
Expand All @@ -39,14 +33,9 @@ describe('CreateBookUseCase', () => {
}

// Act
const result = await container.createBook.with(command)
const result = async () => await container.createBook.with(command)

// Assert
result.match(
(success) => unexpected.success(success),
(error) => {
expect(error).toBeInstanceOf(ApplicationError)
},
)
expect(result).rejects.toThrowError(ApplicationError)
})
})
30 changes: 9 additions & 21 deletions src/core/book/application/__tests__/edit-book.use-case.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { describe, expect, it } from 'vitest'

import { EditBookRequest } from '@/core/book/dto/requests/edit-book.request'
import { NotFoundError } from '@/core/common/domain/errors/application/not-found-error'
import { container } from '@/lib/container'
import { prisma } from '@/lib/prisma/prisma'
import { unexpected } from '@/lib/utils/unexpected'
import { BooksExamples } from '@/tests/examples/books.examples'
import { createAvailableBook } from '@/tests/examples/factories'

Expand All @@ -20,21 +18,16 @@ describe('EditBookUseCase', () => {
})

// Act
const result = await container.editBook.with(command)
await container.editBook.with(command)

// Assert
result.match(
async () => {
const savedBook = await prisma.book.findFirst({
where: {
id: command.id,
},
})
expect(savedBook?.version).toEqual(1)
expect(savedBook?.title).toEqual(command.title)
const savedBook = await prisma.book.findFirst({
where: {
id: command.id,
},
(error) => unexpected.error(error),
)
})
expect(savedBook?.version).toEqual(1)
expect(savedBook?.title).toEqual(command.title)
})

it('should returns an error if book does not exists', async () => {
Expand All @@ -48,14 +41,9 @@ describe('EditBookUseCase', () => {
})

// Act
const result = await container.editBook.with(command)
const result = async () => await container.editBook.with(command)

// Assert
result.match(
(_ok) => unexpected.success(_ok),
(error) => {
expect(error).toBeInstanceOf(NotFoundError)
},
)
expect(result).rejects.toThrowError()
})
})
20 changes: 4 additions & 16 deletions src/core/book/application/__tests__/loan-book.use-case.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { describe, expect, it } from 'vitest'

import { LoanBookRequest } from '@/core/book/dto/requests/loan-book.request'
import { ApplicationError } from '@/core/common/domain/errors/application-error'
import { container } from '@/lib/container'
import { prisma } from '@/lib/prisma/prisma'
import { unexpected } from '@/lib/utils/unexpected'
import {
createAvailableBook,
createLoan,
Expand All @@ -23,15 +21,10 @@ describe('Loan book', () => {
})

// Act
const result = container.loanBook.with(request)
await container.loanBook.with(request)

// Assert
await result.match(
async () => {
expect(await prisma.loan.count()).toBe(1)
},
(error) => unexpected.error(error),
)
expect(await prisma.loan.count()).toBe(1)
})

it('should not loan an unavailable book to a user', async () => {
Expand All @@ -45,14 +38,9 @@ describe('Loan book', () => {
})

// Act
const result = container.loanBook.with(request)
const result = async () => container.loanBook.with(request)

// Assert
await result.match(
(_ok) => unexpected.success(_ok),
(_error) => {
expect(_error).instanceof(ApplicationError)
},
)
expect(result).rejects.toThrowError()
})
})
10 changes: 2 additions & 8 deletions src/core/book/application/__tests__/return-book.use-case.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { describe, expect, it } from 'vitest'
import { ReturnBookRequest } from '@/core/book/dto/requests/return-book.request'
import { container } from '@/lib/container'
import { prisma } from '@/lib/prisma/prisma'
import { unexpected } from '@/lib/utils/unexpected'
import {
createLoan,
createLoanedBook,
Expand All @@ -21,14 +20,9 @@ describe('Return book', () => {
})

// Act
const result = container.returnBook.with(request)
await container.returnBook.with(request)

// Assert
await result.match(
async () => {
expect(await prisma.loan.count()).toBe(0)
},
(error) => unexpected.error(error),
)
expect(await prisma.loan.count()).toBe(0)
})
})
6 changes: 3 additions & 3 deletions src/core/book/application/create-book.use-case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ export class CreateBookUseCase {
constructor(private readonly books: Books) {}

async with(command: CreateBookRequest) {
return BookFactory.create(command).asyncAndThen((book) =>
this.books.save(book),
)
const book = BookFactory.create(command)

return this.books.save(book)
}
}
48 changes: 21 additions & 27 deletions src/core/book/application/edit-book.use-case.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { okAsync, Result, ResultAsync } from 'neverthrow'

import { Book } from '@/core/book/domain/model/book.entity'
import { Books } from '@/core/book/domain/services/books.repository'
import { EditBookRequest } from '@/core/book/dto/requests/edit-book.request'
import { NotFoundError } from '@/core/common/domain/errors/application/not-found-error'
import { ApplicationError } from '@/core/common/domain/errors/application-error'
import { DomainError } from '@/core/common/domain/errors/domain-error'
import { BookId } from '@/core/common/domain/value-objects/book-id'
import { FullNames } from '@/core/common/domain/value-objects/fullnames'
import { Image } from '@/core/common/domain/value-objects/image'
Expand All @@ -14,34 +9,33 @@ import { Title } from '@/core/common/domain/value-objects/title'
export class EditBookUseCase {
constructor(private readonly books: Books) {}

async with(command: EditBookRequest) {
return BookId.create(command.id)
.asyncAndThen((bookId) => this.findBook(bookId))
.andThen((book) => this.updateBook(book, command))
async with(command: EditBookRequest): Promise<void> {
const bookId = BookId.create(command.id)
const book = await this.findBook(bookId)

return this.updateBook(book, command)
}

private findBook(bookId: BookId): ResultAsync<Book, NotFoundError> {
return (
this.books.findAvailable(bookId) as ResultAsync<Book, NotFoundError>
).orElse(() => this.books.findLoaned(bookId))
private async findBook(bookId: BookId): Promise<Book> {
try {
return this.books.findAvailable(bookId)
} catch {
return this.books.findLoaned(bookId)
}
}

private updateBook(
private async updateBook(
book: Book,
command: EditBookRequest,
): ResultAsync<void, DomainError | ApplicationError> {
return Result.combine([
Title.create(command.title),
FullNames.create(command.authors),
Image.create(command.image),
])
.asyncAndThen(([title, authors, image]) => {
book.title = title
book.image = image
book.authors = authors
): Promise<void> {
const authors = FullNames.create(command.authors)
const image = Image.create(command.image)
const title = Title.create(command.title)

book.authors = authors
book.image = image
book.title = title

return okAsync(book)
})
.andThen((_book) => this.books.save(_book))
return this.books.save(book)
}
}
24 changes: 13 additions & 11 deletions src/core/book/application/loan-book-use.case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@ export class LoanBookUseCase {
private readonly loanBookService: LoanBookService,
) {}

with(command: LoanBookRequest) {
return this.findAvailableBook(command.bookId) //
.andThen((book) => this.loanBook(book, command.userId))
async with(command: LoanBookRequest) {
const book = await this.findAvailableBook(command.bookId)

return this.loanBook(book, command.userId)
}

private findAvailableBook(bookId: string) {
return BookId.create(bookId).asyncAndThen((_bookId) =>
this.books.findAvailable(_bookId),
)
private async findAvailableBook(bookId: string) {
const _bookId = BookId.create(bookId)

return this.books.findAvailable(_bookId)
}

private loanBook(book: AvailableBook, userId: string) {
return UserId.create(userId)
.asyncAndThen((_email) => book.loanTo(_email, this.loanBookService))
.andThen(() => this.books.save(book))
private async loanBook(book: AvailableBook, userId: string) {
const _userId = UserId.create(userId)
await book.loanTo(_userId, this.loanBookService)

return this.books.save(book)
}
}
26 changes: 11 additions & 15 deletions src/core/book/application/return-book.use-case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,20 @@ export class ReturnBookUseCase {
private readonly returnBookService: ReturnBookService,
) {}

with(command: ReturnBookRequest) {
return this.findLoanedBook(command.bookId) //
.andThen((book) => this.returnBook(book))
}
async with(command: ReturnBookRequest) {
const book = await this.findLoanedBook(command.bookId) //

private findLoanedBook(bookId: string) {
return BookId.create(bookId).asyncAndThen((_bookId) =>
this.books.findLoaned(_bookId),
)
return this.returnBook(book)
}

private returnBook(book: LoanedBook) {
return book
.doAvailable(this.returnBookService)
.andThen(() => this.books.save(book))
private async findLoanedBook(bookId: string) {
const _bookId = BookId.create(bookId)

return this.books.findLoaned(_bookId)
}
}

export function add(...arguments_: number[]) {
return arguments_.reduce((a, b) => a + b, 0)
private async returnBook(book: LoanedBook) {
await book.doAvailable(this.returnBookService)
return this.books.save(book)
}
}
12 changes: 3 additions & 9 deletions src/core/book/domain/model/available-book.entity.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import { ResultAsync } from 'neverthrow'

import { Book, BookState } from '@/core/book/domain/model/book.entity'
import { ApplicationError } from '@/core/common/domain/errors/application-error'
import { UserId } from '@/core/common/domain/value-objects/user-id'
import { ignore } from '@/core/common/utils/ignore'
import { LoanBookService } from '@/core/loan/domain/services/loan-book.service'

export class AvailableBook extends Book {
loanTo(
async loanTo(
userId: UserId,
loanBookService: LoanBookService,
): ResultAsync<void, ApplicationError> {
): Promise<void> {
this._state = BookState.LOANED

return loanBookService.with(this, userId).andThen(() => {
return ignore()
})
return loanBookService.with(this, userId)
}
}
Loading