2 Incheckningar f99f3e54ef ... e9839bfb77

Upphovsman SHA1 Meddelande Datum
  BedirhanOZCAN e9839bfb77 Merge branch 'bugfix/dto-bugfix' into develop 1 månad sedan
  BedirhanOZCAN 19b3c33869 Bugfix: Clean up code formatting and improve validation messages across menu and product actions 1 månad sedan

+ 10 - 3
src/actions/auth/register/index.ts

@@ -78,7 +78,7 @@ const register = async (input: RegisterInput): Promise<RegisterResult> => {
             slug,
             mail
         });
-        
+
         await Menu.create({
             userID: newUser._id,
             isViewPrice: true,
@@ -87,9 +87,16 @@ const register = async (input: RegisterInput): Promise<RegisterResult> => {
         });
 
         const starterPlan = await Plan.findOne({
-            title: "Starter" 
+            title: "Starter"
         });
-        
+
+        if (!starterPlan) {
+            return {
+                message: "starter-plan-not-configured-in-database",
+                code: 500
+            };
+        }
+
         await Subscription.create({
             userID: newUser._id.toString(),
             planID: starterPlan._id,

+ 7 - 7
src/actions/menu/addCategory/index.ts

@@ -1,9 +1,9 @@
 import {
-    Category 
+    Category
 } from "../../../models";
 import {
-    AddCategoryInput, 
-    AddCategoryResult
+    AddCategoryResult,
+    AddCategoryInput
 } from "./types";
 
 const addCategory = async (userID: string, categoryLimit: number, input: AddCategoryInput): Promise<AddCategoryResult> => {
@@ -21,7 +21,7 @@ const addCategory = async (userID: string, categoryLimit: number, input: AddCate
                 message: "category-limit-reached",
                 code: 403
             };
-        } 
+        }
 
         const existing = await Category.findOne({
             userID,
@@ -29,8 +29,8 @@ const addCategory = async (userID: string, categoryLimit: number, input: AddCate
         });
         if (existing) {
             return {
-                message: "index-already-in-use", 
-                code: 409 
+                message: "index-already-in-use",
+                code: 409
             };
         }
 
@@ -56,7 +56,7 @@ const addCategory = async (userID: string, categoryLimit: number, input: AddCate
         console.error("CreateCategory error:", error);
         return {
             message: "internal-server-error",
-            code: 500 
+            code: 500
         };
     }
 };

+ 4 - 4
src/actions/menu/addMenu/index.ts

@@ -1,9 +1,9 @@
 import {
-    Menu 
+    Menu
 } from "../../../models";
 import {
-    AddMenuInput,
-    AddMenuResult
+    AddMenuResult,
+    AddMenuInput
 } from "./types";
 
 const addMenu = async (userID: string, menuLimit: number, input: AddMenuInput): Promise<AddMenuResult> => {
@@ -23,7 +23,7 @@ const addMenu = async (userID: string, menuLimit: number, input: AddMenuInput):
                 message: "menu-limit-reached",
                 code: 403
             };
-        }  
+        }
 
         const newMenu = await Menu.create({
             isViewPrice,

+ 8 - 10
src/actions/menu/addProduct/index.ts

@@ -1,9 +1,10 @@
 import {
-    Menu, Product 
+    Product,
+    Menu
 } from "../../../models";
 import {
-    AddProductInput,
-    AddProductResult
+    AddProductResult,
+    AddProductInput
 } from "./types";
 
 const addProduct = async (userID: string, productLimit: number, input: AddProductInput): Promise<AddProductResult> => {
@@ -13,22 +14,19 @@ const addProduct = async (userID: string, productLimit: number, input: AddProduc
             ...rest
         } = input;
 
-        const normalizedCategoryIDs = Array.isArray(categoryIDs)
-            ? categoryIDs
-            : categoryIDs ? [categoryIDs] : [];
-
         const productCount = await Product.countDocuments({
             userID
         });
+
         if (productCount >= productLimit) {
             return {
                 message: "product-limit-reached",
                 code: 403
             };
-        } 
+        }
 
         const newProduct = await Product.create({
-            categoryIDs: normalizedCategoryIDs,
+            categoryIDs: categoryIDs,
             ...rest,
             userID,
         });
@@ -43,7 +41,7 @@ const addProduct = async (userID: string, productLimit: number, input: AddProduc
                 $push: {
                     products: {
                         productID: newProduct._id,
-                        categoryIDs: normalizedCategoryIDs,
+                        categoryIDs: categoryIDs,
                         isViewPrice: true
                     }
                 }

+ 6 - 4
src/actions/menu/addProduct/types.ts

@@ -4,13 +4,15 @@ import {
     IsBoolean,
     IsString,
     IsNumber,
+    IsArray,
     Min
 } from "class-validator";
 
-
 export class AddProductInput {
-    @IsNotEmpty({ message: "categoryID-is-required" })
-    categoryIDs?: string | string[];
+    @IsArray({ message: "categoryIDs-must-be-an-array" })
+    @IsString({ each: true, message: "each-categoryID-must-be-a-string" })
+    @IsNotEmpty({ message: "categoryIDs-is-required" })
+    categoryIDs!: string[];
 
     @IsString()
     @IsNotEmpty({ message: "title-is-required" })
@@ -26,7 +28,7 @@ export class AddProductInput {
     price!: number;
 
     @IsOptional()
-    photos?: { 
+    photos?: {
         coverPhotoPath: string;
         isPrimary: boolean;
     }[];

+ 8 - 9
src/actions/menu/deleteCategory/index.ts

@@ -1,6 +1,7 @@
+import mongoose from "mongoose";
 import {
-    Category, 
-    Product 
+    Category,
+    Product
 } from "../../../models";
 import {
     DeleteCategoryResult,
@@ -13,20 +14,18 @@ const deleteCategory = async (userID: string, input: DeleteCategoryInput): Promi
             categoryIDs
         } = input;
 
-        const normalizedCategoryIDs = Array.isArray(categoryIDs)
-            ? categoryIDs
-            : categoryIDs ? [categoryIDs] : [];
-
-        if (normalizedCategoryIDs.length === 0) {
+        if (categoryIDs.length === 0) {
             return {
                 message: "categoryIDs-cannot-be-empty",
                 code: 400
             };
         }
 
+        const objectIdArray = categoryIDs.map(id => new mongoose.Types.ObjectId(id));
+
         const productInCategory = await Product.findOne({
             categoryIDs: {
-                $in: normalizedCategoryIDs
+                $in: objectIdArray
             }
         });
 
@@ -39,7 +38,7 @@ const deleteCategory = async (userID: string, input: DeleteCategoryInput): Promi
 
         const result = await Category.deleteMany({
             _id: {
-                $in: normalizedCategoryIDs
+                $in: objectIdArray
             },
             userID: userID
         });

+ 6 - 2
src/actions/menu/deleteCategory/types.ts

@@ -1,10 +1,14 @@
 import {
-    IsNotEmpty
+    IsNotEmpty,
+    IsString,
+    IsArray
 } from "class-validator";
 
 export class DeleteCategoryInput {
+    @IsArray({ message: "categoryIDs-must-be-an-array" })
+    @IsString({ each: true, message: "each-categoryID-must-be-a-string" })
     @IsNotEmpty({ message: "categoryIDs-is-required" })
-    categoryIDs!: string | string[];
+    categoryIDs!: string[];
 }
 
 export interface DeleteCategoryResult {

+ 28 - 12
src/actions/menu/deleteMenu/index.ts

@@ -1,3 +1,4 @@
+import mongoose from "mongoose";
 import {
     Menu
 } from "../../../models";
@@ -12,33 +13,48 @@ const deleteMenu = async (userID: string, input: DeleteMenuInput): Promise<Delet
             menuIDs
         } = input;
 
-        const menu = await Menu.findOne({
+        // Erken Çıkış
+        if (menuIDs.length === 0) {
+            return {
+                message: "menuIDs-cannot-be-empty",
+                code: 400
+            };
+        }
+
+        // KESİNLİKLE YAPILMASI GEREKEN TİP DÖNÜŞÜMÜ
+        const objectIdArray = menuIDs.map(id => new mongoose.Types.ObjectId(id));
+
+        // KULLANICININ İSTEDİĞİ MENÜLER GERÇEKTEN VAR MI
+        // findOne yerine gönderilen ID sayısıyla, bulunan ID sayısını karşılaştırıyoruz
+        const existingMenusCount = await Menu.countDocuments({
             _id: {
-                $in: menuIDs 
+                $in: objectIdArray
             },
-            userID
+            userID: userID
         });
 
-        if (!menu) {
+        // Kullanıcı 3 ID yolladı ama DB'de 2 tane bulduysak, biri uydurma veya başkasının menüsüdür.
+        if (existingMenusCount !== objectIdArray.length) {
             return {
-                message: "menu-not-found",
+                message: "one-or-more-menus-not-found-or-unauthorized",
                 code: 404
             };
         }
 
-        const activeMenuCount = await Menu.countDocuments({ // kullanıcının toplam aktif menü sayısı
-            userID,
+        const activeMenuCount = await Menu.countDocuments({
+            userID: userID,
             isActive: true
         });
 
-        const deletingActiveCount = await Menu.countDocuments({ //silinmek istenen menüler arasında kaç tane aktif var
+        const deletingActiveCount = await Menu.countDocuments({
             _id: {
-                $in: menuIDs 
+                $in: objectIdArray
             },
-            userID,
+            userID: userID,
             isActive: true
         });
 
+        // Kullanıcının en az 1 tane aktif menüsü kalmak zorundadır
         if (activeMenuCount - deletingActiveCount < 1) {
             return {
                 message: "cannot-delete-last-active-menu",
@@ -48,9 +64,9 @@ const deleteMenu = async (userID: string, input: DeleteMenuInput): Promise<Delet
 
         await Menu.deleteMany({
             _id: {
-                $in: menuIDs 
+                $in: objectIdArray
             },
-            userID
+            userID: userID
         });
 
         return {

+ 3 - 1
src/actions/menu/deleteMenu/types.ts

@@ -1,10 +1,12 @@
 import {
     IsNotEmpty,
+    IsString,
     IsArray
 } from "class-validator";
 
 export class DeleteMenuInput {
-    @IsArray()
+    @IsArray({ message: "menuIDs-must-be-an-array" })
+    @IsString({ each: true, message: "each-menuID-must-be-a-string" })
     @IsNotEmpty({ message: "menuIDs-is-required" })
     menuIDs!: string[];
 }

+ 10 - 6
src/actions/menu/deleteProduct/index.ts

@@ -4,8 +4,8 @@ import {
     Menu
 } from "../../../models";
 import {
-    DeleteProductInput,
-    DeleteProductResult
+    DeleteProductResult,
+    DeleteProductInput
 } from "./types";
 
 const deleteProduct = async (userID: string, input: DeleteProductInput): Promise<DeleteProductResult> => {
@@ -14,11 +14,14 @@ const deleteProduct = async (userID: string, input: DeleteProductInput): Promise
             productIDs
         } = input;
 
-        const normalizedProductIDs = Array.isArray(productIDs)
-            ? productIDs
-            : productIDs ? [productIDs] : [];
+        if (productIDs.length === 0) {
+            return {
+                message: "productIDs-cannot-be-empty",
+                code: 400
+            };
+        }
 
-        const objectIdArray = normalizedProductIDs.map(id => new mongoose.Types.ObjectId(id));
+        const objectIdArray = productIDs.map(id => new mongoose.Types.ObjectId(id));
 
         const result = await Product.deleteMany({
             _id: {
@@ -48,6 +51,7 @@ const deleteProduct = async (userID: string, input: DeleteProductInput): Promise
                 }
             }
         );
+
         return {
             message: "product-deleted-successfully",
             code: 200

+ 4 - 1
src/actions/menu/deleteProduct/types.ts

@@ -1,11 +1,14 @@
 import {
     IsNotEmpty,
+    IsArray,
     IsString
 } from "class-validator";
 
 export class DeleteProductInput {
+    @IsArray({ message: "productIDs-must-be-an-array" })
+    @IsString({ each: true, message: "each-productID-must-be-a-string" })
     @IsNotEmpty({ message: "productIDs-is-required" })
-    productIDs!: string | string[];
+    productIDs!: string[];
 }
 
 export type DeleteProductResult = {

+ 3 - 3
src/actions/menu/getMenus/index.ts

@@ -2,8 +2,8 @@ import {
     Menu
 } from "../../../models";
 import {
-    GetMenusInput,
-    GetMenusResult
+    GetMenusResult,
+    GetMenusInput
 } from "./types";
 
 const getMenus = async (userID: string, query: GetMenusInput): Promise<GetMenusResult> => {
@@ -14,7 +14,7 @@ const getMenus = async (userID: string, query: GetMenusInput): Promise<GetMenusR
         } = query;
 
         const matchStage: any = {
-            userID 
+            userID
         };
 
         if (isActive !== undefined) {

+ 0 - 1
src/actions/menu/getMenus/types.ts

@@ -1,5 +1,4 @@
 import {
-    IsBoolean,
     IsOptional,
     IsString
 } from "class-validator";

+ 2 - 4
src/actions/menu/getProducts/index.ts

@@ -8,7 +8,7 @@ import {
     Menu
 } from "../../../models/";
 
-const getProducts = async (userID: string , query: GetProductsInput): Promise<GetProductsResult> => {
+const getProducts = async (userID: string, query: GetProductsInput): Promise<GetProductsResult> => {
     try {
 
         const {
@@ -22,10 +22,8 @@ const getProducts = async (userID: string , query: GetProductsInput): Promise<Ge
         };
 
         if (categoryIDs) {
-            const normalizedCatIDs = Array.isArray(categoryIDs) ? categoryIDs : [categoryIDs];
-            
             matchStage.categoryIDs = {
-                $in: normalizedCatIDs.map(id => new mongoose.Types.ObjectId(id))
+                $in: categoryIDs.map(id => new mongoose.Types.ObjectId(id))
             };
         }
 

+ 5 - 2
src/actions/menu/getProducts/types.ts

@@ -1,11 +1,14 @@
 import {
     IsOptional,
-    IsString
+    IsString,
+    IsArray
 } from "class-validator";
 
 export class GetProductsInput {
     @IsOptional()
-    categoryIDs?: string | string[];
+    @IsArray({ message: "categoryIDs-must-be-an-array" })
+    @IsString({ each: true, message: "each-categoryID-must-be-a-string" })
+    categoryIDs?: string[];
 
     @IsString()
     @IsOptional()

+ 2 - 2
src/actions/menu/updateMenu/index.ts

@@ -2,8 +2,8 @@ import {
     Menu
 } from "../../../models";
 import {
-    UpdateMenuInput,
-    UpdateMenuResult
+    UpdateMenuResult,
+    UpdateMenuInput
 } from "./types";
 
 const updateMenu = async (userID: string, input: UpdateMenuInput): Promise<UpdateMenuResult> => {

+ 2 - 3
src/actions/menu/updateMenu/types.ts

@@ -1,9 +1,8 @@
 import {
-    IsBoolean,
     IsNotEmpty,
     IsOptional,
-    IsString,
-    Validate
+    IsBoolean,
+    IsString
 } from "class-validator";
 
 export class UpdateMenuInput {

+ 5 - 5
src/actions/menu/updateProduct/index.ts

@@ -3,8 +3,8 @@ import {
     Product
 } from "../../../models";
 import {
-    UpdateProductInput,
-    UpdateProductResult
+    UpdateProductResult,
+    UpdateProductInput
 } from "./types";
 
 const updateProduct = async (userID: string, input: UpdateProductInput): Promise<UpdateProductResult> => {
@@ -14,13 +14,13 @@ const updateProduct = async (userID: string, input: UpdateProductInput): Promise
             productID,
             ...rest
         } = input;
- 
+
         const updateData: any = {
             ...rest
         };
-        
+
         if (categoryIDs !== undefined) {
-            updateData.categoryIDs = Array.isArray(categoryIDs) ? categoryIDs : [categoryIDs];
+            updateData.categoryIDs = categoryIDs;
         }
 
         const updatedDoc = await Product.findOneAndUpdate(

+ 4 - 1
src/actions/menu/updateProduct/types.ts

@@ -4,6 +4,7 @@ import {
     IsBoolean,
     IsString,
     IsNumber,
+    IsArray,
     Min
 } from "class-validator";
 
@@ -12,9 +13,11 @@ export class UpdateProductInput {
     @IsNotEmpty({ message: "productID-is-required" })
     productID!: string;
 
+    @IsArray({ message: "categoryIDs-must-be-an-array" })
+    @IsString({ each: true, message: "each-categoryID-must-be-a-string" })
     @IsNotEmpty({ message: "categoryIDs-cannot-be-empty" }) //categoryId hiç gelmeyebilir ancak gelecekse de "" gelemez
     @IsOptional()
-    categoryIDs?: string | string[];
+    categoryIDs?: string[];
 
     @IsString({ message: "title-must-be-a-string" })
     @IsOptional()