feat(products): implement CRUD operations and pagination

Added the implementation for Create, Read, Update, and Delete (CRUD) operations in the `ProductsService` and `ProductsController`. These include methods for adding, editing, deleting and listing all products with pagination. All these changes are reflected in the `ProductsModule` and related DTO file.
This commit is contained in:
Mathis H (Avnyr) 2024-07-15 11:41:18 +02:00
parent 1618bee00d
commit 007cee5951
Signed by: Mathis
GPG Key ID: DD9E0666A747D126
4 changed files with 183 additions and 9 deletions

View File

@ -1,12 +1,55 @@
import { Controller } from '@nestjs/common'; import {
BadRequestException,
Body,
Controller, Delete, Get,
HttpCode,
HttpStatus,
Param,
Patch,
Post, Query,
UseGuards
} from "@nestjs/common";
import { CreateProductDto, EditProductDto } from "src/products/products.dto";
import { AdminGuard } from "src/auth/auth.guard";
import { ProductsService } from "src/products/products.service";
@Controller('products') @Controller('products')
export class ProductsController { export class ProductsController {
//TODO add a new product (admin) constructor(
private readonly productsService: ProductsService
) {}
//TODO edit a product (admin) @HttpCode(HttpStatus.CREATED)
@UseGuards(AdminGuard)
//TODO delete a product (admin) @Post('new')
async newProduct(@Body() dto: CreateProductDto) {
const result = await this.productsService.add(dto)
if (result.count !== 1) {
throw new BadRequestException('Insertion failed');
}
return result.statement
}
//TODO list all product with pagination. //TODO list all product with pagination.
@HttpCode(HttpStatus.OK)
@Get("all")
async getAllProducts(@Query('page') page: number) {
return this.productsService.findAll(page || 0, 20);
}
//TODO edit a product (admin)
@HttpCode(HttpStatus.OK)
@UseGuards(AdminGuard)
@Patch(':id')
async editProduct(@Param('id') id: string, @Body() dto: EditProductDto) {
}
//TODO delete a product (admin)
@HttpCode(HttpStatus.ACCEPTED)
@UseGuards(AdminGuard)
@Delete(':id')
async deleteProduct(@Param('id') id: string) {
}
} }

View File

@ -0,0 +1,45 @@
import {
IsEmail, IsInt,
IsNotEmpty,
IsString,
IsStrongPassword,
MaxLength, Min,
MinLength
} from "class-validator";
import { optional } from "zod";
export class CreateProductDto {
@MinLength(1)
@MaxLength(32)
@IsNotEmpty()
@IsString()
slugName: string;
@MinLength(1)
@MaxLength(64)
@IsNotEmpty()
@IsString()
displayName: string;
@IsInt()
@Min(0)
price: number;
}
export class EditProductDto {
@MinLength(1)
@MaxLength(32)
@IsNotEmpty()
@IsString()
slugName?: string;
@MinLength(1)
@MaxLength(64)
@IsNotEmpty()
@IsString()
displayName?: string;
@IsInt()
@Min(0)
price?: number;
}

View File

@ -1,8 +1,10 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { ProductsController } from './products.controller'; import { ProductsController } from './products.controller';
import { ProductsService } from './products.service'; import { ProductsService } from './products.service';
import { DrizzleModule } from "src/drizzle/drizzle.module";
@Module({ @Module({
imports: [DrizzleModule],
controllers: [ProductsController], controllers: [ProductsController],
providers: [ProductsService] providers: [ProductsService]
}) })

View File

@ -1,13 +1,97 @@
import { Injectable } from '@nestjs/common'; import { Injectable, InternalServerErrorException } from "@nestjs/common";
import { DrizzleService } from "src/drizzle/drizzle.service";
import { ProductsTable } from "src/schema";
import { CreateProductDto, EditProductDto } from "src/products/products.dto";
import { countDistinct, eq } from "drizzle-orm";
@Injectable() @Injectable()
export class ProductsService { export class ProductsService {
//TODO add a new product (admin)
//TODO edit a product (admin) constructor(
private db: DrizzleService,
) {}
//TODO delete a product (admin) async add(data: CreateProductDto) {
try {
const res = await this.db.use()
.insert(ProductsTable)
.values({
slugName: data.slugName,
displayName: data.displayName,
price: String(data.price),
imagePath: "placeholder"
})
console.log(`Adding new product "${data.slugName}" ...\n`, res)
return res;
} catch (err) {
throw new InternalServerErrorException(err);
}
}
async edit(productId: string, data: EditProductDto) {
try {
const res = await this.db.use()
.update(ProductsTable)
.set({
slugName: data.slugName,
displayName: data.displayName,
price: String(data.price),
})
.where(eq(ProductsTable.uuid, productId))
.prepare("editProductById")
.execute();
console.log(`Editing product n°${productId} ...\n`, res)
return res;
} catch (err) {
throw new InternalServerErrorException(err);
}
}
async delete(productId: string) {
try {
const res = await this.db.use()
.delete(ProductsTable)
.where(eq(ProductsTable.uuid, productId))
.prepare("deleteProductById")
.execute();
console.log(`Deleting product n°${productId} ...\n`, res)
return res;
} catch (err) {
throw new InternalServerErrorException(err);
}
}
//TODO list all product with pagination. //TODO list all product with pagination.
async findAll(page: number, limit: number) {
try {
//get the number of row first
const count = await this.db.use()
.select({
total: countDistinct(ProductsTable.uuid)
})
.from(ProductsTable)
.prepare("countProducts")
.execute();
const res = await this.db.use()
.select()
.from(ProductsTable)
.limit(limit)
.offset((page - 1) * limit)
.prepare("findAllProducts")
.execute();
console.log(`Fetching products (page ${page}, limit ${limit}) ...\n`)
const response = {
total: count[0].total,
page: page,
productsPerPage: limit,
products: res
}
console.log(response)
return response;
} catch (err) {
throw new InternalServerErrorException(err);
}
}
} }