Refactor Freebit integration services to enhance type safety and maintainability by utilizing updated provider methods and removing deprecated types. Update authentication and mapping logic to align with the new domain structure, improving data handling consistency across services. Introduce new response transformation methods for Freebit authentication, ensuring robust error handling and streamlined integration.

This commit is contained in:
barsa 2025-10-03 17:13:02 +09:00
parent 12c3dc976f
commit c206598615
6 changed files with 75 additions and 48 deletions

View File

@ -3,12 +3,21 @@ import { ConfigService } from "@nestjs/config";
import { Logger } from "nestjs-pino";
import { getErrorMessage } from "@bff/core/utils/error.util";
import type {
FreebitConfig,
FreebitAuthRequest,
FreebitAuthResponse,
AuthRequest as FreebitAuthRequest,
AuthResponse as FreebitAuthResponse,
} from "@customer-portal/domain/sim/providers/freebit";
import { Providers } from "@customer-portal/domain/sim";
import { FreebitError } from "./freebit-error.service";
interface FreebitConfig {
baseUrl: string;
oemId: string;
oemKey: string;
timeout: number;
retryAttempts: number;
detailsEndpoint?: string;
}
@Injectable()
export class FreebitAuthService {
private readonly config: FreebitConfig;
@ -60,10 +69,10 @@ export class FreebitAuthService {
throw new Error("Freebit API not configured: FREEBIT_OEM_KEY is missing");
}
const request: FreebitAuthRequest = {
const request = Providers.Freebit.schemas.auth.parse({
oemId: this.config.oemId,
oemKey: this.config.oemKey,
};
});
const response = await fetch(`${this.config.baseUrl}/authOem/`, {
method: "POST",
@ -75,13 +84,15 @@ export class FreebitAuthService {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = (await response.json()) as FreebitAuthResponse;
if (data.resultCode !== "100") {
const json = (await response.json()) as unknown;
const data = Providers.Freebit.mapper.transformFreebitAuthResponse(json);
if (data.resultCode !== "100" || !data.authKey) {
throw new FreebitError(
`Authentication failed: ${data.status.message}`,
`Authentication failed: ${data.status?.message ?? "Unknown error"}`,
data.resultCode,
data.status.statusCode,
data.status.message
data.status?.statusCode,
data.status?.message
);
}

View File

@ -1,9 +1,4 @@
import { Injectable } from "@nestjs/common";
import type {
FreebitAccountDetailsResponse,
FreebitTrafficInfoResponse,
FreebitQuotaHistoryResponse,
} from "@customer-portal/domain/sim/providers/freebit";
import type { SimDetails, SimTopUpHistory, SimUsage } from "@customer-portal/domain/sim";
import { Providers } from "@customer-portal/domain/sim";
@ -12,21 +7,21 @@ export class FreebitMapperService {
/**
* Map Freebit account details response to SimDetails
*/
mapToSimDetails(response: FreebitAccountDetailsResponse): SimDetails {
mapToSimDetails(response: unknown): SimDetails {
return Providers.Freebit.transformFreebitAccountDetails(response);
}
/**
* Map Freebit traffic info response to SimUsage
*/
mapToSimUsage(response: FreebitTrafficInfoResponse): SimUsage {
mapToSimUsage(response: unknown): SimUsage {
return Providers.Freebit.transformFreebitTrafficInfo(response);
}
/**
* Map Freebit quota history response to SimTopUpHistory
*/
mapToSimTopUpHistory(response: FreebitQuotaHistoryResponse, account: string): SimTopUpHistory {
mapToSimTopUpHistory(response: unknown, account: string): SimTopUpHistory {
return Providers.Freebit.transformFreebitQuotaHistory(response, account);
}

View File

@ -5,16 +5,15 @@ import { FreebitClientService } from "./freebit-client.service";
import { FreebitMapperService } from "./freebit-mapper.service";
import { FreebitAuthService } from "./freebit-auth.service";
import type {
FreebitAccountDetailsResponse,
FreebitTrafficInfoResponse,
FreebitTopUpResponse,
FreebitQuotaHistoryResponse,
FreebitPlanChangeResponse,
FreebitAddSpecResponse,
FreebitCancelPlanResponse,
FreebitEsimReissueResponse,
FreebitEsimAddAccountResponse,
FreebitEsimAccountActivationResponse,
TopUpResponse,
PlanChangeResponse,
AddSpecResponse,
CancelPlanResponse,
CancelAccountResponse,
EsimReissueResponse,
EsimAddAccountResponse,
EsimActivationResponse,
QuotaHistoryRequest,
} from "@customer-portal/domain/sim/providers/freebit";
import type {
FreebitTopUpRequest,
@ -73,7 +72,7 @@ export class FreebitOperationsService {
])
);
let response: FreebitAccountDetailsResponse | undefined;
let response: Providers.Freebit.mapper.FreebitAccountDetailsResponse | undefined;
let lastError: unknown;
for (const ep of candidates) {
@ -82,7 +81,7 @@ export class FreebitOperationsService {
this.logger.warn(`Retrying Freebit account details with alternative endpoint: ${ep}`);
}
response = await this.client.makeAuthenticatedRequest<
FreebitAccountDetailsResponse,
Providers.Freebit.mapper.FreebitAccountDetailsResponse,
typeof request
>(ep, request);
break;
@ -120,7 +119,7 @@ export class FreebitOperationsService {
const request: FreebitTrafficInfoRequest = Providers.Freebit.schemas.trafficInfo.parse({ account });
const response = await this.client.makeAuthenticatedRequest<
FreebitTrafficInfoResponse,
Providers.Freebit.mapper.FreebitTrafficInfoResponse,
typeof request
>("/mvno/getTrafficInfo/", request);
@ -159,7 +158,7 @@ export class FreebitOperationsService {
? { ...baseRequest, runTime: payload.options?.scheduledAt }
: baseRequest;
await this.client.makeAuthenticatedRequest<FreebitTopUpResponse, typeof request>(
await this.client.makeAuthenticatedRequest<TopUpResponse, typeof request>(
endpoint,
request
);
@ -198,8 +197,8 @@ export class FreebitOperationsService {
});
const response = await this.client.makeAuthenticatedRequest<
FreebitQuotaHistoryResponse,
FreebitQuotaHistoryRequest
Providers.Freebit.mapper.FreebitQuotaHistoryResponse,
QuotaHistoryRequest
>("/mvno/getQuotaHistory/", request);
return this.mapper.mapToSimTopUpHistory(response, account);
@ -239,10 +238,10 @@ export class FreebitOperationsService {
runTime: parsed.scheduledAt,
};
const response = await this.client.makeAuthenticatedRequest<
FreebitPlanChangeResponse,
typeof request
>("/mvno/changePlan/", request);
const response = await this.client.makeAuthenticatedRequest<PlanChangeResponse, typeof request>(
"/mvno/changePlan/",
request
);
this.logger.log(`Successfully changed plan for account ${parsed.account} to ${parsed.newPlanCode}`, {
account: parsed.account,
@ -314,7 +313,7 @@ export class FreebitOperationsService {
payload.contractLine = features.networkType;
}
await this.client.makeAuthenticatedRequest<FreebitAddSpecResponse, typeof payload>(
await this.client.makeAuthenticatedRequest<AddSpecResponse, typeof payload>(
"/master/addSpec/",
payload
);
@ -352,7 +351,7 @@ export class FreebitOperationsService {
runTime: parsed.runDate,
};
await this.client.makeAuthenticatedRequest<FreebitCancelPlanResponse, typeof request>(
await this.client.makeAuthenticatedRequest<CancelPlanResponse, typeof request>(
"/mvno/releasePlan/",
request
);
@ -381,7 +380,7 @@ export class FreebitOperationsService {
requestDatas: [{ kind: "MVNO", account }],
};
await this.client.makeAuthenticatedRequest<FreebitEsimReissueResponse, typeof request>(
await this.client.makeAuthenticatedRequest<EsimReissueResponse, typeof request>(
"/mvno/reissueEsim/",
request
);
@ -422,14 +421,14 @@ export class FreebitOperationsService {
planCode: parsed.planCode,
});
const request: FreebitEsimAddAccountRequest = {
const payload: FreebitEsimAddAccountRequest = {
...requestPayload,
authKey: await this.auth.getAuthKey(),
};
await this.client.makeAuthenticatedRequest<FreebitEsimAddAccountResponse, FreebitEsimAddAccountRequest>(
await this.client.makeAuthenticatedRequest<EsimAddAccountResponse, FreebitEsimAddAccountRequest>(
"/mvno/esim/addAcnt/",
request
payload
);
this.logger.log(`Successfully reissued eSIM profile via addAcnt for account ${parsed.account}`, {
@ -515,10 +514,10 @@ export class FreebitOperationsService {
Providers.Freebit.schemas.esimActivationRequest.parse(payload);
// Use JSON request for PA05-41
await this.client.makeAuthenticatedJsonRequest<
FreebitEsimAccountActivationResponse,
typeof payload
>("/mvno/esim/addAcct/", payload);
await this.client.makeAuthenticatedJsonRequest<EsimActivationResponse, typeof payload>(
"/mvno/esim/addAcct/",
payload
);
this.logger.log("Successfully activated new eSIM account via PA05-41", {
account,

View File

@ -47,6 +47,7 @@ export type CancelAccountResponse = ReturnType<typeof Mapper.transformFreebitCan
export type EsimReissueResponse = ReturnType<typeof Mapper.transformFreebitEsimReissueResponse>;
export type EsimAddAccountResponse = ReturnType<typeof Mapper.transformFreebitEsimAddAccountResponse>;
export type EsimActivationResponse = ReturnType<typeof Mapper.transformFreebitEsimActivationResponse>;
export type AuthResponse = ReturnType<typeof Mapper.transformFreebitAuthResponse>;
export * from "./mapper";
export * from "./raw.types";

View File

@ -8,6 +8,7 @@ import {
type FreebitAccountDetailsRaw,
type FreebitTrafficInfoRaw,
type FreebitQuotaHistoryRaw,
type FreebitAuthResponseRaw,
type FreebitTopUpRaw,
type FreebitAddSpecRaw,
type FreebitPlanChangeRaw,
@ -18,6 +19,7 @@ import {
freebitAccountDetailsRawSchema,
freebitTrafficInfoRawSchema,
freebitQuotaHistoryRawSchema,
freebitAuthResponseRawSchema,
freebitTopUpRawSchema,
freebitAddSpecRawSchema,
freebitPlanChangeRawSchema,
@ -206,4 +208,10 @@ export function transformFreebitEsimActivationResponse(raw: unknown) {
return freebitEsimAddAccountRawSchema.parse(raw);
}
export type FreebitEsimActivationResponse = ReturnType<typeof transformFreebitEsimActivationResponse>;
export function transformFreebitAuthResponse(raw: unknown): FreebitAuthResponseRaw {
return freebitAuthResponseRawSchema.parse(raw);
}

View File

@ -166,3 +166,16 @@ export const freebitQuotaHistoryRawSchema = z.object({
export type FreebitQuotaHistoryRaw = z.infer<typeof freebitQuotaHistoryRawSchema>;
export const freebitAuthResponseRawSchema = z.object({
resultCode: z.string().optional(),
status: z
.object({
message: z.string().optional(),
statusCode: z.union([z.string(), z.number()]).optional(),
})
.optional(),
authKey: z.string().optional(),
});
export type FreebitAuthResponseRaw = z.infer<typeof freebitAuthResponseRawSchema>;