initial commit

This commit is contained in:
2026-06-09 14:07:37 +03:30
parent 30ac533800
commit 996a4fcda7
121 changed files with 20557 additions and 3 deletions

View 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