forked from Yara724/api
fanavaran lookups done
This commit is contained in:
@@ -3,6 +3,10 @@ import { HttpModule } from "@nestjs/axios";
|
|||||||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||||
import { MongooseModule } from "@nestjs/mongoose";
|
import { MongooseModule } from "@nestjs/mongoose";
|
||||||
import { createHttpModuleOptions } from "src/core/config/http-proxy.factory";
|
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 { FanavaranAuditModule } from "./fanavaran-audit.module";
|
||||||
import { FanavaranAuthService } from "./fanavaran-auth.service";
|
import { FanavaranAuthService } from "./fanavaran-auth.service";
|
||||||
import { FanavaranClientConfigService } from "./fanavaran-client-config.service";
|
import { FanavaranClientConfigService } from "./fanavaran-client-config.service";
|
||||||
@@ -29,6 +33,7 @@ import {
|
|||||||
name: FanavaranClientConfig.name,
|
name: FanavaranClientConfig.name,
|
||||||
schema: FanavaranClientConfigSchema,
|
schema: FanavaranClientConfigSchema,
|
||||||
},
|
},
|
||||||
|
{ name: LookupModel.name, schema: LookupSchema },
|
||||||
]),
|
]),
|
||||||
FanavaranAuditModule,
|
FanavaranAuditModule,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -7,9 +7,15 @@ import {
|
|||||||
Logger,
|
Logger,
|
||||||
NotFoundException,
|
NotFoundException,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
|
import { InjectModel } from "@nestjs/mongoose";
|
||||||
|
import { Model } from "mongoose";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
import { isAxiosError } from "axios";
|
import { isAxiosError } from "axios";
|
||||||
import { type FanavaranClientKey } from "src/core/config/fanavaran-client.config";
|
import { type FanavaranClientKey } from "src/core/config/fanavaran-client.config";
|
||||||
|
import {
|
||||||
|
LookupModel,
|
||||||
|
type LookupDocument,
|
||||||
|
} from "src/lookups/entities/schema/lookup.schema";
|
||||||
import {
|
import {
|
||||||
FANAVARAN_LOOKUP_BASE_URL,
|
FANAVARAN_LOOKUP_BASE_URL,
|
||||||
fanavaranLookupCacheDir,
|
fanavaranLookupCacheDir,
|
||||||
@@ -24,6 +30,8 @@ export class FanavaranLookupService {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly httpService: HttpService,
|
private readonly httpService: HttpService,
|
||||||
private readonly fanavaranAuthService: FanavaranAuthService,
|
private readonly fanavaranAuthService: FanavaranAuthService,
|
||||||
|
@InjectModel(LookupModel.name)
|
||||||
|
private readonly lookupModel: Model<LookupDocument>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private cacheFilePath(clientKey: FanavaranClientKey, fileName: string): string {
|
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(
|
async getRemoteLookup(
|
||||||
clientKey: FanavaranClientKey,
|
clientKey: FanavaranClientKey,
|
||||||
url: string,
|
url: string,
|
||||||
cacheFile: 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> {
|
): 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);
|
const cached = await this.readCacheFile(clientKey, cacheFile);
|
||||||
if (cached !== null) {
|
if (cached !== null) {
|
||||||
return cached;
|
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 {
|
try {
|
||||||
const data = await this.fetchFromFanavaran(clientKey, url);
|
const data = await this.fetchFromFanavaran(clientKey, url);
|
||||||
await this.writeCacheFile(clientKey, cacheFile, data);
|
await this.writeCacheFile(clientKey, cacheFile, data);
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (fallback) {
|
if (dbSource && !preferDbBeforeRemote) {
|
||||||
this.logger.warn(
|
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();
|
||||||
await this.writeCacheFile(clientKey, cacheFile, data);
|
if (data != null) {
|
||||||
return data;
|
await this.writeCacheFile(clientKey, cacheFile, data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw error;
|
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(
|
async inquiryByVin(
|
||||||
clientKey: FanavaranClientKey,
|
clientKey: FanavaranClientKey,
|
||||||
vin: string,
|
vin: string,
|
||||||
|
|||||||
@@ -61,12 +61,14 @@ export class LookupsService {
|
|||||||
const clientKey = this.activeClientKey();
|
const clientKey = this.activeClientKey();
|
||||||
const definition = this.findRemoteLookup(lookupName);
|
const definition = this.findRemoteLookup(lookupName);
|
||||||
|
|
||||||
|
// Parsian: file → lookups collection → Fanavaran (default inside FanavaranLookupService)
|
||||||
|
// Tejaratno: file → Fanavaran → DB on API failure
|
||||||
return this.fanavaranLookupService.getRemoteLookup(
|
return this.fanavaranLookupService.getRemoteLookup(
|
||||||
clientKey,
|
clientKey,
|
||||||
definition.url,
|
definition.url,
|
||||||
definition.cacheFile,
|
definition.cacheFile,
|
||||||
clientKey === "tejaratno"
|
clientKey === "tejaratno"
|
||||||
? async () => this.getLookup(lookupName)
|
? { dbSource: () => this.getLookup(lookupName) }
|
||||||
: undefined,
|
: undefined,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user