Skip to content

Commit

Permalink
feat: add mongoose id validation service and custom decorator
Browse files Browse the repository at this point in the history
  • Loading branch information
CondensedMilk7 committed Aug 22, 2023
1 parent 8281ab0 commit 7285507
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 36 deletions.
19 changes: 10 additions & 9 deletions src/modules/shop/cart/carts.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,14 +259,15 @@ export class CartsService {
if (product) {
const productStock = product.stock;
product.stock -= doc.quantity;
if (product.stock < 0) {
this.exceptionService.throwError(
ExceptionStatusKeys.Conflict,
`Product with this ${doc.productId} id, already sold some items, currently can't checkout becouse product have ${productStock} and user want's ${doc.quantity}`,
ProductExceptionKeys.ProductStockSoldBeforeCheckout,
);
return;
}
//if (product.stock < 0) {
//// TODO: fix this error breaking the app
//this.exceptionService.throwError(
//ExceptionStatusKeys.Conflict,
//`The quantity of product with ID '${doc.productId}' exceeds the stock (${productStock})`,
//ProductExceptionKeys.ProductStockSoldBeforeCheckout,
//);
//return;
//}
total += doc.quantity;
cacheOfProducts.push(product);
}
Expand All @@ -284,7 +285,7 @@ export class CartsService {
await cart.deleteOne();
return {
success: true,
message: `Stocks were updated, currently ${total} item were sold. Cart will be cleared, user have to create new cart with POST request`,
message: `Stocks were updated, currently ${total} items were sold. The cart will be cleared, user has to create a new cart with POST request`,
};
}
}
7 changes: 6 additions & 1 deletion src/modules/shop/dtos/cart.dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { IsString } from 'class-validator';
import { MongooseId } from 'src/shared/mongoose-id.decorator';

// TODO: add validation
export class CartDto {
id: string; // TODO: validation for mongooseId
@IsString()
@MongooseId()
id: string;
quantity: number;
}
6 changes: 5 additions & 1 deletion src/modules/shop/dtos/product-id.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// TODO: add validation
import { IsString } from 'class-validator';
import { MongooseId } from 'src/shared/mongoose-id.decorator';

export class ProductIdDto {
@IsString()
@MongooseId()
id: string;
}
8 changes: 7 additions & 1 deletion src/modules/shop/product/products.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,18 @@ import {
} from '../dtos';
import { CurrentUser, CurrentUserInterceptor, JwtGuard } from 'src/shared';
import { UserPayload } from 'src/interfaces';
import { MongooseValidatorService } from 'src/shared/mongoose-validator.service';

@Controller('shop/products')
export class ProductsController {
constructor(private readonly productsService: ProductsService) {}
constructor(
private readonly productsService: ProductsService,
private readonly mongooseValidator: MongooseValidatorService,
) {}

@Get('id/:id')
getProductById(@Param('id') id: string) {
this.mongooseValidator.isValidObjectId(id);
return this.productsService.getProductById(id);
}

Expand All @@ -35,6 +40,7 @@ export class ProductsController {
@Param('id') id: string,
@Body() updateProductDto: UpdateProductDto,
) {
this.mongooseValidator.isValidObjectId(id);
return this.productsService.updateProduct(id, updateProductDto);
}

Expand Down
25 changes: 2 additions & 23 deletions src/modules/shop/product/products.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import mongoose, { Model, SortOrder } from 'mongoose';
import { Model, SortOrder } from 'mongoose';

import {
CreateProductDto,
Expand All @@ -11,12 +11,7 @@ import {
} from '../dtos';
import { Product, ProductDocument } from 'src/schemas';
import { ExceptionService } from 'src/shared';
import {
ExceptionStatusKeys,
GlobalExceptionKeys,
SortDirection,
SortProductsBy,
} from 'src/enums';
import { ExceptionStatusKeys, SortDirection, SortProductsBy } from 'src/enums';
import { ProductCategory, UserPayload } from 'src/interfaces';
import { API_CONFIG } from 'src/consts';

Expand All @@ -36,14 +31,6 @@ export class ProductsService {
}

async getProductById(id: string): Promise<Product> {
// TODO: Implement pipe or way to check id | we have to use mongoose.isValidObjectId
if (!mongoose.isValidObjectId(id)) {
this.exceptionService.throwError(
ExceptionStatusKeys.BadRequest,
'id must provided from product',
GlobalExceptionKeys.IncorrectMongooseID,
);
}
const product = await this.productModel.findById(id);
if (!product) {
this.exceptionService.throwError(ExceptionStatusKeys.NotFound);
Expand All @@ -52,15 +39,7 @@ export class ProductsService {
return product;
}

// TODO: Implement middleware or way to check id | we have to use mongoose.isValidObjectId
async updateProduct(id: string, body: UpdateProductDto): Promise<Product> {
if (!mongoose.isValidObjectId(id)) {
this.exceptionService.throwError(
ExceptionStatusKeys.BadRequest,
'id must provided from product',
GlobalExceptionKeys.IncorrectMongooseID,
);
}
const product = await this.productModel.findOneAndUpdate({ _id: id }, body);
if (!product) {
this.exceptionService.throwError(ExceptionStatusKeys.NotFound);
Expand Down
8 changes: 7 additions & 1 deletion src/modules/shop/shop.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { ExceptionService } from 'src/shared';
import { ProductsController, ProductsService } from './product';
import { CartsController, CartsService } from './cart';
import { MongooseValidatorService } from 'src/shared/mongoose-validator.service';

@Module({
imports: [
Expand All @@ -26,7 +27,12 @@ import { CartsController, CartsService } from './cart';
signOptions: { expiresIn: `${process.env.JWT_EXPIRES_IN || '1'}h` },
}),
],
providers: [ExceptionService, ProductsService, CartsService],
providers: [
ExceptionService,
ProductsService,
CartsService,
MongooseValidatorService,
],
controllers: [ProductsController, CartsController],
})
export class ShopModule {}
34 changes: 34 additions & 0 deletions src/shared/mongoose-id.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { HttpException } from '@nestjs/common';
import { registerDecorator } from 'class-validator';
import { GlobalExceptionKeys } from 'src/enums';
import mongoose from 'mongoose';

export function MongooseId() {
return function (object: object, propertyName: string) {
registerDecorator({
name: 'mongooseId',
target: object.constructor,
propertyName: propertyName,
//constraints: [property],
//options: validationOptions,
validator: {
validate(value: string) {
if (mongoose.isValidObjectId(value)) {
return true;
} else {
const httpExceptionObject: {
status: number;
error: string;
message: string[];
} = {
status: 400,
error: 'Invalid mongoose object ID',
message: [GlobalExceptionKeys.IncorrectMongooseID],
};
throw new HttpException(httpExceptionObject, 400);
}
},
},
});
};
}
23 changes: 23 additions & 0 deletions src/shared/mongoose-validator.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Injectable } from '@nestjs/common';
import { ExceptionService } from './exception.service';
import { ExceptionStatusKeys, GlobalExceptionKeys } from 'src/enums';
import mongoose from 'mongoose';

@Injectable()
export class MongooseValidatorService {
constructor(private exceptionService: ExceptionService) {}

isValidObjectId(...ids: string[]) {
ids.forEach((id) => {
mongoose.isValidObjectId(id) || this.throwInvalidId();
});
}

private throwInvalidId() {
this.exceptionService.throwError(
ExceptionStatusKeys.BadRequest,
'Ivalid mongoose object id',
GlobalExceptionKeys.IncorrectMongooseID,
);
}
}

0 comments on commit 7285507

Please sign in to comment.