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

View File

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

View File

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

View File

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