diff --git a/src/common/constants/index.ts b/src/common/constants/index.ts index d2b97d8..ef26995 100755 --- a/src/common/constants/index.ts +++ b/src/common/constants/index.ts @@ -15,6 +15,6 @@ export const API_HEADER_SLUG = { required: true, schema: { type: 'string', - default: 'zhivan', + default: 'boote', }, }; diff --git a/src/modules/products/controllers/category.controller.ts b/src/modules/products/controllers/category.controller.ts index 49ce116..2da5bf0 100644 --- a/src/modules/products/controllers/category.controller.ts +++ b/src/modules/products/controllers/category.controller.ts @@ -5,11 +5,6 @@ import { UpdateCategoryDto } from '../dto/update-category.dto'; import { ApiTags, ApiOperation, - ApiCreatedResponse, - ApiOkResponse, - ApiNotFoundResponse, - ApiParam, - ApiBody, ApiBearerAuth, ApiHeader, } from '@nestjs/swagger'; @@ -28,62 +23,55 @@ export class CategoryController { @Get('public/categories/shop/:slug') @ApiOperation({ summary: 'Get all categories by shop slug' }) @ApiHeader(API_HEADER_SLUG) - @ApiParam({ name: 'slug', required: true, description: 'Shop Slug' }) - @ApiOkResponse({ description: 'List of all categories for the shop' }) findAllByRestaurant(@Param('slug') slug: string) { - return this.categoryService.findAllByShop(slug); + return this.categoryService.findAllByShopSlug(slug); } /*** Admin ***/ + @Post('admin/categories') @ApiOperation({ summary: 'Create category' }) - @ApiBody({ type: CreateCategoryDto }) @Permissions(Permission.MANAGE_CATEGORIES) @UseGuards(AdminAuthGuard) @ApiBearerAuth() - @Post('admin/categories') - create(@Body() dto: CreateCategoryDto, @ShopId() restId: string) { - return this.categoryService.create(restId, dto); + create(@Body() dto: CreateCategoryDto, @ShopId() shopId: string) { + return this.categoryService.create(shopId, dto); } + + @Get('admin/categories') @Permissions(Permission.MANAGE_CATEGORIES) @ApiOperation({ summary: 'my shop categories' }) @UseGuards(AdminAuthGuard) @ApiBearerAuth() - @Get('admin/categories') - findAll(@ShopId() restId: string) { - return this.categoryService.findAllByShopId(restId); + findAll(@ShopId() shopId: string) { + return this.categoryService.findAllByShopId(shopId); } - @Permissions(Permission.MANAGE_CATEGORIES) - @UseGuards(AdminAuthGuard) - @ApiBearerAuth() @Get('admin/categories/:id') + @Permissions(Permission.MANAGE_CATEGORIES) + @UseGuards(AdminAuthGuard) + @ApiBearerAuth() @ApiOperation({ summary: 'Get category' }) - @ApiParam({ name: 'id', required: true, type: String }) - findOne(@Param('id') id: string, @ShopId() restId: string) { - return this.categoryService.findOne(restId, id); + findOne(@Param('id') id: string, @ShopId() shopId: string) { + return this.categoryService.findOne(shopId, id); } - @UseGuards(AdminAuthGuard) - @ApiBearerAuth() - @Permissions(Permission.MANAGE_CATEGORIES) @Patch('admin/categories/:id') - @ApiOperation({ summary: 'Update category' }) - @ApiParam({ name: 'id' }) - @ApiBody({ type: UpdateCategoryDto }) - update(@Param('id') id: string, @Body() dto: UpdateCategoryDto, @ShopId() restId: string) { - return this.categoryService.update(restId, id, dto); - } - @UseGuards(AdminAuthGuard) @ApiBearerAuth() @Permissions(Permission.MANAGE_CATEGORIES) + @ApiOperation({ summary: 'Update category' }) + update(@Param('id') id: string, @Body() dto: UpdateCategoryDto, @ShopId() shopId: string) { + return this.categoryService.update(shopId, id, dto); + } + @Delete('admin/categories/:id') + @UseGuards(AdminAuthGuard) + @ApiBearerAuth() + @Permissions(Permission.MANAGE_CATEGORIES) @ApiOperation({ summary: 'Delete category' }) - @ApiParam({ name: 'id' }) - @ApiOkResponse({ description: 'Deleted' }) - remove(@Param('id') id: string, @ShopId() restId: string) { - return this.categoryService.remove(restId, id); + remove(@Param('id') id: string, @ShopId() shopId: string) { + return this.categoryService.remove(shopId, id); } } diff --git a/src/modules/products/dto/create-category.dto.ts b/src/modules/products/dto/create-category.dto.ts index 54f6046..fb9f9e0 100644 --- a/src/modules/products/dto/create-category.dto.ts +++ b/src/modules/products/dto/create-category.dto.ts @@ -26,7 +26,6 @@ export class CreateCategoryDto { order?: number; @IsOptional() - @IsUUID() @ApiPropertyOptional({ example: '123e4567-e89b-12d3-a456-426614174000' }) parentId?: string; } diff --git a/src/modules/products/providers/category.service.ts b/src/modules/products/providers/category.service.ts index 756fa93..7f41862 100644 --- a/src/modules/products/providers/category.service.ts +++ b/src/modules/products/providers/category.service.ts @@ -1,4 +1,4 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; +import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { CreateCategoryDto } from '../dto/create-category.dto'; import { UpdateCategoryDto } from '../dto/update-category.dto'; import { CategoryRepository } from '../repositories/category.repository'; @@ -44,34 +44,25 @@ export class CategoryService { return category; } - async findAllByShop(shopSlug: string): Promise { + async findAllByShopSlug(shopSlug: string): Promise { return this.categoryRepository.find( { shop: { slug: shopSlug }, isActive: true, parent: null }, - { orderBy: { order: 'ASC' }, populate: ['parent'] }); + { orderBy: { order: 'ASC' }, populate: ['children','parent'] }); } async findAllByShopId(shopId: string): Promise { return this.categoryRepository.find( { shop: { id: shopId } }, - { orderBy: { order: 'asc' }, populate: ['parent'] }); + { orderBy: { order: 'asc' }, populate: ['children','parent'] }); } - async findOne(shopId: string, id: string): Promise { - const cat = await this.categoryRepository.findOne({ id, shop: { id: shopId } }, { populate: ['products', 'parent'] }); - if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); + async findOne(shopId: string, categoryId: string): Promise { + const category = await this.findOrFail(categoryId) + if (category.shop.id !== shopId) { + throw new BadRequestException("Category doesnnot belong to shop") + } + return category - return { - id: cat.id, - title: cat.title, - isActive: cat.isActive, - shop: cat.shop, - avatarUrl: cat.avatarUrl, - order: cat.order, - parent: cat.parent, - createdAt: cat.createdAt, - updatedAt: cat.updatedAt, - products: cat.products.getItems().map(f => ({ id: f.id, title: f.title })), - } as unknown as Category; } async update(shopId: string, id: string, dto: UpdateCategoryDto): Promise { @@ -102,4 +93,10 @@ export class CategoryService { cat.deletedAt = new Date(); await this.em.persistAndFlush(cat); } + + async findOrFail(categoryId: string) { + const category = await this.categoryRepository.findOne({ id: categoryId },{populate:['parent','children']}); + if (!category) throw new NotFoundException(CategoryMessage.NOT_FOUND); + return category + } } diff --git a/src/modules/products/providers/product.service.ts b/src/modules/products/providers/product.service.ts index ccc8e57..8bba4ac 100644 --- a/src/modules/products/providers/product.service.ts +++ b/src/modules/products/providers/product.service.ts @@ -6,16 +6,18 @@ import { CategoryRepository } from '../repositories/category.repository'; import { EntityManager } from '@mikro-orm/postgresql'; import { RequiredEntityData, FilterQuery } from '@mikro-orm/core'; import { Product } from '../entities/product.entity'; -import { CategoryMessage, ProductMessage, RestMessage } from 'src/common/enums/message.enum'; +import { CategoryMessage, ProductMessage } from 'src/common/enums/message.enum'; import { Favorite } from '../entities/favorite.entity'; import { Variant } from '../entities/variant.entity'; import { ShopService } from 'src/modules/shops/providers/shops.service'; +import { CategoryService } from './category.service'; @Injectable() export class ProductService { constructor( private readonly productRepository: ProductRepository, private readonly categoryRepository: CategoryRepository, + private readonly categoryService: CategoryService, private readonly shopService: ShopService, private readonly em: EntityManager, ) { } @@ -23,13 +25,8 @@ export class ProductService { async create(shopId: string, dto: CreateProductDto) { const { categoryId, variants, ...rest } = dto; const shop = await this.shopService.findOrFail(shopId); - if (!shop) { - throw new NotFoundException(RestMessage.NOT_FOUND); - } - const category = await this.categoryRepository.findOne({ id: categoryId, shop: { id: shopId } }); - if (!category) { - throw new NotFoundException(CategoryMessage.NOT_FOUND); - } + + const category = await this.categoryService.findOrFail(categoryId) const product = await this.em.transactional(async em => { // prepare data with defaults for required fields so repository.create typing is satisfied @@ -50,8 +47,6 @@ export class ProductService { const product = em.create(Product, data); - em.persist(product) - for (const variant of variants) { const variantRecord = em.create(Variant, { product, @@ -179,7 +174,7 @@ export class ProductService { return product; } - + async remove(shopId: string, id: string): Promise { const product = await this.findOrFail(id) @@ -216,7 +211,7 @@ export class ProductService { * Helper */ async findOrFail(productId: string) { - const product = await this.productRepository.findOne({ id: productId, isActive: true }, + const product = await this.productRepository.findOne({ id: productId }, { populate: ['category', 'variants', 'shop'] }); if (!product) throw new NotFoundException(ProductMessage.NOT_FOUND);