refactor: enhance error handling in WHMCS and signup workflows
- Introduced structured error codes in BadRequestException for better clarity in WHMCS and signup workflows. - Updated error messages to include specific context, improving user feedback during account verification and migration processes. - Refined validation logic to ensure consistent error handling across services.
This commit is contained in:
parent
230a61c520
commit
49e9dba3a3
@ -7,6 +7,7 @@ import { CacheService } from "@bff/infra/cache/cache.service.js";
|
|||||||
import { SalesforceFacade } from "@bff/integrations/salesforce/facades/salesforce.facade.js";
|
import { SalesforceFacade } from "@bff/integrations/salesforce/facades/salesforce.facade.js";
|
||||||
import { SalesforceAccountService } from "@bff/integrations/salesforce/services/salesforce-account.service.js";
|
import { SalesforceAccountService } from "@bff/integrations/salesforce/services/salesforce-account.service.js";
|
||||||
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
|
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
|
||||||
|
import { ErrorCode } from "@customer-portal/domain/common";
|
||||||
import type { SignupRequest } from "@customer-portal/domain/auth";
|
import type { SignupRequest } from "@customer-portal/domain/auth";
|
||||||
import type { SignupAccountSnapshot, SignupAccountCacheEntry } from "./signup.types.js";
|
import type { SignupAccountSnapshot, SignupAccountCacheEntry } from "./signup.types.js";
|
||||||
import { PORTAL_SOURCE_NEW_SIGNUP } from "@bff/modules/auth/constants/portal.constants.js";
|
import { PORTAL_SOURCE_NEW_SIGNUP } from "@bff/modules/auth/constants/portal.constants.js";
|
||||||
@ -37,9 +38,10 @@ export class SignupAccountResolverService {
|
|||||||
if (normalizedCustomerNumber) {
|
if (normalizedCustomerNumber) {
|
||||||
const resolved = await this.getAccountSnapshot(normalizedCustomerNumber);
|
const resolved = await this.getAccountSnapshot(normalizedCustomerNumber);
|
||||||
if (!resolved) {
|
if (!resolved) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException({
|
||||||
`Salesforce account not found for Customer Number: ${normalizedCustomerNumber}`
|
code: ErrorCode.CUSTOMER_NOT_FOUND,
|
||||||
);
|
message: `Salesforce account not found for Customer Number: ${normalizedCustomerNumber}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resolved.WH_Account__c && resolved.WH_Account__c.trim() !== "") {
|
if (resolved.WH_Account__c && resolved.WH_Account__c.trim() !== "") {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { WhmcsAccountDiscoveryService } from "@bff/integrations/whmcs/services/w
|
|||||||
import { WhmcsClientService } from "@bff/integrations/whmcs/services/whmcs-client.service.js";
|
import { WhmcsClientService } from "@bff/integrations/whmcs/services/whmcs-client.service.js";
|
||||||
import { SalesforceFacade } from "@bff/integrations/salesforce/facades/salesforce.facade.js";
|
import { SalesforceFacade } from "@bff/integrations/salesforce/facades/salesforce.facade.js";
|
||||||
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
|
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
|
||||||
|
import { ErrorCode } from "@customer-portal/domain/common";
|
||||||
import { mapPrismaUserToDomain } from "@bff/infra/mappers/index.js";
|
import { mapPrismaUserToDomain } from "@bff/infra/mappers/index.js";
|
||||||
import { getCustomFieldValue } from "@customer-portal/domain/customer/providers";
|
import { getCustomFieldValue } from "@customer-portal/domain/customer/providers";
|
||||||
import { safeOperation, OperationCriticality } from "@bff/core/utils/safe-operation.util.js";
|
import { safeOperation, OperationCriticality } from "@bff/core/utils/safe-operation.util.js";
|
||||||
@ -79,9 +80,10 @@ export class WhmcsLinkWorkflowService {
|
|||||||
getCustomFieldValue(clientDetails.customfields, "Customer Number")?.trim();
|
getCustomFieldValue(clientDetails.customfields, "Customer Number")?.trim();
|
||||||
|
|
||||||
if (!customerNumber) {
|
if (!customerNumber) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException({
|
||||||
`Customer Number not found in WHMCS custom field 198. Please contact support.`
|
code: ErrorCode.ACCOUNT_MAPPING_MISSING,
|
||||||
);
|
message: `Customer Number not found in WHMCS custom field 198 for client ${clientNumericId}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.log("Found Customer Number for WHMCS client", {
|
this.logger.log("Found Customer Number for WHMCS client", {
|
||||||
@ -91,7 +93,10 @@ export class WhmcsLinkWorkflowService {
|
|||||||
|
|
||||||
const sfAccount = await this.salesforceService.findAccountByCustomerNumber(customerNumber);
|
const sfAccount = await this.salesforceService.findAccountByCustomerNumber(customerNumber);
|
||||||
if (!sfAccount) {
|
if (!sfAccount) {
|
||||||
throw new BadRequestException("Salesforce account not found. Please contact support.");
|
throw new BadRequestException({
|
||||||
|
code: ErrorCode.CUSTOMER_NOT_FOUND,
|
||||||
|
message: `Salesforce account not found for Customer Number: ${customerNumber}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdUser = await this.usersService.create(
|
const createdUser = await this.usersService.create(
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { Logger } from "nestjs-pino";
|
|||||||
import * as argon2 from "argon2";
|
import * as argon2 from "argon2";
|
||||||
|
|
||||||
import { type MigrateWhmcsAccountRequest } from "@customer-portal/domain/get-started";
|
import { type MigrateWhmcsAccountRequest } from "@customer-portal/domain/get-started";
|
||||||
|
import { ErrorCode } from "@customer-portal/domain/common";
|
||||||
import {
|
import {
|
||||||
getCustomFieldValue,
|
getCustomFieldValue,
|
||||||
serializeWhmcsKeyValueMap,
|
serializeWhmcsKeyValueMap,
|
||||||
@ -112,7 +113,10 @@ export class WhmcsMigrationWorkflowService {
|
|||||||
// Verify WHMCS client still exists and matches session
|
// Verify WHMCS client still exists and matches session
|
||||||
const whmcsClient = await this.whmcsDiscovery.findAccountByEmail(email);
|
const whmcsClient = await this.whmcsDiscovery.findAccountByEmail(email);
|
||||||
if (!whmcsClient || whmcsClient.id !== whmcsClientId) {
|
if (!whmcsClient || whmcsClient.id !== whmcsClientId) {
|
||||||
throw new BadRequestException("WHMCS account verification failed. Please start over.");
|
throw new BadRequestException({
|
||||||
|
code: ErrorCode.VALIDATION_FAILED,
|
||||||
|
message: `WHMCS client mismatch for email ${email}: expected ${whmcsClientId}, got ${whmcsClient?.id ?? "none"}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for existing portal user
|
// Check for existing portal user
|
||||||
@ -124,9 +128,10 @@ export class WhmcsMigrationWorkflowService {
|
|||||||
// Find Salesforce account for mapping
|
// Find Salesforce account for mapping
|
||||||
const sfAccount = await this.findSalesforceAccountForMigration(email, whmcsClientId);
|
const sfAccount = await this.findSalesforceAccountForMigration(email, whmcsClientId);
|
||||||
if (!sfAccount) {
|
if (!sfAccount) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException({
|
||||||
"Unable to find your Salesforce account. Please contact support."
|
code: ErrorCode.CUSTOMER_NOT_FOUND,
|
||||||
);
|
message: `No Salesforce account found for migration: email=${email}, whmcsClientId=${whmcsClientId}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash password for portal storage
|
// Hash password for portal storage
|
||||||
|
|||||||
@ -293,11 +293,7 @@ export const whmcsCurrenciesResponseSchema = z
|
|||||||
result: z.enum(["success", "error"]).optional(),
|
result: z.enum(["success", "error"]).optional(),
|
||||||
message: z.string().optional(),
|
message: z.string().optional(),
|
||||||
errorcode: z.string().optional(),
|
errorcode: z.string().optional(),
|
||||||
totalresults: z
|
totalresults: whmcsOptionalNumber,
|
||||||
.string()
|
|
||||||
.transform(val => Number.parseInt(val, 10))
|
|
||||||
.or(z.number())
|
|
||||||
.optional(),
|
|
||||||
currencies: z
|
currencies: z
|
||||||
.object({
|
.object({
|
||||||
currency: z.array(whmcsCurrencySchema).or(whmcsCurrencySchema),
|
currency: z.array(whmcsCurrencySchema).or(whmcsCurrencySchema),
|
||||||
|
|||||||
@ -208,21 +208,19 @@ const nullableProfileOverrides = nullableProfileFields.reduce<Record<string, z.Z
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const whmcsClientSchema = whmcsRawClientSchema
|
export const whmcsClientSchema = whmcsRawClientSchema.extend({
|
||||||
.extend({
|
...nullableProfileOverrides,
|
||||||
...nullableProfileOverrides,
|
defaultpaymethodid: whmcsOptionalNumber.nullable(),
|
||||||
defaultpaymethodid: whmcsOptionalNumber.nullable(),
|
currency: whmcsOptionalNumber.nullable(),
|
||||||
currency: whmcsOptionalNumber.nullable(),
|
allowSingleSignOn: whmcsOptionalBoolean.nullable(),
|
||||||
allowSingleSignOn: whmcsOptionalBoolean.nullable(),
|
email_verified: whmcsOptionalBoolean.nullable(),
|
||||||
email_verified: whmcsOptionalBoolean.nullable(),
|
marketing_emails_opt_in: whmcsOptionalBoolean.nullable(),
|
||||||
marketing_emails_opt_in: whmcsOptionalBoolean.nullable(),
|
address: addressSchema.nullable().optional(),
|
||||||
address: addressSchema.nullable().optional(),
|
email_preferences: emailPreferencesSchema.nullable().optional(),
|
||||||
email_preferences: emailPreferencesSchema.nullable().optional(),
|
customfields: whmcsCustomFieldsSchema,
|
||||||
customfields: whmcsCustomFieldsSchema,
|
users: whmcsUsersSchema,
|
||||||
users: whmcsUsersSchema,
|
stats: statsSchema.optional(),
|
||||||
stats: statsSchema.optional(),
|
});
|
||||||
})
|
|
||||||
.transform(raw => ({ ...raw }));
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// User Schema (API Response - Normalized camelCase)
|
// User Schema (API Response - Normalized camelCase)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user