forked from Yara724/api
YARA-1035
This commit is contained in:
97
src/client/client-external-inquiries.controller.ts
Normal file
97
src/client/client-external-inquiries.controller.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Patch,
|
||||
Put,
|
||||
} from "@nestjs/common";
|
||||
import {
|
||||
ApiBody,
|
||||
ApiOperation,
|
||||
ApiParam,
|
||||
ApiResponse,
|
||||
ApiTags,
|
||||
} from "@nestjs/swagger";
|
||||
import { ClientService } from "./client.service";
|
||||
import {
|
||||
ClientExternalInquiriesCatalogDto,
|
||||
ClientExternalInquiriesListDto,
|
||||
ClientExternalInquiriesViewDto,
|
||||
ExternalInquiryFlagsDto,
|
||||
UpdateClientExternalInquiriesDto,
|
||||
} from "./dto/client-external-inquiries.dto";
|
||||
|
||||
/**
|
||||
* Per-insurer external inquiry toggles (public for now — lock down when super-admin exists).
|
||||
*/
|
||||
@ApiTags("client-external-inquiries")
|
||||
@Controller("client")
|
||||
export class ClientExternalInquiriesController {
|
||||
constructor(private readonly clientService: ClientService) {}
|
||||
|
||||
@Get("external-inquiries/catalog")
|
||||
@ApiOperation({
|
||||
summary: "List supported external inquiry kinds and API paths",
|
||||
description: "No auth (temporary). Describes global master switch + per-client flags.",
|
||||
})
|
||||
@ApiResponse({ status: 200, type: ClientExternalInquiriesCatalogDto })
|
||||
getCatalog(): ClientExternalInquiriesCatalogDto {
|
||||
return this.clientService.getExternalInquiriesCatalog();
|
||||
}
|
||||
|
||||
@Get("external-inquiries")
|
||||
@ApiOperation({
|
||||
summary: "List external inquiry settings for all insurers",
|
||||
description:
|
||||
"No auth (temporary). Returns stored flags and effective live flags (after global master switch).",
|
||||
})
|
||||
@ApiResponse({ status: 200, type: ClientExternalInquiriesListDto })
|
||||
listAll(): Promise<ClientExternalInquiriesListDto> {
|
||||
return this.clientService.listExternalInquirySettings();
|
||||
}
|
||||
|
||||
@Get(":clientId/external-inquiries")
|
||||
@ApiOperation({
|
||||
summary: "Get external inquiry settings for one insurer",
|
||||
description: "No auth (temporary).",
|
||||
})
|
||||
@ApiParam({ name: "clientId", description: "Insurer client Mongo ObjectId" })
|
||||
@ApiResponse({ status: 200, type: ClientExternalInquiriesViewDto })
|
||||
getOne(
|
||||
@Param("clientId") clientId: string,
|
||||
): Promise<ClientExternalInquiriesViewDto> {
|
||||
return this.clientService.getExternalInquirySettings(clientId);
|
||||
}
|
||||
|
||||
@Put(":clientId/external-inquiries")
|
||||
@ApiOperation({
|
||||
summary: "Replace external inquiry flags for one insurer",
|
||||
description:
|
||||
"No auth (temporary). Omitted inquiry keys default to `false` (mock). Global master switch still applies at runtime.",
|
||||
})
|
||||
@ApiParam({ name: "clientId", description: "Insurer client Mongo ObjectId" })
|
||||
@ApiBody({ type: ExternalInquiryFlagsDto })
|
||||
@ApiResponse({ status: 200, type: ClientExternalInquiriesViewDto })
|
||||
replace(
|
||||
@Param("clientId") clientId: string,
|
||||
@Body() body: ExternalInquiryFlagsDto,
|
||||
): Promise<ClientExternalInquiriesViewDto> {
|
||||
return this.clientService.replaceExternalInquirySettings(clientId, body);
|
||||
}
|
||||
|
||||
@Patch(":clientId/external-inquiries")
|
||||
@ApiOperation({
|
||||
summary: "Partially update external inquiry flags for one insurer",
|
||||
description: "No auth (temporary). Only supplied flags are changed.",
|
||||
})
|
||||
@ApiParam({ name: "clientId", description: "Insurer client Mongo ObjectId" })
|
||||
@ApiBody({ type: UpdateClientExternalInquiriesDto })
|
||||
@ApiResponse({ status: 200, type: ClientExternalInquiriesViewDto })
|
||||
patch(
|
||||
@Param("clientId") clientId: string,
|
||||
@Body() body: UpdateClientExternalInquiriesDto,
|
||||
): Promise<ClientExternalInquiriesViewDto> {
|
||||
return this.clientService.patchExternalInquirySettings(clientId, body);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ import { Module } from "@nestjs/common";
|
||||
import { MongooseModule } from "@nestjs/mongoose";
|
||||
import { SystemSettingsModule } from "src/system-settings/system-settings.module";
|
||||
import { ClientController } from "./client.controller";
|
||||
import { ClientExternalInquiriesController } from "./client-external-inquiries.controller";
|
||||
import { ClientPanelController } from "./client-panel.controller";
|
||||
import { ClientService } from "./client.service";
|
||||
import { ExternalInquirySettingsService } from "./external-inquiry-settings.service";
|
||||
import { BranchDbService } from "./entities/db-service/branch.db.service";
|
||||
import { ClientDbService } from "./entities/db-service/client.db.service";
|
||||
import { BranchModel, BranchSchema } from "./entities/schema/branch.schema";
|
||||
@@ -20,8 +22,12 @@ import { ClientDbSchema, ClientModel } from "./entities/schema/client.schema";
|
||||
},
|
||||
]),
|
||||
],
|
||||
controllers: [ClientController, ClientPanelController],
|
||||
providers: [ClientService, ClientDbService, BranchDbService],
|
||||
exports: [ClientService, ClientDbService, BranchDbService],
|
||||
controllers: [
|
||||
ClientController,
|
||||
ClientPanelController,
|
||||
ClientExternalInquiriesController,
|
||||
],
|
||||
providers: [ClientService, ClientDbService, BranchDbService, ExternalInquirySettingsService],
|
||||
exports: [ClientService, ClientDbService, BranchDbService, ExternalInquirySettingsService],
|
||||
})
|
||||
export class ClientModule {}
|
||||
|
||||
@@ -17,6 +17,19 @@ import {
|
||||
} from "src/client/dto/client-settings.dto";
|
||||
import type { ClientMediaLimits } from "./entities/schema/client.schema";
|
||||
import { ClientDbService } from "./entities/db-service/client.db.service";
|
||||
import {
|
||||
ClientExternalInquiriesCatalogDto,
|
||||
ClientExternalInquiriesListDto,
|
||||
ClientExternalInquiriesViewDto,
|
||||
toClientExternalInquiriesView,
|
||||
UpdateClientExternalInquiriesDto,
|
||||
} from "./dto/client-external-inquiries.dto";
|
||||
import {
|
||||
EXTERNAL_INQUIRY_TYPES,
|
||||
mergeExternalInquiryFlags,
|
||||
} from "src/common/types/external-inquiry.types";
|
||||
import { ExternalInquirySettingsService } from "./external-inquiry-settings.service";
|
||||
import { SystemSettingsService } from "src/system-settings/system-settings.service";
|
||||
|
||||
/**
|
||||
* System-wide default applied when a client document has no
|
||||
@@ -94,7 +107,11 @@ const MEDIA_KINDS: MediaKind[] = ["video", "image", "voice"];
|
||||
|
||||
@Injectable()
|
||||
export class ClientService {
|
||||
constructor(private readonly clientDbService: ClientDbService) {}
|
||||
constructor(
|
||||
private readonly clientDbService: ClientDbService,
|
||||
private readonly externalInquirySettingsService: ExternalInquirySettingsService,
|
||||
private readonly systemSettingsService: SystemSettingsService,
|
||||
) {}
|
||||
async addClient(client: ClientDto): Promise<ClientDtoRs> {
|
||||
try {
|
||||
const newClient = await this.clientDbService.create({
|
||||
@@ -393,4 +410,74 @@ export class ClientService {
|
||||
|
||||
return this.getPanelSettings(client._id);
|
||||
}
|
||||
|
||||
getExternalInquiriesCatalog(): ClientExternalInquiriesCatalogDto {
|
||||
return {
|
||||
inquiryTypes: [...EXTERNAL_INQUIRY_TYPES],
|
||||
globalSettingPath: "/system-settings (PATCH externalApis.sandHubUseLiveApi)",
|
||||
perClientSettingPath: "/client/{clientId}/external-inquiries",
|
||||
};
|
||||
}
|
||||
|
||||
async listExternalInquirySettings(): Promise<ClientExternalInquiriesListDto> {
|
||||
const global = await this.systemSettingsService.isSandHubLiveEnabled();
|
||||
const clients = await this.clientDbService.findAll();
|
||||
return {
|
||||
items: clients.map((c) => toClientExternalInquiriesView(c, global)),
|
||||
};
|
||||
}
|
||||
|
||||
async getExternalInquirySettings(
|
||||
clientKey: string | Types.ObjectId,
|
||||
): Promise<ClientExternalInquiriesViewDto> {
|
||||
const client = await this.loadClientOrThrow(clientKey);
|
||||
const global = await this.systemSettingsService.isSandHubLiveEnabled();
|
||||
return toClientExternalInquiriesView(client, global);
|
||||
}
|
||||
|
||||
async replaceExternalInquirySettings(
|
||||
clientKey: string | Types.ObjectId,
|
||||
body: UpdateClientExternalInquiriesDto,
|
||||
): Promise<ClientExternalInquiriesViewDto> {
|
||||
const client = await this.loadClientOrThrow(clientKey);
|
||||
const flags = mergeExternalInquiryFlags(body as Partial<Record<string, boolean>>);
|
||||
const $set: Record<string, unknown> = {};
|
||||
for (const key of EXTERNAL_INQUIRY_TYPES) {
|
||||
$set[`settings.externalInquiries.${key}`] = flags[key];
|
||||
}
|
||||
const updated = await this.clientDbService.findByIdAndUpdate(
|
||||
client._id.toString(),
|
||||
{ $set },
|
||||
);
|
||||
if (!updated) throw new NotFoundException("Client not found");
|
||||
this.externalInquirySettingsService.invalidateClientCache(
|
||||
client._id.toString(),
|
||||
);
|
||||
return this.getExternalInquirySettings(client._id);
|
||||
}
|
||||
|
||||
async patchExternalInquirySettings(
|
||||
clientKey: string | Types.ObjectId,
|
||||
body: UpdateClientExternalInquiriesDto,
|
||||
): Promise<ClientExternalInquiriesViewDto> {
|
||||
const client = await this.loadClientOrThrow(clientKey);
|
||||
const $set: Record<string, unknown> = {};
|
||||
for (const key of EXTERNAL_INQUIRY_TYPES) {
|
||||
if (body[key] !== undefined) {
|
||||
$set[`settings.externalInquiries.${key}`] = body[key];
|
||||
}
|
||||
}
|
||||
if (Object.keys($set).length === 0) {
|
||||
throw new BadRequestException("No external inquiry flags to update.");
|
||||
}
|
||||
const updated = await this.clientDbService.findByIdAndUpdate(
|
||||
client._id.toString(),
|
||||
{ $set },
|
||||
);
|
||||
if (!updated) throw new NotFoundException("Client not found");
|
||||
this.externalInquirySettingsService.invalidateClientCache(
|
||||
client._id.toString(),
|
||||
);
|
||||
return this.getExternalInquirySettings(client._id);
|
||||
}
|
||||
}
|
||||
|
||||
154
src/client/dto/client-external-inquiries.dto.ts
Normal file
154
src/client/dto/client-external-inquiries.dto.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import { ApiProperty, ApiPropertyOptional, PartialType } from "@nestjs/swagger";
|
||||
import {
|
||||
EXTERNAL_INQUIRY_TYPES,
|
||||
ExternalInquiryFlags,
|
||||
mergeExternalInquiryFlags,
|
||||
} from "src/common/types/external-inquiry.types";
|
||||
import { IsBoolean, IsMongoId, IsOptional, ValidateNested } from "class-validator";
|
||||
import { Type } from "class-transformer";
|
||||
|
||||
export class ExternalInquiryFlagsDto implements ExternalInquiryFlags {
|
||||
@ApiProperty({
|
||||
description: "Third-party plate / policy block inquiry (Tejarat or ESG policyByPlate).",
|
||||
example: false,
|
||||
})
|
||||
@IsBoolean()
|
||||
thirdPartyPlate: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: "CAR_BODY (badane) plate inquiry.",
|
||||
example: false,
|
||||
})
|
||||
@IsBoolean()
|
||||
carBodyPlate: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Personal identity inquiry (national code + birth date).",
|
||||
example: false,
|
||||
})
|
||||
@IsBoolean()
|
||||
personalIdentity: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Sheba account validation.",
|
||||
example: false,
|
||||
})
|
||||
@IsBoolean()
|
||||
sheba: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Driving licence check.",
|
||||
example: false,
|
||||
})
|
||||
@IsBoolean()
|
||||
drivingLicense: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Vehicle ownership validation.",
|
||||
example: false,
|
||||
})
|
||||
@IsBoolean()
|
||||
carOwnership: boolean;
|
||||
}
|
||||
|
||||
export class UpdateClientExternalInquiriesDto extends PartialType(
|
||||
ExternalInquiryFlagsDto,
|
||||
) {}
|
||||
|
||||
export class ClientExternalInquiriesViewDto {
|
||||
@ApiProperty({ example: "664a1b2c3d4e5f6789012345" })
|
||||
clientId: string;
|
||||
|
||||
@ApiProperty({ example: 8 })
|
||||
clientCode: number;
|
||||
|
||||
@ApiProperty({ example: "بیمه پارسیان" })
|
||||
clientName: string;
|
||||
|
||||
@ApiProperty({
|
||||
description:
|
||||
"Global master switch from `system_settings.externalApis.sandHubUseLiveApi`. When false, all inquiries use mocks regardless of these flags.",
|
||||
})
|
||||
globalSandHubLiveEnabled: boolean;
|
||||
|
||||
@ApiProperty({ type: ExternalInquiryFlagsDto })
|
||||
externalInquiries: ExternalInquiryFlags;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Effective live flags after applying the global master switch.",
|
||||
type: ExternalInquiryFlagsDto,
|
||||
})
|
||||
effectiveLive: ExternalInquiryFlags;
|
||||
}
|
||||
|
||||
export class ClientExternalInquiriesListDto {
|
||||
@ApiProperty({ type: [ClientExternalInquiriesViewDto] })
|
||||
items: ClientExternalInquiriesViewDto[];
|
||||
}
|
||||
|
||||
export class ClientExternalInquiriesCatalogDto {
|
||||
@ApiProperty({
|
||||
enum: EXTERNAL_INQUIRY_TYPES,
|
||||
isArray: true,
|
||||
description: "Supported inquiry kinds stored on each client document.",
|
||||
})
|
||||
inquiryTypes: readonly string[];
|
||||
|
||||
@ApiProperty({
|
||||
description:
|
||||
"Global master switch path: PATCH /system-settings or PATCH /client/external-inquiries-live",
|
||||
})
|
||||
globalSettingPath: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Per-insurer CRUD base path (public for now).",
|
||||
example: "/client/{clientId}/external-inquiries",
|
||||
})
|
||||
perClientSettingPath: string;
|
||||
}
|
||||
|
||||
/** Build a view DTO from a lean client document. */
|
||||
export function toClientExternalInquiriesView(
|
||||
client: {
|
||||
_id?: unknown;
|
||||
clientCode?: number;
|
||||
clientName?: { persian?: string; english?: string } | string;
|
||||
settings?: { externalInquiries?: Partial<ExternalInquiryFlags> };
|
||||
},
|
||||
globalSandHubLiveEnabled: boolean,
|
||||
): ClientExternalInquiriesViewDto {
|
||||
const flags = mergeExternalInquiryFlags(client.settings?.externalInquiries);
|
||||
const effectiveLive = {} as ExternalInquiryFlags;
|
||||
for (const key of EXTERNAL_INQUIRY_TYPES) {
|
||||
effectiveLive[key] = globalSandHubLiveEnabled && flags[key] === true;
|
||||
}
|
||||
const name =
|
||||
typeof client.clientName === "string"
|
||||
? client.clientName
|
||||
: client.clientName?.persian ||
|
||||
client.clientName?.english ||
|
||||
String(client.clientCode ?? "");
|
||||
|
||||
return {
|
||||
clientId: String(client._id ?? ""),
|
||||
clientCode: Number(client.clientCode ?? 0),
|
||||
clientName: name,
|
||||
globalSandHubLiveEnabled,
|
||||
externalInquiries: flags,
|
||||
effectiveLive,
|
||||
};
|
||||
}
|
||||
|
||||
export class ClientIdParamDto {
|
||||
@ApiProperty({ description: "Mongo ObjectId of the insurer client document" })
|
||||
@IsMongoId()
|
||||
clientId: string;
|
||||
}
|
||||
|
||||
/** Optional nested patch used internally when validating partial bodies. */
|
||||
export class ExternalInquiryFlagsPatchDto {
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => UpdateClientExternalInquiriesDto)
|
||||
externalInquiries?: UpdateClientExternalInquiriesDto;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
import type { ExternalInquiryFlags } from "src/common/types/external-inquiry.types";
|
||||
|
||||
export type ClientDocument = ClientModel & Document;
|
||||
|
||||
@@ -37,6 +38,30 @@ export class ClientMediaSettings {
|
||||
voice?: ClientMediaLimits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-insurer toggles for outbound inquiry HTTP. Each flag is AND-ed with the
|
||||
* global `system_settings.externalApis.sandHubUseLiveApi` master switch.
|
||||
*/
|
||||
export class ClientExternalInquirySettings implements Partial<ExternalInquiryFlags> {
|
||||
@Prop({ type: Boolean, required: false, default: false })
|
||||
thirdPartyPlate?: boolean;
|
||||
|
||||
@Prop({ type: Boolean, required: false, default: false })
|
||||
carBodyPlate?: boolean;
|
||||
|
||||
@Prop({ type: Boolean, required: false, default: false })
|
||||
personalIdentity?: boolean;
|
||||
|
||||
@Prop({ type: Boolean, required: false, default: false })
|
||||
sheba?: boolean;
|
||||
|
||||
@Prop({ type: Boolean, required: false, default: false })
|
||||
drivingLicense?: boolean;
|
||||
|
||||
@Prop({ type: Boolean, required: false, default: false })
|
||||
carOwnership?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-tenant tunables. Add new policy fields here; consumers read them via
|
||||
* `ClientService` with documented defaults so older client documents keep
|
||||
@@ -57,6 +82,10 @@ export class ClientSettings {
|
||||
*/
|
||||
@Prop({ type: ClientMediaSettings, required: false })
|
||||
media?: ClientMediaSettings;
|
||||
|
||||
/** Per-inquiry live/mock toggles (see {@link ClientExternalInquirySettings}). */
|
||||
@Prop({ type: ClientExternalInquirySettings, required: false })
|
||||
externalInquiries?: ClientExternalInquirySettings;
|
||||
}
|
||||
|
||||
@Schema({ collection: "clients", versionKey: false })
|
||||
|
||||
83
src/client/external-inquiry-settings.service.spec.ts
Normal file
83
src/client/external-inquiry-settings.service.spec.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { Types } from "mongoose";
|
||||
import { ExternalInquirySettingsService } from "./external-inquiry-settings.service";
|
||||
import { SystemSettingsService } from "src/system-settings/system-settings.service";
|
||||
|
||||
describe("ExternalInquirySettingsService", () => {
|
||||
const systemSettings = {
|
||||
isSandHubLiveEnabled: jest.fn(),
|
||||
};
|
||||
const clientDb = {
|
||||
findOne: jest.fn(),
|
||||
};
|
||||
|
||||
let service: ExternalInquirySettingsService;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
delete process.env.CLIENT_ID;
|
||||
delete process.env.CLIENT_NAME;
|
||||
service = new ExternalInquirySettingsService(
|
||||
systemSettings as unknown as SystemSettingsService,
|
||||
clientDb as any,
|
||||
);
|
||||
service.invalidateClientCache();
|
||||
});
|
||||
|
||||
it("returns false when global master switch is off", async () => {
|
||||
systemSettings.isSandHubLiveEnabled.mockResolvedValue(false);
|
||||
clientDb.findOne.mockResolvedValue({
|
||||
clientCode: 8,
|
||||
settings: { externalInquiries: { sheba: true } },
|
||||
});
|
||||
|
||||
await expect(service.isInquiryLive("sheba", "client-id")).resolves.toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns true only when global and per-insurer flags are on", async () => {
|
||||
const clientId = new Types.ObjectId().toString();
|
||||
systemSettings.isSandHubLiveEnabled.mockResolvedValue(true);
|
||||
clientDb.findOne.mockResolvedValue({
|
||||
clientCode: 8,
|
||||
clientName: { persian: "بیمه پارسیان" },
|
||||
settings: {
|
||||
externalInquiries: {
|
||||
thirdPartyPlate: true,
|
||||
sheba: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
service.isInquiryLive("thirdPartyPlate", clientId),
|
||||
).resolves.toBe(true);
|
||||
await expect(service.isInquiryLive("sheba", clientId)).resolves.toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("uses client document for mock company context", async () => {
|
||||
const clientId = new Types.ObjectId().toString();
|
||||
clientDb.findOne.mockResolvedValue({
|
||||
clientCode: 8,
|
||||
clientName: { persian: "بیمه پارسیان" },
|
||||
});
|
||||
|
||||
await expect(service.getMockCompanyContext(clientId)).resolves.toEqual({
|
||||
companyId: "8",
|
||||
companyName: "بیمه پارسیان",
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to deployment env when client is missing", async () => {
|
||||
process.env.CLIENT_ID = "15";
|
||||
process.env.CLIENT_NAME = "بیمه سامان";
|
||||
clientDb.findOne.mockResolvedValue(null);
|
||||
|
||||
await expect(service.getMockCompanyContext()).resolves.toEqual({
|
||||
companyId: "15",
|
||||
companyName: "بیمه سامان",
|
||||
});
|
||||
});
|
||||
});
|
||||
120
src/client/external-inquiry-settings.service.ts
Normal file
120
src/client/external-inquiry-settings.service.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
DEFAULT_EXTERNAL_INQUIRY_FLAGS,
|
||||
ExternalInquiryFlags,
|
||||
ExternalInquiryType,
|
||||
mergeExternalInquiryFlags,
|
||||
MockInquiryCompanyContext,
|
||||
} from "src/common/types/external-inquiry.types";
|
||||
import { SystemSettingsService } from "src/system-settings/system-settings.service";
|
||||
import { ClientDbService } from "./entities/db-service/client.db.service";
|
||||
|
||||
@Injectable()
|
||||
export class ExternalInquirySettingsService {
|
||||
private readonly logger = new Logger(ExternalInquirySettingsService.name);
|
||||
private clientCache = new Map<string, { doc: any; at: number }>();
|
||||
private readonly cacheTtlMs = 15_000;
|
||||
|
||||
constructor(
|
||||
private readonly systemSettingsService: SystemSettingsService,
|
||||
private readonly clientDbService: ClientDbService,
|
||||
) {}
|
||||
|
||||
private cacheKey(ref?: string | Types.ObjectId | null): string {
|
||||
if (ref != null && String(ref).trim()) return `id:${String(ref)}`;
|
||||
const code = process.env.CLIENT_ID;
|
||||
return code ? `env:${code}` : "env:default";
|
||||
}
|
||||
|
||||
private async loadClient(
|
||||
clientRef?: string | Types.ObjectId | null,
|
||||
): Promise<any | null> {
|
||||
const key = this.cacheKey(clientRef);
|
||||
const cached = this.clientCache.get(key);
|
||||
const now = Date.now();
|
||||
if (cached && now - cached.at < this.cacheTtlMs) {
|
||||
return cached.doc;
|
||||
}
|
||||
|
||||
let doc: any = null;
|
||||
if (clientRef != null && Types.ObjectId.isValid(String(clientRef))) {
|
||||
doc = await this.clientDbService.findOne({
|
||||
_id: new Types.ObjectId(String(clientRef)),
|
||||
});
|
||||
}
|
||||
if (!doc) {
|
||||
const code = Number(process.env.CLIENT_ID);
|
||||
if (Number.isFinite(code)) {
|
||||
doc = await this.clientDbService.findOne({ clientCode: code });
|
||||
}
|
||||
}
|
||||
|
||||
this.clientCache.set(key, { doc, at: now });
|
||||
return doc;
|
||||
}
|
||||
|
||||
invalidateClientCache(clientId?: string): void {
|
||||
if (clientId) {
|
||||
this.clientCache.delete(`id:${clientId}`);
|
||||
} else {
|
||||
this.clientCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
async getFlagsForClient(
|
||||
clientRef?: string | Types.ObjectId | null,
|
||||
): Promise<ExternalInquiryFlags> {
|
||||
const client = await this.loadClient(clientRef);
|
||||
return mergeExternalInquiryFlags(client?.settings?.externalInquiries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Live HTTP when global master switch is on AND the per-insurer flag is true.
|
||||
*/
|
||||
async isInquiryLive(
|
||||
type: ExternalInquiryType,
|
||||
clientRef?: string | Types.ObjectId | null,
|
||||
): Promise<boolean> {
|
||||
if (!(await this.systemSettingsService.isSandHubLiveEnabled())) {
|
||||
return false;
|
||||
}
|
||||
const flags = await this.getFlagsForClient(clientRef);
|
||||
return flags[type] === true;
|
||||
}
|
||||
|
||||
/** Company fields injected into mocked plate/car-body inquiry payloads. */
|
||||
async getMockCompanyContext(
|
||||
clientRef?: string | Types.ObjectId | null,
|
||||
): Promise<MockInquiryCompanyContext> {
|
||||
const client = await this.loadClient(clientRef);
|
||||
if (client) {
|
||||
const name =
|
||||
typeof client.clientName === "string"
|
||||
? client.clientName
|
||||
: client.clientName?.persian ||
|
||||
client.clientName?.english ||
|
||||
"";
|
||||
return {
|
||||
companyId: String(client.clientCode ?? process.env.CLIENT_ID ?? "15"),
|
||||
companyName: name || process.env.CLIENT_NAME || "بیمه",
|
||||
};
|
||||
}
|
||||
return {
|
||||
companyId: String(process.env.CLIENT_ID ?? "15"),
|
||||
companyName: process.env.CLIENT_NAME ?? "بیمه سامان",
|
||||
};
|
||||
}
|
||||
|
||||
async getEffectiveLiveFlags(
|
||||
clientRef?: string | Types.ObjectId | null,
|
||||
): Promise<ExternalInquiryFlags> {
|
||||
const global = await this.systemSettingsService.isSandHubLiveEnabled();
|
||||
const flags = await this.getFlagsForClient(clientRef);
|
||||
const effective = { ...DEFAULT_EXTERNAL_INQUIRY_FLAGS };
|
||||
for (const key of Object.keys(effective) as ExternalInquiryType[]) {
|
||||
effective[key] = global && flags[key] === true;
|
||||
}
|
||||
return effective;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user