From 738344a9d4c71e1df9a7f9f235dc7d10aa6c628f Mon Sep 17 00:00:00 2001 From: "s.hajizadeh" Date: Mon, 3 Aug 2026 15:53:36 +0330 Subject: [PATCH] fanavaran lookups done --- src/fanavaran/fanavaran-lookup.module.ts | 5 ++ src/fanavaran/fanavaran-lookup.service.ts | 100 ++++++++++++++++++++-- src/lookups/lookups.service.ts | 4 +- 3 files changed, 102 insertions(+), 7 deletions(-) diff --git a/src/fanavaran/fanavaran-lookup.module.ts b/src/fanavaran/fanavaran-lookup.module.ts index 41699e7..178b33a 100644 --- a/src/fanavaran/fanavaran-lookup.module.ts +++ b/src/fanavaran/fanavaran-lookup.module.ts @@ -3,6 +3,10 @@ import { HttpModule } from "@nestjs/axios"; import { ConfigModule, ConfigService } from "@nestjs/config"; import { MongooseModule } from "@nestjs/mongoose"; import { createHttpModuleOptions } from "src/core/config/http-proxy.factory"; +import { + LookupModel, + LookupSchema, +} from "src/lookups/entities/schema/lookup.schema"; import { FanavaranAuditModule } from "./fanavaran-audit.module"; import { FanavaranAuthService } from "./fanavaran-auth.service"; import { FanavaranClientConfigService } from "./fanavaran-client-config.service"; @@ -29,6 +33,7 @@ import { name: FanavaranClientConfig.name, schema: FanavaranClientConfigSchema, }, + { name: LookupModel.name, schema: LookupSchema }, ]), FanavaranAuditModule, ], diff --git a/src/fanavaran/fanavaran-lookup.service.ts b/src/fanavaran/fanavaran-lookup.service.ts index 0b073b3..cb4063f 100644 --- a/src/fanavaran/fanavaran-lookup.service.ts +++ b/src/fanavaran/fanavaran-lookup.service.ts @@ -7,9 +7,15 @@ import { Logger, NotFoundException, } from "@nestjs/common"; +import { InjectModel } from "@nestjs/mongoose"; +import { Model } from "mongoose"; import { firstValueFrom } from "rxjs"; import { isAxiosError } from "axios"; import { type FanavaranClientKey } from "src/core/config/fanavaran-client.config"; +import { + LookupModel, + type LookupDocument, +} from "src/lookups/entities/schema/lookup.schema"; import { FANAVARAN_LOOKUP_BASE_URL, fanavaranLookupCacheDir, @@ -24,6 +30,8 @@ export class FanavaranLookupService { constructor( private readonly httpService: HttpService, private readonly fanavaranAuthService: FanavaranAuthService, + @InjectModel(LookupModel.name) + private readonly lookupModel: Model, ) {} private cacheFilePath(clientKey: FanavaranClientKey, fileName: string): string { @@ -117,34 +125,114 @@ export class FanavaranLookupService { } } + /** + * Resolve a remote Fanavaran lookup. + * + * Parsian (default): file → `lookups` collection → Fanavaran API + * Tejaratno with dbSource: file → Fanavaran → DB on API failure + * Others: file → Fanavaran + */ async getRemoteLookup( clientKey: FanavaranClientKey, url: string, cacheFile: string, - fallback?: () => Promise, + options?: + | (() => Promise) + | { + /** Soft/hard DB read — return null/undefined to miss. */ + dbSource?: () => Promise; + /** When true: try DB after file miss, before Fanavaran. */ + preferDbBeforeRemote?: boolean; + }, ): Promise { + const normalized = + typeof options === "function" + ? { dbSource: options, preferDbBeforeRemote: false } + : options ?? {}; + + const preferDbBeforeRemote = + normalized.preferDbBeforeRemote ?? clientKey === "parsian"; + + const dbSource = + normalized.dbSource ?? + (preferDbBeforeRemote + ? () => this.readLookupsCollectionByCacheFile(cacheFile) + : undefined); + const cached = await this.readCacheFile(clientKey, cacheFile); if (cached !== null) { return cached; } + if (preferDbBeforeRemote && dbSource) { + const fromDb = await this.tryDbSource(clientKey, cacheFile, dbSource); + if (fromDb !== null) { + await this.writeCacheFile(clientKey, cacheFile, fromDb); + return fromDb; + } + } + try { const data = await this.fetchFromFanavaran(clientKey, url); await this.writeCacheFile(clientKey, cacheFile, data); return data; } catch (error) { - if (fallback) { + if (dbSource && !preferDbBeforeRemote) { this.logger.warn( - `Fanavaran lookup fetch failed for ${clientKey}/${cacheFile}; using fallback`, + `Fanavaran lookup fetch failed for ${clientKey}/${cacheFile}; using DB fallback`, ); - const data = await fallback(); - await this.writeCacheFile(clientKey, cacheFile, data); - return data; + const data = await dbSource(); + if (data != null) { + await this.writeCacheFile(clientKey, cacheFile, data); + return data; + } } throw error; } } + private lookupNameFromCacheFile(cacheFile: string): string { + return cacheFile.replace(/\.json$/i, ""); + } + + private async readLookupsCollectionByCacheFile( + cacheFile: string, + ): Promise { + const name = this.lookupNameFromCacheFile(cacheFile); + const doc = await this.lookupModel.findOne({ name }).lean().exec(); + if (!doc || doc.response == null) { + return null; + } + return doc.response; + } + + private async tryDbSource( + clientKey: FanavaranClientKey, + cacheFile: string, + dbSource: () => Promise, + ): Promise { + try { + const data = await dbSource(); + if (data == null) { + this.logger.debug( + `[${clientKey}] No DB lookup for ${cacheFile}; will try Fanavaran`, + ); + return null; + } + this.logger.log( + `[${clientKey}] Using lookups collection for ${cacheFile} (before Fanavaran)`, + ); + return data; + } catch (error) { + this.logger.warn( + `[${clientKey}] DB lookup miss for ${cacheFile}: ${ + error instanceof Error ? error.message : error + }; will try Fanavaran`, + ); + return null; + } + } + async inquiryByVin( clientKey: FanavaranClientKey, vin: string, diff --git a/src/lookups/lookups.service.ts b/src/lookups/lookups.service.ts index 9efe9e6..0115f2f 100644 --- a/src/lookups/lookups.service.ts +++ b/src/lookups/lookups.service.ts @@ -61,12 +61,14 @@ export class LookupsService { const clientKey = this.activeClientKey(); const definition = this.findRemoteLookup(lookupName); + // Parsian: file → lookups collection → Fanavaran (default inside FanavaranLookupService) + // Tejaratno: file → Fanavaran → DB on API failure return this.fanavaranLookupService.getRemoteLookup( clientKey, definition.url, definition.cacheFile, clientKey === "tejaratno" - ? async () => this.getLookup(lookupName) + ? { dbSource: () => this.getLookup(lookupName) } : undefined, ); }