214 lines
7.5 KiB
TypeScript

import { z } from "zod";
import type { Customer, CustomerAddress, CustomerStats } from "../../contract";
import {
customerAddressSchema,
customerEmailPreferencesSchema,
customerSchema,
customerStatsSchema,
customerUserSchema,
} from "../../schema";
import {
whmcsClientSchema,
whmcsClientResponseSchema,
whmcsClientStatsSchema,
type WhmcsClient,
type WhmcsClientResponse,
type WhmcsClientStats,
whmcsCustomFieldSchema,
whmcsUserSchema,
} from "./raw.types";
const toBoolean = (value: unknown): boolean | undefined => {
if (value === undefined || value === null) {
return undefined;
}
if (typeof value === "boolean") {
return value;
}
if (typeof value === "number") {
return value === 1;
}
if (typeof value === "string") {
const normalized = value.trim().toLowerCase();
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
}
return undefined;
};
const toNumber = (value: unknown): number | undefined => {
if (value === undefined || value === null) {
return undefined;
}
if (typeof value === "number") {
return value;
}
const parsed = Number.parseInt(String(value).replace(/[^0-9-]/g, ""), 10);
return Number.isFinite(parsed) ? parsed : undefined;
};
const toString = (value: unknown): string | undefined => {
if (value === undefined || value === null) {
return undefined;
}
return String(value);
};
const normalizeCustomFields = (input: unknown): Record<string, string> | undefined => {
if (!input) {
return undefined;
}
const parsed = whmcsCustomFieldSchema.array().safeParse(input).success
? (input as Array<{ id: number | string; value?: string | null; name?: string }>)
: (() => {
const wrapped = z.object({ customfield: z.union([whmcsCustomFieldSchema, whmcsCustomFieldSchema.array()]) }).safeParse(input);
if (wrapped.success) {
const value = wrapped.data.customfield;
return Array.isArray(value) ? value : [value];
}
return undefined;
})();
if (!parsed) {
return undefined;
}
const result: Record<string, string> = {};
for (const field of parsed) {
const key = field.name || String(field.id);
if (key) {
result[key] = field.value ?? "";
}
}
return Object.keys(result).length > 0 ? result : undefined;
};
const normalizeUsers = (input: unknown): Customer["users"] => {
if (!input) {
return undefined;
}
const parsed = z
.union([whmcsUserSchema, whmcsUserSchema.array()])
.safeParse(input);
if (!parsed.success) {
return undefined;
}
const usersArray = Array.isArray(parsed.data) ? parsed.data : [parsed.data];
const normalize = (value: z.infer<typeof whmcsUserSchema>) => customerUserSchema.parse(value);
const users = usersArray.map(normalize);
return users.length > 0 ? users : undefined;
};
const normalizeAddress = (client: WhmcsClient): CustomerAddress | undefined => {
const address = customerAddressSchema.parse({
address1: client.address1 ?? null,
address2: client.address2 ?? null,
city: client.city ?? null,
state: client.fullstate ?? client.state ?? null,
postcode: client.postcode ?? null,
country: client.country ?? null,
countryCode: client.countrycode ?? null,
phoneNumber: client.phonenumberformatted ?? client.telephoneNumber ?? client.phonenumber ?? null,
phoneCountryCode: client.phonecc ?? null,
});
const hasValues = Object.values(address).some(value => value !== undefined && value !== null && value !== "");
return hasValues ? address : undefined;
};
const normalizeEmailPreferences = (input: unknown) => customerEmailPreferencesSchema.parse(input ?? {});
const normalizeStats = (input: unknown): CustomerStats | undefined => {
const parsed = whmcsClientStatsSchema.safeParse(input);
if (!parsed.success || !parsed.data) {
return undefined;
}
const stats = customerStatsSchema.parse(parsed.data);
return stats;
};
export const parseWhmcsClient = (raw: unknown): WhmcsClient => whmcsClientSchema.parse(raw);
export const parseWhmcsClientResponse = (raw: unknown): WhmcsClientResponse =>
whmcsClientResponseSchema.parse(raw);
export const transformWhmcsClient = (raw: unknown): Customer => {
const client = parseWhmcsClient(raw);
const customer: Customer = {
id: Number(client.id),
clientId: client.client_id ? Number(client.client_id) : client.userid ? Number(client.userid) : undefined,
ownerUserId: client.owner_user_id ? Number(client.owner_user_id) : undefined,
userId: client.userid ? Number(client.userid) : undefined,
uuid: client.uuid ?? null,
firstname: client.firstname ?? null,
lastname: client.lastname ?? null,
fullname: client.fullname ?? null,
companyName: client.companyname ?? null,
email: client.email,
status: client.status ?? null,
language: client.language ?? null,
defaultGateway: client.defaultgateway ?? null,
defaultPaymentMethodId: client.defaultpaymethodid ? Number(client.defaultpaymethodid) : undefined,
currencyId: client.currency !== undefined ? toNumber(client.currency) : undefined,
currencyCode: client.currency_code ?? null,
taxId: client.tax_id ?? null,
phoneNumber: client.phonenumberformatted ?? client.telephoneNumber ?? client.phonenumber ?? null,
phoneCountryCode: client.phonecc ?? null,
telephoneNumber: client.telephoneNumber ?? null,
allowSingleSignOn: toBoolean(client.allowSingleSignOn) ?? null,
emailVerified: toBoolean(client.email_verified) ?? null,
marketingEmailsOptIn:
toBoolean(client.isOptedInToMarketingEmails) ?? toBoolean(client.marketing_emails_opt_in) ?? null,
notes: client.notes ?? null,
createdAt: client.datecreated ?? null,
lastLogin: client.lastlogin ?? null,
address: normalizeAddress(client),
emailPreferences: normalizeEmailPreferences(client.email_preferences),
customFields: normalizeCustomFields(client.customfields),
users: normalizeUsers(client.users?.user ?? client.users),
raw: client,
};
return customerSchema.parse(customer);
};
export const transformWhmcsClientResponse = (raw: unknown): Customer => {
const parsed = parseWhmcsClientResponse(raw);
const customer = transformWhmcsClient(parsed.client);
const stats = normalizeStats(parsed.stats);
if (stats) {
customer.stats = stats;
}
customer.raw = parsed;
return customerSchema.parse(customer);
};
export const transformWhmcsClientAddress = (raw: unknown): CustomerAddress | undefined => {
const client = parseWhmcsClient(raw);
return normalizeAddress(client);
};
export const transformWhmcsClientStats = (raw: unknown): CustomerStats | undefined => normalizeStats(raw);
export const prepareWhmcsClientAddressUpdate = (
address: Partial<CustomerAddress>
): Record<string, unknown> => {
const update: Record<string, unknown> = {};
if (address.address1 !== undefined) update.address1 = address.address1 ?? "";
if (address.address2 !== undefined) update.address2 = address.address2 ?? "";
if (address.city !== undefined) update.city = address.city ?? "";
if (address.state !== undefined) update.state = address.state ?? "";
if (address.postcode !== undefined) update.postcode = address.postcode ?? "";
if (address.country !== undefined) update.country = address.country ?? "";
if (address.countryCode !== undefined) update.countrycode = address.countryCode ?? "";
if (address.phoneNumber !== undefined) update.phonenumber = address.phoneNumber ?? "";
if (address.phoneCountryCode !== undefined) update.phonecc = address.phoneCountryCode ?? "";
return update;
};