From bb6593c9a38b050282001aee84e50ef77d90dbd6 Mon Sep 17 00:00:00 2001 From: "T. Narantuya" Date: Tue, 2 Sep 2025 16:09:54 +0900 Subject: [PATCH] Refactor OrderFulfillmentController for improved readability and consistency - Consolidated import statements for better clarity. - Streamlined API operation and parameter definitions for enhanced readability. - Cleaned up response schemas and error handling for consistency across API responses. - Improved logging messages for better context during order fulfillment requests. --- .../order-fulfillment.controller.ts | 112 ++++++++---------- 1 file changed, 48 insertions(+), 64 deletions(-) diff --git a/apps/bff/src/orders/controllers/order-fulfillment.controller.ts b/apps/bff/src/orders/controllers/order-fulfillment.controller.ts index 28cc8795..64da5e82 100644 --- a/apps/bff/src/orders/controllers/order-fulfillment.controller.ts +++ b/apps/bff/src/orders/controllers/order-fulfillment.controller.ts @@ -1,13 +1,4 @@ -import { - Controller, - Post, - Param, - Body, - Headers, - HttpCode, - HttpStatus, - UseGuards, -} from "@nestjs/common"; +import { Controller, Post, Param, Body, Headers, HttpCode, HttpStatus, UseGuards } from "@nestjs/common"; import { ApiTags, ApiOperation, ApiParam, ApiResponse, ApiHeader } from "@nestjs/swagger"; import { ThrottlerGuard } from "@nestjs/throttler"; import { Logger } from "nestjs-pino"; @@ -28,98 +19,90 @@ export class OrderFulfillmentController { @Post(":sfOrderId/fulfill") @HttpCode(HttpStatus.OK) @UseGuards(ThrottlerGuard, EnhancedWebhookSignatureGuard) - @ApiOperation({ + @ApiOperation({ summary: "Fulfill order from Salesforce", - description: - "Secure endpoint called by Salesforce Quick Action to fulfill orders in WHMCS. Handles complete flow: SF Order → WHMCS AddOrder/AcceptOrder → SF Status Update", + description: "Secure endpoint called by Salesforce Quick Action to fulfill orders in WHMCS. Handles complete flow: SF Order → WHMCS AddOrder/AcceptOrder → SF Status Update" }) - @ApiParam({ - name: "sfOrderId", - type: String, + @ApiParam({ + name: "sfOrderId", + type: String, description: "Salesforce Order ID to provision", - example: "8014x000000ABCDXYZ", + example: "8014x000000ABCDXYZ" }) - @ApiHeader({ - name: "X-SF-Signature", - description: "HMAC-SHA256 signature of request body using shared secret", + @ApiHeader({ + name: "X-SF-Signature", + description: "HMAC-SHA256 signature of request body using shared secret", required: true, - example: "a1b2c3d4e5f6...", + example: "a1b2c3d4e5f6..." }) - @ApiHeader({ - name: "X-SF-Timestamp", - description: "ISO timestamp of request (max 5 minutes old)", + @ApiHeader({ + name: "X-SF-Timestamp", + description: "ISO timestamp of request (max 5 minutes old)", required: true, - example: "2024-01-15T10:30:00Z", + example: "2024-01-15T10:30:00Z" }) - @ApiHeader({ - name: "X-SF-Nonce", - description: "Unique nonce to prevent replay attacks", + @ApiHeader({ + name: "X-SF-Nonce", + description: "Unique nonce to prevent replay attacks", required: true, - example: "abc123def456", + example: "abc123def456" }) - @ApiHeader({ - name: "Idempotency-Key", - description: "Unique key for safe retries", + @ApiHeader({ + name: "Idempotency-Key", + description: "Unique key for safe retries", required: true, - example: "provision_8014x000000ABCDXYZ_1705312200000", + example: "provision_8014x000000ABCDXYZ_1705312200000" }) - @ApiResponse({ - status: 200, + @ApiResponse({ + status: 200, description: "Order provisioning completed successfully", schema: { type: "object", properties: { success: { type: "boolean", example: true }, - status: { - type: "string", - enum: ["Provisioned", "Already Provisioned"], - example: "Provisioned", - }, + status: { type: "string", enum: ["Provisioned", "Already Provisioned"], example: "Provisioned" }, whmcsOrderId: { type: "string", example: "12345" }, whmcsServiceIds: { type: "array", items: { type: "number" }, example: [67890, 67891] }, - message: { type: "string", example: "Order provisioned successfully in WHMCS" }, - }, - }, + message: { type: "string", example: "Order provisioned successfully in WHMCS" } + } + } }) - @ApiResponse({ - status: 400, + @ApiResponse({ + status: 400, description: "Invalid request or order not found", schema: { - type: "object", + type: "object", properties: { success: { type: "boolean", example: false }, status: { type: "string", example: "Failed" }, message: { type: "string", example: "Salesforce order not found" }, - errorCode: { type: "string", example: "ORDER_NOT_FOUND" }, - }, - }, + errorCode: { type: "string", example: "ORDER_NOT_FOUND" } + } + } }) - @ApiResponse({ - status: 401, - description: "Invalid signature or authentication", + @ApiResponse({ + status: 401, + description: "Invalid signature or authentication" }) - @ApiResponse({ - status: 409, + @ApiResponse({ + status: 409, description: "Payment method missing or other conflict", schema: { type: "object", properties: { success: { type: "boolean", example: false }, status: { type: "string", example: "Failed" }, - message: { - type: "string", - example: "Payment method missing - client must add payment method before provisioning", - }, - errorCode: { type: "string", example: "PAYMENT_METHOD_MISSING" }, - }, - }, + message: { type: "string", example: "Payment method missing - client must add payment method before provisioning" }, + errorCode: { type: "string", example: "PAYMENT_METHOD_MISSING" } + } + } }) async fulfillOrder( @Param("sfOrderId") sfOrderId: string, @Body() payload: OrderFulfillmentRequest, @Headers("idempotency-key") idempotencyKey: string ) { - this.logger.log("Salesforce fulfillment request received", { + this.logger.log("Salesforce order fulfillment request received", { sfOrderId, idempotencyKey, timestamp: payload.timestamp, @@ -128,8 +111,8 @@ export class OrderFulfillmentController { try { const result = await this.orderFulfillmentService.fulfillOrder( - sfOrderId, - payload, + sfOrderId, + payload, idempotencyKey ); @@ -150,6 +133,7 @@ export class OrderFulfillmentController { ...(result.errorCode && { errorCode: result.errorCode }), timestamp: new Date().toISOString(), }; + } catch (error) { this.logger.error("Salesforce provisioning failed", { error: error instanceof Error ? error.message : String(error),