forked from Yara724/api
fanavaran lookups done
This commit is contained in:
@@ -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();
|
||||
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<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,
|
||||
|
||||
Reference in New Issue
Block a user