Skip to content

Commit

Permalink
feat: adding services for books (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
nathan2slime authored Jul 14, 2024
1 parent dae0c4d commit eaa82dd
Show file tree
Hide file tree
Showing 18 changed files with 371 additions and 35 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ FROM base
COPY --from=prod-deps /app/node_modules /app/node_modules
COPY --from=build /app/dist /app/dist

CMD [ "pnpm", "start" ]
CMD [ "node", "dist/main" ]
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"@nestjs/schematics": "^10.0.0",
"@nestjs/swagger": "^7.4.0",
"@nestjs/testing": "^10.0.0",
"cookie-parser": "^1.4.6",
"dotenv": "^16.4.5",
"husky": "^9.0.11",
"@nestjs/cli": "^10.0.0",
"mongoose": "^8.5.0",
"passport-jwt": "^4.0.1",
"reflect-metadata": "^0.2.0",
Expand All @@ -49,12 +54,7 @@
"@typescript-eslint/eslint-plugin": "^7.16.0",
"@typescript-eslint/parser": "^7.16.0",
"commitizen": "^4.3.0",
"@nestjs/cli": "^10.0.0",
"lint-staged": "^15.2.7",
"@nestjs/schematics": "^10.0.0",
"husky": "^9.0.11",
"@nestjs/swagger": "^7.4.0",
"@nestjs/testing": "^10.0.0",
"@types/bcrypt": "^5.0.2",
"@types/cookie-parser": "^1.4.7",
"@types/express": "^4.17.17",
Expand Down
30 changes: 15 additions & 15 deletions pnpm-lock.yaml

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

2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { MongooseModule } from '@nestjs/mongoose';
import { AuthModule } from '~/app/auth/auth.module';
import { UserModule } from '~/app/user/user.module';
import { AuthorModule } from '~/app/author/author.module';
import { BookModule } from '~/app/book/book.module';

import { env } from '~/env';

Expand All @@ -13,6 +14,7 @@ import { env } from '~/env';
AuthModule,
AuthorModule,
UserModule,
BookModule,
],
controllers: [],
providers: [],
Expand Down
10 changes: 5 additions & 5 deletions src/app/author/author.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { Response } from 'express';

import {
CreateAuthorDto,
SearchAuthorDto,
QuerySearchDto,
UpdateAuthorDto,
} from '~/app/author/author.dto';
import { AuthorService } from '~/app/author/author.service';
Expand All @@ -43,20 +43,20 @@ export class AuthorController {
return res.status(HttpStatus.CREATED).json(data);
}

@Get(':id')
@Get('show/:id')
@ApiResponse({
status: 200,
description: 'Get a author',
})
async getById(@Param('id') id: string, @Res() res: Response) {
const data = await this.authorService.getById(id);

return res.status(HttpStatus.CREATED).json(data);
return res.status(HttpStatus.OK).json(data);
}

@Delete('delete/:id')
@ApiResponse({
status: 201,
status: 200,
description: 'Delete a author',
})
@UseGuards(JwtAuthGuard, RoleGuard)
Expand Down Expand Up @@ -89,7 +89,7 @@ export class AuthorController {
status: 200,
description: 'Author search',
})
async search(@Query() query: SearchAuthorDto, @Res() res: Response) {
async search(@Query() query: QuerySearchDto, @Res() res: Response) {
const data = await this.authorService.search(query);
return res.status(HttpStatus.OK).json(data);
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/author/author.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class CreateAuthorDto {
biography: string;
}

export class SearchAuthorDto {
export class QuerySearchDto {
@ApiProperty({ required: false })
@Transform(params => {
if (params.value) {
Expand Down
4 changes: 2 additions & 2 deletions src/app/author/author.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FilterQuery, Model } from 'mongoose';

import {
CreateAuthorDto,
SearchAuthorDto,
QuerySearchDto,
UpdateAuthorDto,
} from '~/app/author/author.dto';
import { Author } from '~/schemas/author.schema';
Expand Down Expand Up @@ -32,7 +32,7 @@ export class AuthorService {
return this.authorModel.findById(id);
}

async search(data: SearchAuthorDto) {
async search(data: QuerySearchDto) {
const query: FilterQuery<Author> = {};

if (data.query) {
Expand Down
71 changes: 70 additions & 1 deletion src/app/author/tests/author.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ import { getModelToken } from '@nestjs/mongoose';

import { Author } from '~/schemas/author.schema';
import { AuthorService } from '~/app/author/author.service';
import { CreateAuthorDto } from '~/app/author/author.dto';
import {
CreateAuthorDto,
QuerySearchDto,
UpdateAuthorDto,
} from '~/app/author/author.dto';
import { Entity } from '~/types';

import * as pagination from '~/utils/funcs/pagination';

describe('AuthorService', () => {
let authorModel: Model<Author>;
let authorService: AuthorService;
Expand Down Expand Up @@ -46,4 +52,67 @@ describe('AuthorService', () => {
expect(authorModel.create).toHaveBeenCalledWith(payload);
});
});

describe('delete', () => {
const authorId = expect.anything();

it('must delete an author by id', async () => {
jest
.spyOn(authorModel, 'findByIdAndDelete')
.mockResolvedValue(expect.anything());

await authorService.delete(authorId);

expect(authorModel.findByIdAndDelete).toHaveBeenCalledWith(authorId);
});
});

describe('search', () => {
const payload = {
items: expect.anything(),
total: expect.anything(),
pages: expect.anything(),
perPage: expect.anything(),
page: expect.anything(),
};
const args: QuerySearchDto = {
page: expect.anything(),
perPage: expect.anything(),
query: expect.anything(),
sortField: expect.anything(),
sortOrder: expect.anything(),
};

it('must search for an author', async () => {
jest.spyOn(pagination, 'paginate').mockResolvedValue(payload);

const res = await authorService.search(args);

expect(res).toBe(payload);
});
});

describe('update', () => {
const author = {} as Entity<Author>[];
const payload: UpdateAuthorDto = {
biography: expect.anything(),
name: expect.anything(),
birthDate: expect.anything(),
};

const authorId = expect.anything();

it('must update author and return it updated', async () => {
jest.spyOn(authorModel, 'findByIdAndUpdate').mockResolvedValue(author);

const data = await authorService.update(authorId, payload);

expect(data).toBe(author);
expect(authorModel.findByIdAndUpdate).toHaveBeenCalledWith(
authorId,
payload,
{ new: true },
);
});
});
});
94 changes: 94 additions & 0 deletions src/app/book/book.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
Body,
Controller,
Delete,
Get,
HttpStatus,
Param,
Post,
Put,
Query,
Res,
UseGuards,
} from '@nestjs/common';
import { ApiResponse, ApiTags } from '@nestjs/swagger';
import { Response } from 'express';

import { CreateBookDto, UpdateBookDto } from '~/app/book/book.dto';
import { BookService } from '~/app/book/book.service';
import { QuerySearchDto } from '~/app/author/author.dto';
import { JwtAuthGuard } from '~/app/auth/auth.guard';
import { RoleGuard } from '~/app/auth/role.guard';
import { Roles } from '~/app/auth/auth.decorator';
import { Role } from '~/types/role.enum';

@ApiTags('Book')
@Controller('book')
export class BookController {
constructor(private readonly bookService: BookService) {}

@Post('create')
@ApiResponse({
status: 201,
description: 'Create new book',
})
@UseGuards(JwtAuthGuard, RoleGuard)
@Roles([Role.ADMIN])
async create(@Body() body: CreateBookDto, @Res() res: Response) {
const data = await this.bookService.create(body);

return res.status(HttpStatus.CREATED).json(data);
}

@Get('show/:id')
@ApiResponse({
status: 200,
description: 'Get a book',
})
async getById(@Param('id') id: string, @Res() res: Response) {
const data = await this.bookService.getById(id);

return res.status(HttpStatus.OK).json(data);
}

@Delete('delete/:id')
@ApiResponse({
status: 200,
description: 'Delete a book',
})
@UseGuards(JwtAuthGuard, RoleGuard)
@Roles([Role.ADMIN])
async delete(@Param('id') id: string, @Res() res: Response) {
await this.bookService.delete(id);

return res.status(HttpStatus.OK).send();
}

@Put('update/:id')
@ApiResponse({
status: 201,
description: 'Update a book',
})
@UseGuards(JwtAuthGuard, RoleGuard)
@Roles([Role.ADMIN])
async update(
@Body() body: UpdateBookDto,
@Param('id') id: string,
@Res() res: Response,
) {
const data = await this.bookService.update(id, body);

return res.status(HttpStatus.OK).json(data);
}

@Get('search')
@ApiResponse({
status: 200,
description: 'Book search',
})
async search(@Query() query: QuerySearchDto, @Res() res: Response) {
const data = await this.bookService.search(query);

return res.status(HttpStatus.OK).json(data);
}
}
Loading

0 comments on commit eaa82dd

Please sign in to comment.