forked from Shared/esg
initial commit
This commit is contained in:
79
src/logging/dto/inquiry-log-query.dto.ts
Normal file
79
src/logging/dto/inquiry-log-query.dto.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsEnum, IsISO8601, IsInt, IsOptional, IsString, Max, Min } from 'class-validator';
|
||||
import { InquiryStatus } from '../../common/enums/inquiry-status.enum';
|
||||
import { InquiryType } from '../../common/enums/inquiry-type.enum';
|
||||
import { ProviderName } from '../../common/enums/provider-name.enum';
|
||||
|
||||
export enum LogTimeRangePreset {
|
||||
LAST_24_HOURS = 'LAST_24_HOURS',
|
||||
LAST_7_DAYS = 'LAST_7_DAYS',
|
||||
LAST_30_DAYS = 'LAST_30_DAYS',
|
||||
}
|
||||
|
||||
export enum LogTimelineInterval {
|
||||
HOUR = 'HOUR',
|
||||
DAY = 'DAY',
|
||||
MONTH = 'MONTH',
|
||||
}
|
||||
|
||||
export class InquiryLogQueryDto {
|
||||
@ApiPropertyOptional({ enum: InquiryType })
|
||||
@IsOptional()
|
||||
@IsEnum(InquiryType)
|
||||
inquiryType?: InquiryType;
|
||||
|
||||
@ApiPropertyOptional({ enum: ProviderName })
|
||||
@IsOptional()
|
||||
@IsEnum(ProviderName)
|
||||
provider?: ProviderName;
|
||||
|
||||
@ApiPropertyOptional({ enum: InquiryStatus })
|
||||
@IsOptional()
|
||||
@IsEnum(InquiryStatus)
|
||||
status?: InquiryStatus;
|
||||
|
||||
@ApiPropertyOptional({ enum: LogTimeRangePreset })
|
||||
@IsOptional()
|
||||
@IsEnum(LogTimeRangePreset)
|
||||
preset?: LogTimeRangePreset;
|
||||
|
||||
@ApiPropertyOptional({ example: '2026-06-01T00:00:00.000Z' })
|
||||
@IsOptional()
|
||||
@IsISO8601()
|
||||
from?: string;
|
||||
|
||||
@ApiPropertyOptional({ example: '2026-06-08T00:00:00.000Z' })
|
||||
@IsOptional()
|
||||
@IsISO8601()
|
||||
to?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Matches error.code' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
errorCode?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Case-insensitive match against error.message' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
errorMessage?: string;
|
||||
|
||||
@ApiPropertyOptional({ minimum: 1, maximum: 500, default: 50 })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(500)
|
||||
limit?: number;
|
||||
|
||||
@ApiPropertyOptional({ minimum: 1, default: 1 })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
page?: number;
|
||||
}
|
||||
|
||||
export class InquiryLogTimelineQueryDto extends InquiryLogQueryDto {
|
||||
@ApiPropertyOptional({ enum: LogTimelineInterval, default: LogTimelineInterval.HOUR })
|
||||
@IsOptional()
|
||||
@IsEnum(LogTimelineInterval)
|
||||
interval?: LogTimelineInterval;
|
||||
}
|
||||
414
src/logging/inquiry-log.service.ts
Normal file
414
src/logging/inquiry-log.service.ts
Normal file
@@ -0,0 +1,414 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { FilterQuery, Model } from 'mongoose';
|
||||
import { InquiryStatus } from '../common/enums/inquiry-status.enum';
|
||||
import { InquiryType } from '../common/enums/inquiry-type.enum';
|
||||
import { ProviderName } from '../common/enums/provider-name.enum';
|
||||
import { maskPayload } from '../common/helpers/mask-payload.helper';
|
||||
import {
|
||||
InquiryLogQueryDto,
|
||||
InquiryLogTimelineQueryDto,
|
||||
LogTimelineInterval,
|
||||
LogTimeRangePreset,
|
||||
} from './dto/inquiry-log-query.dto';
|
||||
import { InquiryLog, InquiryLogDocument } from './schemas/inquiry-log.schema';
|
||||
|
||||
export interface CreateInquiryLogInput {
|
||||
inquiryType: InquiryType;
|
||||
provider: ProviderName;
|
||||
requestPayload: Record<string, unknown>;
|
||||
responsePayload?: Record<string, unknown>;
|
||||
status: InquiryStatus;
|
||||
duration: number;
|
||||
requestId: string;
|
||||
trackingCode: string;
|
||||
error?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface InquiryLogListItem {
|
||||
id: string;
|
||||
inquiryType: InquiryType;
|
||||
provider: ProviderName;
|
||||
status: InquiryStatus;
|
||||
duration: number;
|
||||
requestId: string;
|
||||
trackingCode: string;
|
||||
maskedRequest: Record<string, unknown>;
|
||||
responsePayload?: Record<string, unknown>;
|
||||
error?: Record<string, unknown>;
|
||||
createdAt?: Date;
|
||||
}
|
||||
|
||||
export interface InquiryLogSummary {
|
||||
total: number;
|
||||
success: number;
|
||||
failure: number;
|
||||
partial: number;
|
||||
successRate: number;
|
||||
failureRate: number;
|
||||
averageDuration: number;
|
||||
minDuration: number;
|
||||
maxDuration: number;
|
||||
}
|
||||
|
||||
export interface CommonErrorItem {
|
||||
code: string;
|
||||
message: string;
|
||||
count: number;
|
||||
lastSeenAt: Date;
|
||||
}
|
||||
|
||||
export interface DurationExtremeItem {
|
||||
id: string;
|
||||
inquiryType: InquiryType;
|
||||
provider: ProviderName;
|
||||
status: InquiryStatus;
|
||||
duration: number;
|
||||
requestId: string;
|
||||
trackingCode: string;
|
||||
error?: Record<string, unknown>;
|
||||
createdAt?: Date;
|
||||
}
|
||||
|
||||
export interface TimelineBucket {
|
||||
bucket: string;
|
||||
total: number;
|
||||
success: number;
|
||||
failure: number;
|
||||
partial: number;
|
||||
averageDuration: number;
|
||||
maxDuration: number;
|
||||
commonErrorCode?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class InquiryLogService {
|
||||
constructor(
|
||||
@InjectModel(InquiryLog.name)
|
||||
private readonly inquiryLogModel: Model<InquiryLogDocument>,
|
||||
) {}
|
||||
|
||||
async create(input: CreateInquiryLogInput): Promise<InquiryLogDocument> {
|
||||
return this.inquiryLogModel.create({
|
||||
...input,
|
||||
maskedRequest: maskPayload(input.requestPayload),
|
||||
});
|
||||
}
|
||||
|
||||
async findLogs(query: InquiryLogQueryDto): Promise<{
|
||||
data: InquiryLogListItem[];
|
||||
page: number;
|
||||
limit: number;
|
||||
total: number;
|
||||
}> {
|
||||
const filter = this.buildFilter(query);
|
||||
const limit = query.limit ?? 50;
|
||||
const page = query.page ?? 1;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const [logs, total] = await Promise.all([
|
||||
this.inquiryLogModel
|
||||
.find(filter)
|
||||
.sort({ createdAt: -1 })
|
||||
.skip(skip)
|
||||
.limit(limit)
|
||||
.select('inquiryType provider status duration requestId trackingCode maskedRequest responsePayload error createdAt')
|
||||
.lean()
|
||||
.exec(),
|
||||
this.inquiryLogModel.countDocuments(filter),
|
||||
]);
|
||||
|
||||
return {
|
||||
data: logs.map((log) => ({
|
||||
id: log._id.toString(),
|
||||
inquiryType: log.inquiryType,
|
||||
provider: log.provider,
|
||||
status: log.status,
|
||||
duration: log.duration,
|
||||
requestId: log.requestId,
|
||||
trackingCode: log.trackingCode,
|
||||
maskedRequest: log.maskedRequest,
|
||||
responsePayload: log.responsePayload,
|
||||
error: log.error,
|
||||
createdAt: log.createdAt,
|
||||
})),
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
};
|
||||
}
|
||||
|
||||
async getSummary(query: InquiryLogQueryDto): Promise<InquiryLogSummary> {
|
||||
const [summary] = await this.inquiryLogModel.aggregate<InquiryLogSummary>([
|
||||
{ $match: this.buildFilter(query) },
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
total: { $sum: 1 },
|
||||
success: {
|
||||
$sum: { $cond: [{ $eq: ['$status', InquiryStatus.SUCCESS] }, 1, 0] },
|
||||
},
|
||||
failure: {
|
||||
$sum: { $cond: [{ $eq: ['$status', InquiryStatus.FAILURE] }, 1, 0] },
|
||||
},
|
||||
partial: {
|
||||
$sum: { $cond: [{ $eq: ['$status', InquiryStatus.PARTIAL] }, 1, 0] },
|
||||
},
|
||||
averageDuration: { $avg: '$duration' },
|
||||
minDuration: { $min: '$duration' },
|
||||
maxDuration: { $max: '$duration' },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
total: 1,
|
||||
success: 1,
|
||||
failure: 1,
|
||||
partial: 1,
|
||||
successRate: {
|
||||
$cond: [{ $eq: ['$total', 0] }, 0, { $divide: ['$success', '$total'] }],
|
||||
},
|
||||
failureRate: {
|
||||
$cond: [{ $eq: ['$total', 0] }, 0, { $divide: ['$failure', '$total'] }],
|
||||
},
|
||||
averageDuration: { $ifNull: ['$averageDuration', 0] },
|
||||
minDuration: { $ifNull: ['$minDuration', 0] },
|
||||
maxDuration: { $ifNull: ['$maxDuration', 0] },
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
return summary ?? this.emptySummary();
|
||||
}
|
||||
|
||||
async getCommonErrors(query: InquiryLogQueryDto): Promise<CommonErrorItem[]> {
|
||||
const limit = query.limit ?? 10;
|
||||
|
||||
return this.inquiryLogModel.aggregate<CommonErrorItem>([
|
||||
{
|
||||
$match: {
|
||||
...this.buildFilter(query),
|
||||
error: { $exists: true, $ne: null },
|
||||
},
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
code: { $ifNull: ['$error.code', 'UNKNOWN_ERROR'] },
|
||||
message: { $ifNull: ['$error.message', 'Unknown error'] },
|
||||
},
|
||||
count: { $sum: 1 },
|
||||
lastSeenAt: { $max: '$createdAt' },
|
||||
},
|
||||
},
|
||||
{ $sort: { count: -1, lastSeenAt: -1 } },
|
||||
{ $limit: limit },
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
code: '$_id.code',
|
||||
message: '$_id.message',
|
||||
count: 1,
|
||||
lastSeenAt: 1,
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
async getDurationExtremes(query: InquiryLogQueryDto): Promise<{
|
||||
fastest: DurationExtremeItem[];
|
||||
slowest: DurationExtremeItem[];
|
||||
}> {
|
||||
const filter = this.buildFilter(query);
|
||||
const limit = query.limit ?? 10;
|
||||
const projection =
|
||||
'inquiryType provider status duration requestId trackingCode error createdAt';
|
||||
|
||||
const [fastest, slowest] = await Promise.all([
|
||||
this.inquiryLogModel.find(filter).sort({ duration: 1 }).limit(limit).select(projection).lean(),
|
||||
this.inquiryLogModel.find(filter).sort({ duration: -1 }).limit(limit).select(projection).lean(),
|
||||
]);
|
||||
|
||||
return {
|
||||
fastest: fastest.map((log) => this.toDurationExtremeItem(log)),
|
||||
slowest: slowest.map((log) => this.toDurationExtremeItem(log)),
|
||||
};
|
||||
}
|
||||
|
||||
async getTimeline(query: InquiryLogTimelineQueryDto): Promise<TimelineBucket[]> {
|
||||
const interval = query.interval ?? LogTimelineInterval.HOUR;
|
||||
|
||||
return this.inquiryLogModel.aggregate<TimelineBucket>([
|
||||
{ $match: this.buildFilter(query) },
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
bucket: {
|
||||
$dateToString: {
|
||||
format: this.timelineFormat(interval),
|
||||
date: '$createdAt',
|
||||
timezone: 'Asia/Tehran',
|
||||
},
|
||||
},
|
||||
errorCode: { $ifNull: ['$error.code', null] },
|
||||
},
|
||||
total: { $sum: 1 },
|
||||
success: {
|
||||
$sum: { $cond: [{ $eq: ['$status', InquiryStatus.SUCCESS] }, 1, 0] },
|
||||
},
|
||||
failure: {
|
||||
$sum: { $cond: [{ $eq: ['$status', InquiryStatus.FAILURE] }, 1, 0] },
|
||||
},
|
||||
partial: {
|
||||
$sum: { $cond: [{ $eq: ['$status', InquiryStatus.PARTIAL] }, 1, 0] },
|
||||
},
|
||||
durationSum: { $sum: '$duration' },
|
||||
maxDuration: { $max: '$duration' },
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
errorCount: {
|
||||
$cond: [{ $ne: ['$_id.errorCode', null] }, '$total', 0],
|
||||
},
|
||||
},
|
||||
},
|
||||
{ $sort: { '_id.bucket': 1, errorCount: -1 } },
|
||||
{
|
||||
$group: {
|
||||
_id: '$_id.bucket',
|
||||
total: { $sum: '$total' },
|
||||
success: { $sum: '$success' },
|
||||
failure: { $sum: '$failure' },
|
||||
partial: { $sum: '$partial' },
|
||||
durationSum: { $sum: '$durationSum' },
|
||||
maxDuration: { $max: '$maxDuration' },
|
||||
commonErrorCode: { $first: '$_id.errorCode' },
|
||||
},
|
||||
},
|
||||
{ $sort: { _id: 1 } },
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
bucket: '$_id',
|
||||
total: 1,
|
||||
success: 1,
|
||||
failure: 1,
|
||||
partial: 1,
|
||||
averageDuration: {
|
||||
$cond: [{ $eq: ['$total', 0] }, 0, { $divide: ['$durationSum', '$total'] }],
|
||||
},
|
||||
maxDuration: { $ifNull: ['$maxDuration', 0] },
|
||||
commonErrorCode: 1,
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
private buildFilter(query: InquiryLogQueryDto): FilterQuery<InquiryLogDocument> {
|
||||
const filter: FilterQuery<InquiryLogDocument> = {};
|
||||
const createdAt = this.buildCreatedAtFilter(query);
|
||||
|
||||
if (createdAt) {
|
||||
filter.createdAt = createdAt;
|
||||
}
|
||||
|
||||
if (query.inquiryType) {
|
||||
filter.inquiryType = query.inquiryType;
|
||||
}
|
||||
if (query.provider) {
|
||||
filter.provider = query.provider;
|
||||
}
|
||||
if (query.status) {
|
||||
filter.status = query.status;
|
||||
}
|
||||
if (query.errorCode) {
|
||||
filter['error.code'] = query.errorCode;
|
||||
}
|
||||
if (query.errorMessage) {
|
||||
filter['error.message'] = { $regex: query.errorMessage, $options: 'i' };
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
private buildCreatedAtFilter(query: InquiryLogQueryDto): { $gte?: Date; $lte?: Date } | undefined {
|
||||
if (!query.from && !query.to && !query.preset) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const to = query.to ? new Date(query.to) : undefined;
|
||||
const from = query.from ? new Date(query.from) : this.defaultFromDate(query.preset);
|
||||
|
||||
return {
|
||||
...(from && { $gte: from }),
|
||||
...(to && { $lte: to }),
|
||||
};
|
||||
}
|
||||
|
||||
private defaultFromDate(preset?: LogTimeRangePreset): Date {
|
||||
const now = new Date();
|
||||
const selectedPreset = preset ?? LogTimeRangePreset.LAST_24_HOURS;
|
||||
|
||||
switch (selectedPreset) {
|
||||
case LogTimeRangePreset.LAST_30_DAYS:
|
||||
return new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
||||
case LogTimeRangePreset.LAST_7_DAYS:
|
||||
return new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
||||
case LogTimeRangePreset.LAST_24_HOURS:
|
||||
default:
|
||||
return new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
private timelineFormat(interval: LogTimelineInterval): string {
|
||||
switch (interval) {
|
||||
case LogTimelineInterval.MONTH:
|
||||
return '%Y-%m';
|
||||
case LogTimelineInterval.DAY:
|
||||
return '%Y-%m-%d';
|
||||
case LogTimelineInterval.HOUR:
|
||||
default:
|
||||
return '%Y-%m-%d %H:00';
|
||||
}
|
||||
}
|
||||
|
||||
private emptySummary(): InquiryLogSummary {
|
||||
return {
|
||||
total: 0,
|
||||
success: 0,
|
||||
failure: 0,
|
||||
partial: 0,
|
||||
successRate: 0,
|
||||
failureRate: 0,
|
||||
averageDuration: 0,
|
||||
minDuration: 0,
|
||||
maxDuration: 0,
|
||||
};
|
||||
}
|
||||
|
||||
private toDurationExtremeItem(log: {
|
||||
_id: unknown;
|
||||
inquiryType: InquiryType;
|
||||
provider: ProviderName;
|
||||
status: InquiryStatus;
|
||||
duration: number;
|
||||
requestId: string;
|
||||
trackingCode: string;
|
||||
error?: Record<string, unknown>;
|
||||
createdAt?: Date;
|
||||
}): DurationExtremeItem {
|
||||
return {
|
||||
id: String(log._id),
|
||||
inquiryType: log.inquiryType,
|
||||
provider: log.provider,
|
||||
status: log.status,
|
||||
duration: log.duration,
|
||||
requestId: log.requestId,
|
||||
trackingCode: log.trackingCode,
|
||||
error: log.error,
|
||||
createdAt: log.createdAt,
|
||||
};
|
||||
}
|
||||
}
|
||||
47
src/logging/inquiry-logs.controller.ts
Normal file
47
src/logging/inquiry-logs.controller.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { Roles } from '../auth/decorators/roles.decorator';
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||
import { RolesGuard } from '../auth/guards/roles.guard';
|
||||
import { ADMIN_ROLES } from '../common/enums/role.enum';
|
||||
import { InquiryLogQueryDto, InquiryLogTimelineQueryDto } from './dto/inquiry-log-query.dto';
|
||||
import { InquiryLogService } from './inquiry-log.service';
|
||||
|
||||
@ApiTags('Inquiry Logs')
|
||||
@ApiBearerAuth()
|
||||
@Controller('inquiry-logs')
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
@Roles(...ADMIN_ROLES)
|
||||
export class InquiryLogsController {
|
||||
constructor(private readonly inquiryLogService: InquiryLogService) {}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Search inquiry request logs' })
|
||||
async findLogs(@Query() query: InquiryLogQueryDto) {
|
||||
return this.inquiryLogService.findLogs(query);
|
||||
}
|
||||
|
||||
@Get('summary')
|
||||
@ApiOperation({ summary: 'Get request totals, success/failure counts, and duration metrics' })
|
||||
async getSummary(@Query() query: InquiryLogQueryDto) {
|
||||
return this.inquiryLogService.getSummary(query);
|
||||
}
|
||||
|
||||
@Get('errors/common')
|
||||
@ApiOperation({ summary: 'Get most common errors for a provider, inquiry type, and time range' })
|
||||
async getCommonErrors(@Query() query: InquiryLogQueryDto) {
|
||||
return this.inquiryLogService.getCommonErrors(query);
|
||||
}
|
||||
|
||||
@Get('durations/extremes')
|
||||
@ApiOperation({ summary: 'Get fastest and slowest inquiry requests' })
|
||||
async getDurationExtremes(@Query() query: InquiryLogQueryDto) {
|
||||
return this.inquiryLogService.getDurationExtremes(query);
|
||||
}
|
||||
|
||||
@Get('timeline')
|
||||
@ApiOperation({ summary: 'Get request volume, status, error, and duration trend buckets' })
|
||||
async getTimeline(@Query() query: InquiryLogTimelineQueryDto) {
|
||||
return this.inquiryLogService.getTimeline(query);
|
||||
}
|
||||
}
|
||||
15
src/logging/logging.module.ts
Normal file
15
src/logging/logging.module.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { InquiryLogsController } from './inquiry-logs.controller';
|
||||
import { InquiryLogService } from './inquiry-log.service';
|
||||
import { InquiryLog, InquiryLogSchema } from './schemas/inquiry-log.schema';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
MongooseModule.forFeature([{ name: InquiryLog.name, schema: InquiryLogSchema }]),
|
||||
],
|
||||
controllers: [InquiryLogsController],
|
||||
providers: [InquiryLogService],
|
||||
exports: [InquiryLogService],
|
||||
})
|
||||
export class LoggingModule {}
|
||||
51
src/logging/schemas/inquiry-log.schema.ts
Normal file
51
src/logging/schemas/inquiry-log.schema.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { HydratedDocument } from 'mongoose';
|
||||
import { InquiryStatus } from '../../common/enums/inquiry-status.enum';
|
||||
import { InquiryType } from '../../common/enums/inquiry-type.enum';
|
||||
import { ProviderName } from '../../common/enums/provider-name.enum';
|
||||
|
||||
export type InquiryLogDocument = HydratedDocument<InquiryLog>;
|
||||
|
||||
/**
|
||||
* MongoDB audit trail for every inquiry attempt.
|
||||
*/
|
||||
@Schema({ timestamps: { createdAt: true, updatedAt: false }, collection: 'inquiry_logs' })
|
||||
export class InquiryLog {
|
||||
@Prop({ required: true, enum: InquiryType })
|
||||
inquiryType!: InquiryType;
|
||||
|
||||
@Prop({ required: true, enum: ProviderName })
|
||||
provider!: ProviderName;
|
||||
|
||||
@Prop({ type: Object, required: true })
|
||||
requestPayload!: Record<string, unknown>;
|
||||
|
||||
@Prop({ type: Object, required: true })
|
||||
maskedRequest!: Record<string, unknown>;
|
||||
|
||||
@Prop({ type: Object })
|
||||
responsePayload?: Record<string, unknown>;
|
||||
|
||||
@Prop({ required: true, enum: InquiryStatus })
|
||||
status!: InquiryStatus;
|
||||
|
||||
@Prop({ required: true })
|
||||
duration!: number;
|
||||
|
||||
@Prop({ required: true, index: true })
|
||||
requestId!: string;
|
||||
|
||||
@Prop({ required: true, index: true })
|
||||
trackingCode!: string;
|
||||
|
||||
@Prop({ type: Object })
|
||||
error?: Record<string, unknown>;
|
||||
|
||||
createdAt?: Date;
|
||||
}
|
||||
|
||||
export const InquiryLogSchema = SchemaFactory.createForClass(InquiryLog);
|
||||
|
||||
InquiryLogSchema.index({ createdAt: -1, provider: 1, inquiryType: 1, status: 1 });
|
||||
InquiryLogSchema.index({ provider: 1, inquiryType: 1, createdAt: -1 });
|
||||
InquiryLogSchema.index({ 'error.code': 1, createdAt: -1 });
|
||||
Reference in New Issue
Block a user