Merge pull request 'Fixed smsApiKey index error and err handling in ESG' (#142) from s.yahyaee/yara724-api:main into main

Reviewed-on: Yara724/api#142
This commit is contained in:
2026-06-21 12:19:15 +03:30
6 changed files with 56 additions and 28 deletions

View File

@@ -114,6 +114,7 @@ export class ClientService {
) {} ) {}
async addClient(client: ClientDto): Promise<ClientDtoRs> { async addClient(client: ClientDto): Promise<ClientDtoRs> {
try { try {
const smsApiKey = client.property?.smsApiKey?.trim();
const newClient = await this.clientDbService.create({ const newClient = await this.clientDbService.create({
clientCode: client.clientCode, clientCode: client.clientCode,
@@ -129,9 +130,7 @@ export class ClientService {
: (client.clientName?.english ?? null), : (client.clientName?.english ?? null),
}, },
property: { ...(smsApiKey ? { property: { smsApiKey } } : {}),
smsApiKey: client.property?.smsApiKey ?? null,
},
useExpertMode: client.useExpertMode ?? null, useExpertMode: client.useExpertMode ?? null,
}); });

View File

@@ -23,8 +23,9 @@ class ClientName {
} }
class Property { class Property {
@ApiPropertyOptional({}) @ApiPropertyOptional({})
@IsOptional()
@IsString() @IsString()
smsApiKey: string; smsApiKey?: string;
} }
export class ClientDto { export class ClientDto {

View File

@@ -1,15 +1,49 @@
import { Injectable } from "@nestjs/common"; import { Injectable, Logger, OnModuleInit } from "@nestjs/common";
import { InjectModel } from "@nestjs/mongoose"; import { InjectModel } from "@nestjs/mongoose";
import { FilterQuery, Model, UpdateQuery } from "mongoose"; import { FilterQuery, Model, UpdateQuery } from "mongoose";
import { ClientModel, ClientDocument } from "../schema/client.schema"; import { ClientModel, ClientDocument } from "../schema/client.schema";
@Injectable() @Injectable()
export class ClientDbService { export class ClientDbService implements OnModuleInit {
private readonly logger = new Logger(ClientDbService.name);
constructor( constructor(
@InjectModel(ClientModel.name) @InjectModel(ClientModel.name)
private readonly clientModel: Model<ClientModel>, private readonly clientModel: Model<ClientModel>,
) {} ) {}
async onModuleInit(): Promise<void> {
await this.dropLegacyPropertyUniqueIndex();
}
/**
* `property` was once declared `unique: true` in the schema. Production keeps
* `autoIndex: false`, so the stale unique index remained and rejected a second
* client with a missing/null SMS key (E11000 duplicate key).
*/
private async dropLegacyPropertyUniqueIndex(): Promise<void> {
try {
const indexes = await this.clientModel.collection.indexes();
for (const index of indexes) {
if (!index.unique || !index.name || index.name === "_id_") continue;
const keys = Object.keys(index.key ?? {});
const isLegacyPropertyIndex =
keys.length === 1 &&
(keys[0] === "property" || keys[0] === "property.smsApiKey");
if (!isLegacyPropertyIndex) continue;
await this.clientModel.collection.dropIndex(index.name);
this.logger.warn(
`Dropped legacy unique index on clients.${keys[0]}: ${index.name}`,
);
}
} catch (err) {
this.logger.error("Failed to drop legacy client property index", err);
}
}
async create(client: ClientModel): Promise<ClientModel> { async create(client: ClientModel): Promise<ClientModel> {
return await this.clientModel.create(client); return await this.clientModel.create(client);
} }

View File

@@ -96,9 +96,9 @@ export class ClientModel {
english: string; english: string;
}; };
@Prop({ required: false, unique: false, type: Object }) @Prop({ required: false, type: Object })
property: { property?: {
smsApiKey: string; smsApiKey?: string;
}; };
@Prop({ required: true, unique: false }) @Prop({ required: true, unique: false })

View File

@@ -4061,9 +4061,6 @@ export class RequestManagementService {
persian: data.clientName, persian: data.clientName,
english: null, english: null,
}, },
property: {
smsApiKey: null,
},
useExpertMode: "genuine", useExpertMode: "genuine",
}); });
} catch (err) { } catch (err) {

View File

@@ -20,6 +20,9 @@ import { firstValueFrom } from "rxjs";
@Injectable() @Injectable()
export class SandHubService { export class SandHubService {
private static readonly ESG_INQUIRY_UNAVAILABLE_MESSAGE =
"استعلام در دسترس نیست";
private readonly logger = new Logger(SandHubService.name); private readonly logger = new Logger(SandHubService.name);
private loginToken: string | null = null; private loginToken: string | null = null;
private tokenExpiry: Date | null = null; private tokenExpiry: Date | null = null;
@@ -528,17 +531,13 @@ export class SandHubService {
if (!raw) return raw; if (!raw) return raw;
if (raw?.success === false) { if (raw?.success === false) {
this.logger.warn(
"ESG policyByPlate inquiry returned success=false",
raw,
);
return { return {
Error: { Error: {
Message: Message: SandHubService.ESG_INQUIRY_UNAVAILABLE_MESSAGE,
raw?.error?.message ||
raw?.message ||
"ESG policyByPlate inquiry returned an error",
Code: raw?.error?.code || raw?.error?.providerCode || "ESG_INQUIRY_ERROR",
ProviderMessage: raw?.error?.providerMessage,
ProviderCode: raw?.error?.providerCode,
TrackingCode: raw?.trackingCode,
Conflict: raw?.error?.conflict,
}, },
}; };
} }
@@ -622,10 +621,9 @@ export class SandHubService {
private mapEsgPersonInquiryToOldFormat(raw: any): Record<string, unknown> { private mapEsgPersonInquiryToOldFormat(raw: any): Record<string, unknown> {
if (raw?.success === false) { if (raw?.success === false) {
throw new NotFoundException( this.logger.warn("ESG person inquiry returned success=false", raw);
raw?.error?.message || throw new BadRequestException(
raw?.message || SandHubService.ESG_INQUIRY_UNAVAILABLE_MESSAGE,
"Personal inquiry failed: Record not found for the given national code and birth date.",
); );
} }
@@ -648,10 +646,9 @@ export class SandHubService {
private mapEsgShebaInquiryToOldFormat(raw: any): Record<string, unknown> { private mapEsgShebaInquiryToOldFormat(raw: any): Record<string, unknown> {
if (raw?.success === false) { if (raw?.success === false) {
this.logger.warn("ESG sheba inquiry returned success=false", raw);
throw new BadRequestException( throw new BadRequestException(
raw?.error?.message || SandHubService.ESG_INQUIRY_UNAVAILABLE_MESSAGE,
raw?.message ||
"Sheba ID validation failed. The provided Sheba ID does not match the national ID.",
); );
} }