217 lines
7.2 KiB
TypeScript
Raw Normal View History

/**
* Freebit SIM Provider - Mapper
*/
import type { SimDetails, SimUsage, SimTopUpHistory, SimType, SimStatus } from "../../contract.js";
import { simDetailsSchema, simUsageSchema, simTopUpHistorySchema } from "../../schema.js";
import {
type FreebitAccountDetailsRaw,
type FreebitTrafficInfoRaw,
type FreebitQuotaHistoryRaw,
type FreebitAuthResponseRaw,
type FreebitTopUpRaw,
type FreebitAddSpecRaw,
type FreebitPlanChangeRaw,
type FreebitCancelPlanRaw,
type FreebitCancelAccountRaw,
type FreebitEsimReissueRaw,
type FreebitEsimAddAccountRaw,
freebitAccountDetailsRawSchema,
freebitTrafficInfoRawSchema,
freebitQuotaHistoryRawSchema,
freebitAuthResponseRawSchema,
freebitTopUpRawSchema,
freebitAddSpecRawSchema,
freebitPlanChangeRawSchema,
freebitCancelPlanRawSchema,
freebitCancelAccountRawSchema,
freebitEsimReissueRawSchema,
freebitEsimAddAccountRawSchema,
} from "./raw.types.js";
import { normalizeAccount } from "./utils.js";
function asString(value: unknown): string {
if (typeof value === "string") return value;
if (typeof value === "number") return String(value);
return "";
}
function asNumber(value: unknown): number {
if (typeof value === "number") return value;
if (typeof value === "string") {
const parsed = parseFloat(value);
return isNaN(parsed) ? 0 : parsed;
}
return 0;
}
function parseBooleanFlag(value: unknown): boolean {
if (typeof value === "boolean") return value;
if (typeof value === "number") return value === 10;
if (typeof value === "string") return value === "10" || value.toLowerCase() === "true";
return false;
}
function mapSimStatus(status: string | undefined): SimStatus {
if (!status) return "pending";
const normalized = status.toLowerCase();
if (normalized.includes("active") || normalized === "10") return "active";
if (normalized.includes("suspend")) return "suspended";
if (normalized.includes("cancel") || normalized.includes("terminate")) return "cancelled";
return "pending";
}
function deriveSimType(sizeValue: unknown, eid?: string | number | null): SimType {
const simSizeStr = typeof sizeValue === "number" ? String(sizeValue) : sizeValue;
const raw = typeof simSizeStr === "string" ? simSizeStr.toLowerCase() : undefined;
const eidStr = typeof eid === "number" ? String(eid) : eid;
if (eidStr && eidStr.length > 0) {
return "esim";
}
switch (raw) {
case "nano":
return "nano";
case "micro":
return "micro";
case "esim":
return "esim";
default:
return "standard";
}
}
export function transformFreebitAccountDetails(raw: unknown): SimDetails {
const response = freebitAccountDetailsRawSchema.parse(raw);
const account = response.responseDatas.at(0);
if (!account) {
throw new Error("Freebit account details missing response data");
}
const sanitizedAccount = asString(account.account);
const legacySize = "size" in account ? (account as { size?: unknown }).size : undefined;
const simSizeValue = account.simSize ?? legacySize;
const eidValue = account.eid;
const simType = deriveSimType(
typeof simSizeValue === "number" ? String(simSizeValue) : simSizeValue,
typeof eidValue === "number" ? String(eidValue) : eidValue
);
const voiceMailEnabled = parseBooleanFlag(account.voicemail ?? account.voiceMail);
const callWaitingEnabled = parseBooleanFlag(account.callwaiting ?? account.callWaiting);
const internationalRoamingEnabled = parseBooleanFlag(account.worldwing ?? account.worldWing);
const simDetails: SimDetails = {
account: sanitizedAccount,
status: mapSimStatus(account.status),
planCode: asString(account.planCode),
planName: asString(account.planName),
simType,
iccid: asString(account.iccid),
eid: asString(eidValue),
msisdn: asString(account.msisdn),
imsi: asString(account.imsi),
remainingQuotaMb: asNumber(account.quota),
remainingQuotaKb: asNumber(account.quotaKb),
voiceMailEnabled,
callWaitingEnabled,
internationalRoamingEnabled,
networkType: asString(account.contractLine),
activatedAt: asString(account.startDate) || undefined,
expiresAt: asString(account.expireDate) || undefined,
};
return simDetailsSchema.parse(simDetails);
}
export function transformFreebitTrafficInfo(raw: unknown): SimUsage {
const response = freebitTrafficInfoRawSchema.parse(raw);
const simUsage: SimUsage = {
account: asString(response.account),
todayUsageMb: response.traffic?.today ? asNumber(response.traffic.today) / 1024 : 0,
todayUsageKb: response.traffic?.today ? asNumber(response.traffic.today) : 0,
monthlyUsageMb: undefined,
monthlyUsageKb: undefined,
recentDaysUsage: [],
isBlacklisted: parseBooleanFlag(response.traffic?.blackList),
lastUpdated: new Date().toISOString(),
};
return simUsageSchema.parse(simUsage);
}
export function transformFreebitQuotaHistory(
raw: unknown,
accountOverride?: string
): SimTopUpHistory {
const response = freebitQuotaHistoryRawSchema.parse(raw);
const history: SimTopUpHistory = {
account: accountOverride ?? asString(response.account),
totalAdditions: asNumber(response.total),
additionCount: asNumber(response.count),
history: (response.quotaHistory || []).map(detail => ({
quotaKb: asNumber(detail.addQuotaKb),
quotaMb: asNumber(detail.addQuotaKb) / 1024,
addedDate: detail.addDate || "",
expiryDate: detail.expireDate || "",
campaignCode: detail.campaignCode || "",
})),
};
return simTopUpHistorySchema.parse(history);
}
export function transformFreebitTopUpResponse(raw: unknown) {
return freebitTopUpRawSchema.parse(raw);
}
export type FreebitTopUpResponse = ReturnType<typeof transformFreebitTopUpResponse>;
export function transformFreebitAddSpecResponse(raw: unknown) {
return freebitAddSpecRawSchema.parse(raw);
}
export type FreebitAddSpecResponse = ReturnType<typeof transformFreebitAddSpecResponse>;
export function transformFreebitPlanChangeResponse(raw: unknown) {
return freebitPlanChangeRawSchema.parse(raw);
}
export type FreebitPlanChangeResponse = ReturnType<typeof transformFreebitPlanChangeResponse>;
export function transformFreebitCancelPlanResponse(raw: unknown) {
return freebitCancelPlanRawSchema.parse(raw);
}
export type FreebitCancelPlanResponse = ReturnType<typeof transformFreebitCancelPlanResponse>;
export function transformFreebitCancelAccountResponse(raw: unknown) {
return freebitCancelAccountRawSchema.parse(raw);
}
export type FreebitCancelAccountResponse = ReturnType<typeof transformFreebitCancelAccountResponse>;
export function transformFreebitEsimReissueResponse(raw: unknown) {
return freebitEsimReissueRawSchema.parse(raw);
}
export type FreebitEsimReissueResponse = ReturnType<typeof transformFreebitEsimReissueResponse>;
export function transformFreebitEsimAddAccountResponse(raw: unknown) {
return freebitEsimAddAccountRawSchema.parse(raw);
}
export type FreebitEsimAddAccountResponse = ReturnType<
typeof transformFreebitEsimAddAccountResponse
>;
export function transformFreebitEsimActivationResponse(raw: unknown) {
return freebitEsimAddAccountRawSchema.parse(raw);
}
export function transformFreebitAuthResponse(raw: unknown): FreebitAuthResponseRaw {
return freebitAuthResponseRawSchema.parse(raw);
}