Skip to content

Commit 73a5fa1

Browse files
committed
docs(either-errors): base book rental application
1 parent 4e8a27d commit 73a5fa1

25 files changed

+379
-54
lines changed

articles/either-errors/package-lock.json

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

articles/either-errors/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,17 @@
2121
"test:e2e": "jest --config ./test/jest-e2e.json"
2222
},
2323
"dependencies": {
24+
"@nestjs-architects/typed-cqrs": "0.1.1",
2425
"@nestjs/common": "^8.0.0",
2526
"@nestjs/core": "^8.0.0",
27+
"@nestjs/cqrs": "8.0.0",
2628
"@nestjs/platform-express": "^8.0.0",
2729
"fp-ts": "2.11.1",
2830
"reflect-metadata": "^0.1.13",
2931
"rimraf": "^3.0.2",
30-
"rxjs": "^7.2.0"
32+
"rxjs": "^7.2.0",
33+
"utility-types": "3.10.0",
34+
"uuid": "8.3.2"
3135
},
3236
"devDependencies": {
3337
"@nestjs/cli": "^8.0.0",
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Controller, Get, Param, Post, Put } from '@nestjs/common';
2+
import { CommandBus, QueryBus } from '@nestjs/cqrs';
3+
import { GetAvailableBooks } from '../books-catalogue';
4+
import { BorrowBook, DeleteBook, NotAnAdmin, ReturnBook } from '../book-rental';
5+
import { BookSerializerService } from './book-serializer.service';
6+
import { Delete } from '@nestjs/common';
7+
8+
@Controller()
9+
export class AppController {
10+
constructor(
11+
private readonly queryBus: QueryBus,
12+
private readonly commandBus: CommandBus,
13+
private readonly serializer: BookSerializerService,
14+
) {}
15+
16+
@Get()
17+
books() {
18+
return this.queryBus.execute(new GetAvailableBooks());
19+
}
20+
21+
@Post(':isbn')
22+
async borrowBook(@Param('isbn') isbn: string) {
23+
const result = await this.commandBus.execute(new BorrowBook(isbn));
24+
return this.serializer.serialize(result);
25+
}
26+
27+
@Put(':isbn')
28+
async returnBook(@Param('isbn') isbn: string) {
29+
const result = await this.commandBus.execute(new ReturnBook(isbn));
30+
return this.serializer.serialize(result);
31+
}
32+
33+
@Delete(':isbn')
34+
async removeBook(@Param('isbn') isbn: string) {
35+
try {
36+
await this.commandBus.execute(new DeleteBook(isbn));
37+
return undefined;
38+
} catch (error) {
39+
if (error instanceof NotAnAdmin) {
40+
return {
41+
message: `How dare you? ${error}`,
42+
};
43+
} else {
44+
return {
45+
message: `Roaming?`,
46+
};
47+
}
48+
}
49+
}
50+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Either, isRight } from 'fp-ts/Either';
2+
import {
3+
ForbiddenException,
4+
HttpException,
5+
NotFoundException,
6+
} from '@nestjs/common';
7+
8+
enum BorrowErrorViaEnum {
9+
OutOfStock = 'out-of-stock',
10+
BooksLimitReached = 'books-limit-reached',
11+
}
12+
13+
enum ReturnErrorViaEnum {
14+
PaymentRequired = 'payment-required',
15+
}
16+
17+
export class BookSerializerWithEnumsService {
18+
serialize(
19+
result: Either<
20+
BorrowErrorViaEnum | ReturnErrorViaEnum /** | and others...*/,
21+
void
22+
>,
23+
) {
24+
if (isRight(result)) {
25+
return;
26+
}
27+
const error = result.left;
28+
29+
switch (error) {
30+
case ReturnErrorViaEnum.PaymentRequired:
31+
throw new HttpException('Pay debt first.', 402);
32+
case BorrowErrorViaEnum.OutOfStock:
33+
throw new NotFoundException();
34+
case BorrowErrorViaEnum.BooksLimitReached:
35+
throw new ForbiddenException();
36+
default:
37+
const _exhaustiveCheck: never = error;
38+
throw _exhaustiveCheck;
39+
}
40+
}
41+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import {
2+
Injectable,
3+
HttpException,
4+
NotFoundException,
5+
ForbiddenException,
6+
} from '@nestjs/common';
7+
import { Either, isRight } from 'fp-ts/Either';
8+
import {
9+
BorrowError,
10+
ReturnError,
11+
TimeLimitExceeded,
12+
BooksLimitReached,
13+
OutOfStock,
14+
} from '../book-rental';
15+
16+
type BookRentalError = ReturnError | BorrowError;
17+
18+
@Injectable()
19+
export class BookSerializerService {
20+
serialize(response: Either<BookRentalError, void>): void {
21+
if (isRight(response)) {
22+
return;
23+
}
24+
const error = response.left;
25+
26+
switch (error) {
27+
case TimeLimitExceeded:
28+
throw new HttpException('Pay debt first.', 402);
29+
case OutOfStock:
30+
throw new NotFoundException();
31+
case BooksLimitReached:
32+
throw new ForbiddenException();
33+
default:
34+
const _exhaustiveCheck: never = error;
35+
throw _exhaustiveCheck;
36+
}
37+
}
38+
}

articles/either-errors/src/app.controller.spec.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

articles/either-errors/src/app.controller.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
import { CqrsModule } from '@nestjs/cqrs';
12
import { Module } from '@nestjs/common';
2-
import { AppController } from './app.controller';
3+
import { AppController } from './api/app.controller';
34
import { AppService } from './app.service';
5+
import { BookCatalogueModule } from './books-catalogue';
6+
import { BookRentalModule } from './book-rental';
7+
import { BookSerializerService } from './api/book-serializer.service';
48

59
@Module({
6-
imports: [],
10+
imports: [CqrsModule, BookCatalogueModule, BookRentalModule],
711
controllers: [AppController],
8-
providers: [AppService],
12+
providers: [AppService, BookSerializerService],
913
})
1014
export class AppModule {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Module } from '@nestjs/common';
2+
import { CqrsModule } from '@nestjs/cqrs';
3+
4+
import { BorrowBookHandler } from './borrow-book.handler';
5+
import { ReturnBookHandler } from './return-book.handler';
6+
import { DeleteBookHandler } from './delete-book.handler';
7+
8+
@Module({
9+
imports: [CqrsModule],
10+
providers: [BorrowBookHandler, ReturnBookHandler, DeleteBookHandler],
11+
})
12+
export class BookRentalModule {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Command } from '@nestjs-architects/typed-cqrs';
2+
import { Either } from 'fp-ts/Either';
3+
import { BorrowError } from './borrow-errors';
4+
5+
export class BorrowBook extends Command<Either<BorrowError, void>> {
6+
constructor(public readonly isbn: string) {
7+
super();
8+
}
9+
}

0 commit comments

Comments
 (0)