forked from Shared/esg
initial commit
This commit is contained in:
239
src/providers/shared/legacy-api.provider.abstract.ts
Normal file
239
src/providers/shared/legacy-api.provider.abstract.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
import axios, { AxiosError, AxiosInstance } from 'axios';
|
||||
import { InquiryType } from '../../common/enums/inquiry-type.enum';
|
||||
import { ProviderName } from '../../common/enums/provider-name.enum';
|
||||
import { ProviderExecutionContext } from '../../common/interfaces/inquiry-provider.interface';
|
||||
import { BaseProvider } from '../base/base-provider.abstract';
|
||||
import {
|
||||
LegacyProviderApiResponse,
|
||||
} from '../interfaces/provider-config.interface';
|
||||
import { ProviderEnvConfig, InquiryConfig } from '../../config/configuration';
|
||||
|
||||
export interface PersonInquiryPayload {
|
||||
nationalCode: string;
|
||||
birthDate: string;
|
||||
dateHasPostfix?: number;
|
||||
}
|
||||
|
||||
export interface PersonInquiryResult {
|
||||
fullName?: string;
|
||||
nationalCode: string;
|
||||
birthDate: string;
|
||||
raw: unknown;
|
||||
}
|
||||
|
||||
export type LegacyInquiryPayload = Record<string, unknown>;
|
||||
export type LegacyInquiryResult = PersonInquiryResult | { raw: unknown };
|
||||
export type LegacyAuthTokenResolver = (
|
||||
providerName: ProviderName,
|
||||
inquiryType: InquiryType,
|
||||
username: string,
|
||||
password: string,
|
||||
) => Promise<string>;
|
||||
|
||||
/**
|
||||
* Shared implementation for providers with REST API contracts (Hamta, Moallem).
|
||||
*
|
||||
* Supports multiple authentication methods:
|
||||
* - AMITIS: Token-based authentication via AMITIS service
|
||||
* - SOAP: Direct SOAP authentication (handled by subclasses)
|
||||
* - NONE: No authentication required
|
||||
*/
|
||||
export abstract class LegacyApiProvider extends BaseProvider<
|
||||
LegacyInquiryPayload,
|
||||
LegacyInquiryResult
|
||||
> {
|
||||
protected readonly httpClient: AxiosInstance;
|
||||
protected readonly providerConfig: ProviderEnvConfig;
|
||||
|
||||
constructor(
|
||||
config: ProviderEnvConfig,
|
||||
private readonly authTokenResolver?: LegacyAuthTokenResolver,
|
||||
) {
|
||||
super(config);
|
||||
this.providerConfig = config;
|
||||
this.httpClient = axios.create({
|
||||
timeout: config.timeout,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
protected async callProvider(
|
||||
inquiryType: InquiryType,
|
||||
payload: LegacyInquiryPayload,
|
||||
_context: ProviderExecutionContext,
|
||||
): Promise<LegacyInquiryResult> {
|
||||
if (inquiryType === InquiryType.PERSON) {
|
||||
return this.inquirePerson(payload as unknown as PersonInquiryPayload, inquiryType);
|
||||
}
|
||||
|
||||
return this.inquireGeneric(inquiryType, payload);
|
||||
}
|
||||
|
||||
protected async inquirePerson(
|
||||
payload: PersonInquiryPayload,
|
||||
inquiryType: InquiryType,
|
||||
): Promise<PersonInquiryResult> {
|
||||
const inquiryConfig = this.getInquiryConfig(inquiryType);
|
||||
|
||||
try {
|
||||
const response = await this.httpClient.post<LegacyProviderApiResponse>(
|
||||
inquiryConfig.url,
|
||||
{
|
||||
nationalCode: payload.nationalCode,
|
||||
birthDate: payload.birthDate,
|
||||
},
|
||||
await this.buildRequestConfig(inquiryType, inquiryConfig),
|
||||
);
|
||||
|
||||
const body = response.data;
|
||||
|
||||
if (!body.success) {
|
||||
throw this.formatProviderError(body.message, body.code);
|
||||
}
|
||||
|
||||
return {
|
||||
nationalCode: payload.nationalCode,
|
||||
birthDate: payload.birthDate,
|
||||
fullName: body.data?.fullName as string | undefined,
|
||||
raw: body,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
const data = error.response?.data as LegacyProviderApiResponse | undefined;
|
||||
throw this.formatProviderError(
|
||||
data?.message ?? error.message,
|
||||
data?.code ?? String(error.response?.status ?? 'NETWORK_ERROR'),
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
protected async inquireGeneric(
|
||||
inquiryType: InquiryType,
|
||||
payload: LegacyInquiryPayload,
|
||||
): Promise<{ raw: unknown }> {
|
||||
const inquiryConfig = this.getInquiryConfig(inquiryType);
|
||||
|
||||
try {
|
||||
const response = await this.httpClient.post<LegacyProviderApiResponse>(
|
||||
inquiryConfig.url,
|
||||
payload,
|
||||
await this.buildRequestConfig(inquiryType, inquiryConfig),
|
||||
);
|
||||
|
||||
const body = response.data;
|
||||
|
||||
// Handle CentInsur API format (IsSucceed/Result)
|
||||
if ('IsSucceed' in body) {
|
||||
if (!body.IsSucceed) {
|
||||
throw this.formatProviderError(
|
||||
body.Result?.ErrorMessage ?? 'Request failed',
|
||||
'API_ERROR',
|
||||
);
|
||||
}
|
||||
return { raw: body };
|
||||
}
|
||||
|
||||
// Handle legacy format (success)
|
||||
if (!body.success) {
|
||||
throw this.formatProviderError(body.message, body.code);
|
||||
}
|
||||
|
||||
return {
|
||||
raw: body,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
const data = error.response?.data as LegacyProviderApiResponse | undefined;
|
||||
|
||||
// Check for CentInsur format error
|
||||
if (data && 'IsSucceed' in data && !data.IsSucceed) {
|
||||
throw this.formatProviderError(
|
||||
data.Result?.ErrorMessage ?? error.message,
|
||||
String(error.response?.status ?? 'API_ERROR'),
|
||||
);
|
||||
}
|
||||
|
||||
throw this.formatProviderError(
|
||||
data?.message ?? error.message,
|
||||
data?.code ?? String(error.response?.status ?? 'NETWORK_ERROR'),
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
protected getInquiryConfig(inquiryType: InquiryType): InquiryConfig {
|
||||
const inquiryConfig = this.providerConfig.inquiries[inquiryType];
|
||||
|
||||
if (!inquiryConfig || !inquiryConfig.url) {
|
||||
throw this.formatProviderError(
|
||||
undefined,
|
||||
undefined,
|
||||
`Inquiry type ${inquiryType} is not configured for ${this.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
return inquiryConfig;
|
||||
}
|
||||
|
||||
private async buildRequestConfig(
|
||||
inquiryType: InquiryType,
|
||||
inquiryConfig: InquiryConfig,
|
||||
): Promise<{
|
||||
headers?: Record<string, string>;
|
||||
}> {
|
||||
// No authentication required
|
||||
if (inquiryConfig.authMethod === 'NONE') {
|
||||
return {};
|
||||
}
|
||||
|
||||
// SOAP authentication is handled by subclasses (e.g., Shahkar)
|
||||
if (inquiryConfig.authMethod === 'SOAP') {
|
||||
return {};
|
||||
}
|
||||
|
||||
// AMITIS token-based authentication
|
||||
if (inquiryConfig.authMethod === 'AMITIS') {
|
||||
if (!this.authTokenResolver) {
|
||||
throw this.formatProviderError(
|
||||
undefined,
|
||||
undefined,
|
||||
`No auth token resolver configured for ${this.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!inquiryConfig.username || !inquiryConfig.password) {
|
||||
throw this.formatProviderError(
|
||||
undefined,
|
||||
undefined,
|
||||
`Credentials not configured for ${this.name}/${inquiryType}`,
|
||||
);
|
||||
}
|
||||
|
||||
const token = await this.authTokenResolver(
|
||||
this.name,
|
||||
inquiryType,
|
||||
inquiryConfig.username,
|
||||
inquiryConfig.password,
|
||||
);
|
||||
|
||||
return {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
throw this.formatProviderError(
|
||||
undefined,
|
||||
undefined,
|
||||
`Unsupported auth method ${inquiryConfig.authMethod} for ${this.name}/${inquiryType}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Made with Bob
|
||||
Reference in New Issue
Block a user