forked from Yara724/api
Merge pull request 'fanavaran lookups done' (#249) from s.hajizadeh/yara724api:main into main
Reviewed-on: Yara724/api#249
This commit is contained in:
@@ -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,
|
||||
],
|
||||
|
||||
@@ -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<LookupDocument>,
|
||||
) {}
|
||||
|
||||
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<unknown>,
|
||||
options?:
|
||||
| (() => Promise<unknown>)
|
||||
| {
|
||||
/** Soft/hard DB read — return null/undefined to miss. */
|
||||
dbSource?: () => Promise<unknown | null | undefined>;
|
||||
/** When true: try DB after file miss, before Fanavaran. */
|
||||
preferDbBeforeRemote?: boolean;
|
||||
},
|
||||
): Promise<unknown> {
|
||||
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();
|
||||
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<unknown | null> {
|
||||
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<unknown | null | undefined>,
|
||||
): Promise<unknown | null> {
|
||||
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,
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user