codeweaver 1.0.14 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ import { ZodObject } from "zod";
2
+ import ZodValidator from "./validator.class";
3
+
4
+ export const ZodOutput =
5
+ <T extends ZodObject<any, any>>(schema: T): MethodDecorator =>
6
+ (target, propertyKey) =>
7
+ ZodValidator.registerMethodValidationOutputSchema(
8
+ target,
9
+ propertyKey as string,
10
+ schema
11
+ );
@@ -11,12 +11,12 @@ const router = Router();
11
11
  * description: Returns the home page.
12
12
  * responses:
13
13
  * 200:
14
- * description: home page
14
+ * description: Hi there!
15
15
  */
16
16
  router.get(
17
17
  "/",
18
18
  asyncHandler(async (req: Request, res: Response) => {
19
- res.send("Home");
19
+ res.send("Hi there!");
20
20
  })
21
21
  );
22
22
 
@@ -0,0 +1,40 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Zod schema for Order entity
5
+ * @typedef {Object} ZodOrder
6
+ * @property {number} id - Unique identifier (min 1)
7
+ * @property {number} userId - Associated user ID (min 1)
8
+ * @property {{productId: number, quantity: number}[]} products - Array of ordered products
9
+ * @property {"Processing"|"Delivered"|"Canceled"} status - Order status
10
+ * @property {number} total - Total price (min 1000)
11
+ * @property {Date} createdAt - Creation timestamp
12
+ * @property {Date} [canceledAt] - Cancellation timestamp
13
+ * @property {Date} [deliveredAt] - Delivery timestamp
14
+ */
15
+ export const ZodOrder = z.object({
16
+ id: z.number().min(1).int(),
17
+ userId: z.number().min(1).int(),
18
+ products: z.array(
19
+ z.object({
20
+ productId: z.number().min(1).int(),
21
+ quantity: z.number().min(1).int(),
22
+ })
23
+ ),
24
+ status: z.enum(["Processing", "Delivered", "Canceled"]),
25
+ total: z.number().min(1000),
26
+ createdAt: z.date(),
27
+ canceledAt: z.date().optional(),
28
+ deliveredAt: z.date().optional(),
29
+ });
30
+
31
+ export const ZodOrderCreationDto = ZodOrder.omit({
32
+ id: true,
33
+ status: true,
34
+ createdAt: true,
35
+ canceledAt: true,
36
+ deliveredAt: true,
37
+ });
38
+
39
+ export type Order = z.infer<typeof ZodOrder>;
40
+ export type OrderCreationDto = z.infer<typeof ZodOrderCreationDto>;
@@ -1,39 +1,160 @@
1
1
  import { Router, Request, Response } from "express";
2
2
  import asyncHandler from "express-async-handler";
3
+ import OrderController from "./order.controller";
4
+ import { sendError } from "@/utilities";
3
5
 
4
6
  const router = Router();
7
+ const orderController = new OrderController();
8
+
9
+ /**
10
+ * @swagger
11
+ * /orders:
12
+ * post:
13
+ * summary: Create a new order
14
+ * description: Creates an order with user details and products.
15
+ * consumes:
16
+ * - application/json
17
+ * produces:
18
+ * - application/json
19
+ * parameters:
20
+ * - in: body
21
+ * name: order
22
+ * required: true
23
+ * schema:
24
+ * type: object
25
+ * required:
26
+ * - userId
27
+ * - products
28
+ * - total
29
+ * properties:
30
+ * userId:
31
+ * type: integer
32
+ * minimum: 1
33
+ * example: 1
34
+ * products:
35
+ * type: array
36
+ * description: Array of products in the order
37
+ * items:
38
+ * type: object
39
+ * required:
40
+ * - productId
41
+ * - quantity
42
+ * properties:
43
+ * productId:
44
+ * type: integer
45
+ * minimum: 1
46
+ * example: 3
47
+ * quantity:
48
+ * type: integer
49
+ * minimum: 1
50
+ * example: 2
51
+ * total:
52
+ * type: number
53
+ * minimum: 1000
54
+ * example: 250000
55
+ * responses:
56
+ * 201:
57
+ * description: Order created
58
+ */
59
+ router.post(
60
+ "/",
61
+ asyncHandler(async (req: Request, res: Response) => {
62
+ const order = await orderController.create(req.body);
63
+ res.status(201).json(order);
64
+ })
65
+ );
5
66
 
6
67
  /**
7
68
  * @swagger
8
69
  * /orders:
9
70
  * get:
10
- * summary: Get order home
11
- * description: Returns the order home page.
71
+ * summary: Get all orders
12
72
  * responses:
13
73
  * 200:
14
- * description: Order home page
74
+ * description: List of orders
15
75
  */
16
76
  router.get(
17
77
  "/",
18
78
  asyncHandler(async (req: Request, res: Response) => {
19
- res.send("Order Home");
79
+ const orders = await orderController.getAll();
80
+ res.json(orders);
20
81
  })
21
82
  );
22
83
 
23
84
  /**
24
85
  * @swagger
25
- * /orders/history:
86
+ * /orders/{id}:
26
87
  * get:
27
- * summary: Get order history
28
- * description: Returns order history.
88
+ * summary: Get order details
89
+ * parameters:
90
+ * - name: id
91
+ * in: path
92
+ * required: true
93
+ * description: Numeric ID of the order to retrieve
94
+ * type: integer
95
+ * example: 101
29
96
  * responses:
30
97
  * 200:
31
- * description: Order history
98
+ * description: Order details
32
99
  */
33
100
  router.get(
34
- "/history",
101
+ "/:id",
102
+ asyncHandler(async (req: Request, res: Response) => {
103
+ const order = await orderController.get(req.params.id);
104
+ if ("id" in order == false) sendError(res, order);
105
+ else res.json(order);
106
+ })
107
+ );
108
+
109
+ /**
110
+ * @swagger
111
+ * /orders/{id}/cancel:
112
+ * patch:
113
+ * summary: Cancel order
114
+ * parameters:
115
+ * - name: id
116
+ * in: path
117
+ * required: true
118
+ * description: Numeric ID of the order to cancel
119
+ * type: integer
120
+ * example: 101
121
+ * responses:
122
+ * 200:
123
+ * description: Order canceled successfully
124
+ */
125
+ router.patch(
126
+ "/:id/cancel",
127
+ asyncHandler(async (req: Request, res: Response) => {
128
+ const order = await orderController.cancel(req.params.id);
129
+ if ("id" in order == false) sendError(res, order);
130
+ else res.json(order);
131
+ })
132
+ );
133
+
134
+ /**
135
+ * @swagger
136
+ * /orders/{id}/deliver:
137
+ * patch:
138
+ * summary: Mark order as delivered
139
+ * parameters:
140
+ * - name: id
141
+ * in: path
142
+ * required: true
143
+ * description: Numeric ID of the order to deliver
144
+ * type: integer
145
+ * example: 101
146
+ * responses:
147
+ * 200:
148
+ * description: Order marked as delivered
149
+ * 400:
150
+ * description: Delivery is only available when the order is in processing status.
151
+ */
152
+ router.patch(
153
+ "/:id/deliver",
35
154
  asyncHandler(async (req: Request, res: Response) => {
36
- res.send("Order History");
155
+ const order = await orderController.deliver(req.params.id);
156
+ if ("id" in order == false) sendError(res, order);
157
+ else res.json(order);
37
158
  })
38
159
  );
39
160
 
@@ -0,0 +1,191 @@
1
+ import { onError, rateLimit, timeout } from "utils-decorators";
2
+ import {
3
+ Order,
4
+ OrderCreationDto,
5
+ ZodOrderCreationDto,
6
+ } from "./dto/order.dto";
7
+ import { Validate, ZodInput } from "@pkg/ts-zod-decorators";
8
+ import { ResponseError } from "@/types";
9
+ import { tryParseId } from "@/utilities";
10
+
11
+ // Array to store orders (as a mock database)
12
+ const orders: Order[] = [
13
+ {
14
+ id: 1,
15
+ userId: 1,
16
+ products: [
17
+ { productId: 2, quantity: 1 },
18
+ { productId: 6, quantity: 2 },
19
+ ],
20
+ status: "Delivered",
21
+ total: 14400,
22
+ createdAt: new Date("2024-01-15"),
23
+ deliveredAt: new Date("2024-02-10"),
24
+ },
25
+ {
26
+ id: 2,
27
+ userId: 3,
28
+ products: [
29
+ { productId: 9, quantity: 1 },
30
+ { productId: 7, quantity: 1 },
31
+ ],
32
+ status: "Processing",
33
+ total: 36500,
34
+ createdAt: new Date("2024-03-20"),
35
+ },
36
+ {
37
+ id: 3,
38
+ userId: 2,
39
+ products: [
40
+ { productId: 1, quantity: 1 },
41
+ { productId: 4, quantity: 2 },
42
+ ],
43
+ status: "Canceled",
44
+ total: 81000,
45
+ createdAt: new Date("2024-05-01"),
46
+ canceledAt: new Date("2024-05-03"),
47
+ },
48
+ ];
49
+
50
+ function exceedHandler() {
51
+ const message = "Too much call in allowed window";
52
+
53
+ throw new Error(message, {
54
+ cause: { status: 500, message } satisfies ResponseError,
55
+ });
56
+ }
57
+
58
+ function getOrderErrorHandler(e: Error) {
59
+ const message = "Order not found.";
60
+
61
+ throw new Error(message, {
62
+ cause: { status: 404, message, details: e.message } satisfies ResponseError,
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Controller for handling order-related operations
68
+ * @class OrderController
69
+ * @desc Provides methods for order management including creation, status updates and retrieval
70
+ */
71
+ export default class OrderController {
72
+ @rateLimit({
73
+ timeSpanMs: 60000,
74
+ allowedCalls: 300,
75
+ exceedHandler,
76
+ })
77
+ @Validate
78
+ /**
79
+ * Create a new order
80
+ * @param {OrderCreationDto} order - Order creation data
81
+ * @returns {Promise<Order>} Newly created order
82
+ */
83
+ public async create(@ZodInput(ZodOrderCreationDto) order: OrderCreationDto) {
84
+ const newOrder = {
85
+ ...order,
86
+ id: orders.length + 1,
87
+ createdAt: new Date(),
88
+ status: "Processing",
89
+ } satisfies Order;
90
+
91
+ orders.push(newOrder);
92
+ return newOrder;
93
+ }
94
+
95
+ @timeout(20000)
96
+ @rateLimit({
97
+ timeSpanMs: 60000,
98
+ allowedCalls: 300,
99
+ exceedHandler,
100
+ })
101
+ /**
102
+ * Retrieves all orders
103
+ * @returns List of orders
104
+ */
105
+ public async getAll(): Promise<Order[]> {
106
+ return orders;
107
+ }
108
+
109
+ @rateLimit({
110
+ timeSpanMs: 60000,
111
+ allowedCalls: 300,
112
+ exceedHandler,
113
+ })
114
+ @onError({
115
+ func: getOrderErrorHandler,
116
+ })
117
+ /**
118
+ * Finds an order by its ID
119
+ * @param id - Order ID as string
120
+ * @returns Order details or error object if not found
121
+ */
122
+ public async get(id: string): Promise<Order | ResponseError> {
123
+ const orderId = tryParseId(id);
124
+ if (typeof orderId != "number") return orderId satisfies ResponseError;
125
+ const order = orders.find((order) => order.id === orderId);
126
+
127
+ if (!order)
128
+ return {
129
+ status: 404,
130
+ message: "Order dose not exist.",
131
+ } satisfies ResponseError;
132
+
133
+ return order satisfies Order;
134
+ }
135
+
136
+ @rateLimit({
137
+ timeSpanMs: 60000,
138
+ allowedCalls: 300,
139
+ exceedHandler,
140
+ })
141
+ /**
142
+ * Cancel an existing order
143
+ * @param {string} id - Order ID to cancel
144
+ * @returns {Promise<Order | ResponseError>} Updated order or error object
145
+ * @throws {ResponseError} 404 - Order not found
146
+ * @throws {ResponseError} 400 - Invalid ID format or invalid status for cancellation
147
+ */
148
+ public async cancel(id: string): Promise<Order | ResponseError> {
149
+ let order = await this.get(id);
150
+ if ("id" in order == false) return order satisfies ResponseError;
151
+
152
+ if (order.status != "Processing")
153
+ return {
154
+ status: 400,
155
+ message:
156
+ "Cancellation is not available unless the order is in processing status.",
157
+ } satisfies ResponseError;
158
+
159
+ order.status = "Canceled";
160
+ order.deliveredAt = new Date();
161
+ return order satisfies Order;
162
+ }
163
+
164
+ @rateLimit({
165
+ timeSpanMs: 60000,
166
+ allowedCalls: 300,
167
+ exceedHandler,
168
+ })
169
+ /**
170
+ * Mark an order as delivered
171
+ * @param {string} id - Order ID to mark as delivered
172
+ * @returns {Promise<Order | ResponseError>} Updated order or error object
173
+ * @throws {ResponseError} 404 - Order not found
174
+ * @throws {ResponseError} 400 - Invalid ID format or invalid status for delivery
175
+ */
176
+ public async deliver(id: string): Promise<Order | ResponseError> {
177
+ let order = await this.get(id);
178
+ if ("id" in order == false) return order satisfies ResponseError;
179
+
180
+ if (order.status != "Processing")
181
+ return {
182
+ status: 400,
183
+ message:
184
+ "Delivery is only available when the order is in processing status.",
185
+ } satisfies ResponseError;
186
+
187
+ order.status = "Delivered";
188
+ order.deliveredAt = new Date();
189
+ return order satisfies Order;
190
+ }
191
+ }
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Zod schema for Product entity
5
+ * @typedef {Object} ZodProduct
6
+ * @property {number} id - Unique identifier (min 1)
7
+ * @property {string} name - Product name (min 2 chars)
8
+ * @property {number} price - Product price (min 1000)
9
+ * @property {string} [description] - Optional description (min 10 chars)
10
+ * @property {string} category - Product category from predefined enum
11
+ * @property {number} stock - Available stock quantity (min 0)
12
+ */
13
+ export const ZodProduct = z.object({
14
+ id: z.number().min(1).int(),
15
+ name: z.string().min(2),
16
+ price: z.number().min(1000),
17
+ description: z.string().min(10).optional(),
18
+ category: z.enum([
19
+ "Electronics",
20
+ "Appliances",
21
+ "Sports",
22
+ "Kitchen",
23
+ "Mobile Accessories",
24
+ "Computer Accessories",
25
+ "Home Appliances",
26
+ "Books",
27
+ ]),
28
+ stock: z.number().min(0).int(),
29
+ });
30
+
31
+ export const ZodProductCreationDto = ZodProduct.omit({ id: true });
32
+ export const ZodProductUpdateDto = ZodProductCreationDto.partial();
33
+
34
+ export type Product = z.infer<typeof ZodProduct>;
35
+ export type ProductCreationDto = z.infer<typeof ZodProductCreationDto>;
36
+ export type ProductUpdateDto = z.infer<typeof ZodProductUpdateDto>;