Skip to content

Commit 5a25de1

Browse files
committed
feat: add some methods
1 parent 7e80ff6 commit 5a25de1

7 files changed

Lines changed: 342 additions & 41 deletions

File tree

src/logger/domain/interfaces/log-repository.interface.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ export interface ILogRepository {
99
context: string | undefined,
1010
page: number,
1111
limit: number,
12-
): Promise<{ logs: Logger[]; total: number }>;
12+
): Promise<{
13+
length: number;
14+
logs: Logger[];
15+
total: number;
16+
}>;
1317
saveLog(log: LogEntryDto): Promise<Logger>; // Fix: Return `Logger` and use camelCase param
18+
deleteLogById(_id: string): Promise<void>; // Fix: Use camelCase param
19+
getLogStats(
20+
level: string | undefined,
21+
context: string | undefined,
22+
): Promise<any>; // Fix: Return `any` and use camelCase params
1423
}

src/logger/infrastructure/repositories/log.repository.ts

Lines changed: 71 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export class LogRepository implements ILogRepository {
1111
@InjectModel(Logger.name)
1212
private readonly loggerModel: Model<Logger>,
1313
) {}
14-
1514
async findLogById(_id: string): Promise<Logger | null> {
1615
try {
1716
return await this.loggerModel.findById(_id).lean().exec();
@@ -20,37 +19,6 @@ export class LogRepository implements ILogRepository {
2019
throw new Error('Failed to retrieve log');
2120
}
2221
}
23-
24-
async findLogsByFilter(
25-
level: string,
26-
context: string | undefined,
27-
page: number,
28-
limit: number,
29-
): Promise<{ logs: Logger[]; total: number }> {
30-
try {
31-
const filter: { level: string; context?: string } = { level };
32-
33-
if (context) {
34-
filter.context = context;
35-
}
36-
37-
const [logs, total] = await Promise.all([
38-
this.loggerModel
39-
.find(filter)
40-
.sort({ timestamp: -1 }) // Newest first
41-
.skip((page - 1) * limit)
42-
.limit(limit)
43-
.lean()
44-
.exec(),
45-
this.loggerModel.countDocuments(filter).exec(),
46-
]);
47-
48-
return { logs, total };
49-
} catch (error) {
50-
console.error('Error finding logs by filter:', error);
51-
throw new Error('Failed to retrieve logs');
52-
}
53-
}
5422
async saveLog(log: LogEntryDto): Promise<Logger> {
5523
try {
5624
const logEntry = new this.loggerModel(log);
@@ -61,4 +29,75 @@ export class LogRepository implements ILogRepository {
6129
throw new Error('Failed to save log');
6230
}
6331
}
32+
findLogsByFilter(
33+
_level: string,
34+
_context: string | undefined,
35+
_page: number,
36+
_limit: number,
37+
): Promise<{ length: number; logs: Logger[]; total: number }> {
38+
const filter: { [key: string]: string } = {};
39+
if (_level) {
40+
filter.level = _level;
41+
}
42+
if (_context) {
43+
filter.context = _context;
44+
}
45+
const skip = (_page - 1) * _limit;
46+
return this.loggerModel
47+
.find(filter)
48+
.skip(skip)
49+
.limit(_limit)
50+
.lean()
51+
.exec()
52+
.then(async (logs) => {
53+
const total = await this.loggerModel.countDocuments(filter);
54+
return {
55+
length: logs.length,
56+
logs,
57+
total,
58+
};
59+
})
60+
.catch((error) => {
61+
console.error('Error finding logs by filter:', error);
62+
throw new Error('Failed to retrieve logs');
63+
});
64+
}
65+
async deleteLogById(_id: string): Promise<void> {
66+
try {
67+
await this.loggerModel.findByIdAndDelete(_id).exec();
68+
} catch (error) {
69+
console.error('Error deleting log by ID:', error);
70+
throw new Error('Failed to delete log');
71+
}
72+
}
73+
getLogStats(
74+
_level: string | undefined,
75+
_context: string | undefined,
76+
): Promise<any> {
77+
const filter: { [key: string]: string } = {};
78+
if (_level) {
79+
filter.level = _level;
80+
}
81+
if (_context) {
82+
filter.context = _context;
83+
}
84+
return this.loggerModel
85+
.aggregate([
86+
{ $match: filter },
87+
{
88+
$group: {
89+
_id: {
90+
level: '$_level',
91+
context: '$_context',
92+
},
93+
count: { $sum: 1 },
94+
},
95+
},
96+
])
97+
.exec()
98+
.catch((error) => {
99+
console.error('Error getting log stats:', error);
100+
throw new Error('Failed to retrieve log stats');
101+
});
102+
}
64103
}

src/logger/services/logger.service.db.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,19 @@ export class LoggerServiceDb {
3737
}
3838
return log;
3939
}
40+
41+
async deleteLogById(id: string) {
42+
const log = await this.logRepository.findLogById(id);
43+
if (!log) {
44+
throw new Error(`Log with ID: ${id} not found`);
45+
}
46+
await this.logRepository.deleteLogById(id);
47+
}
48+
49+
async getLogStats(
50+
level: string | undefined,
51+
context: string | undefined,
52+
): Promise<any> {
53+
return await this.logRepository.getLogStats(level, context);
54+
}
4055
}

src/notification/domain/interfaces/notification-repository.interface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ export interface INotificationRepository {
1616
findById(id: string): Promise<Notification | null>;
1717

1818
findAll(skip: number, limit: number): Promise<Notification[]>;
19+
20+
deleteById(id: string): Promise<void>;
1921
}

src/notification/infrastructure/repositories/notification.repository.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export class NotificationRepository implements INotificationRepository {
1111
@InjectModel('Notification')
1212
private readonly notificationModel: Model<NotificationDocument>,
1313
) {}
14-
1514
findById(id: string): Promise<Notification | null> {
1615
throw new Error(`Method not implemented. ID: ${id}`);
1716
}
@@ -30,6 +29,10 @@ export class NotificationRepository implements INotificationRepository {
3029
return this.toEntity(saved);
3130
}
3231

32+
async deleteById(id: string): Promise<void> {
33+
await this.notificationModel.findByIdAndDelete(id).exec();
34+
}
35+
3336
private toEntity(doc: NotificationDocument): Notification {
3437
return new Notification(
3538
doc.recipient,

src/notification/presentation/controllers/log.controller.ts

Lines changed: 119 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
1-
import { Controller, Get, HttpStatus, Param, Query } from '@nestjs/common';
1+
import {
2+
Controller,
3+
Get,
4+
HttpStatus,
5+
Param,
6+
Query,
7+
Delete,
8+
HttpCode,
9+
NotFoundException,
10+
BadRequestException,
11+
} from '@nestjs/common';
12+
import {
13+
ApiOperation,
14+
ApiResponse,
15+
ApiQuery,
16+
ApiTags,
17+
ApiParam,
18+
} from '@nestjs/swagger';
219
import { LoggerServiceDb } from '../../../logger/services/logger.service.db';
3-
import { ApiOperation, ApiResponse, ApiQuery, ApiTags } from '@nestjs/swagger';
420

521
@ApiTags('Logs')
622
@Controller('api/logs')
@@ -35,14 +51,28 @@ export class LogController {
3551
type: Number,
3652
description: 'Items per page (default: 10)',
3753
})
38-
getLogs(
54+
async getLogs(
3955
@Query('level') level: string,
4056
@Query('context') context?: string,
4157
@Query('page') page = 1,
4258
@Query('limit') limit = 10,
4359
) {
44-
const logs = this.logService.getLogsByFilter(level, context, page, limit);
45-
return logs;
60+
const logsResult = await this.logService.getLogsByFilter(
61+
level,
62+
context,
63+
page,
64+
limit,
65+
);
66+
const logs: unknown = logsResult;
67+
if (!Array.isArray(logs)) {
68+
throw new BadRequestException('Logs result is not an array');
69+
}
70+
return {
71+
data: logs,
72+
total: logs.length,
73+
page,
74+
totalPages: Math.ceil(logs.length / limit),
75+
};
4676
}
4777

4878
@Get(':id')
@@ -51,10 +81,92 @@ export class LogController {
5181
status: HttpStatus.OK,
5282
description: 'Log retrieved successfully',
5383
})
84+
@ApiParam({
85+
name: 'id',
86+
type: String,
87+
description: 'Log ID',
88+
})
89+
@ApiResponse({
90+
status: 404,
91+
description: 'Log not found',
92+
})
5493
async getLogById(@Param('id') id: string) {
55-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
5694
const log = await this.logService.getLogById(id);
57-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
95+
if (!log) {
96+
throw new NotFoundException(`Log with ID ${id} not found`);
97+
}
5898
return { data: log };
5999
}
100+
101+
@Delete(':id')
102+
@HttpCode(HttpStatus.NO_CONTENT)
103+
@ApiOperation({ summary: 'Delete a specific log by ID' })
104+
@ApiParam({
105+
name: 'id',
106+
type: String,
107+
description: 'Log ID',
108+
})
109+
@ApiResponse({
110+
status: 204,
111+
description: 'Log deleted successfully',
112+
})
113+
@ApiResponse({
114+
status: 404,
115+
description: 'Log not found',
116+
})
117+
async deleteLogById(@Param('id') id: string): Promise<void> {
118+
const log = await this.logService.getLogById(id);
119+
if (!log) {
120+
throw new NotFoundException(`Log with ID ${id} not found`);
121+
}
122+
await this.logService.deleteLogById(id);
123+
}
124+
125+
@Delete()
126+
@HttpCode(HttpStatus.NO_CONTENT)
127+
@ApiOperation({ summary: 'Delete logs by filter' })
128+
@ApiQuery({
129+
name: 'level',
130+
required: true,
131+
description: 'Log level (e.g., info, error)',
132+
})
133+
@ApiQuery({
134+
name: 'context',
135+
required: false,
136+
description: 'Context (e.g., NotificationController)',
137+
})
138+
@ApiResponse({
139+
status: 204,
140+
description: 'Logs deleted successfully',
141+
})
142+
@ApiResponse({
143+
status: 400,
144+
description: 'No logs found for the given filter',
145+
})
146+
@Get('stats')
147+
@ApiOperation({ summary: 'Get log statistics' })
148+
@ApiResponse({
149+
status: HttpStatus.OK,
150+
description: 'Log statistics retrieved successfully',
151+
})
152+
@ApiQuery({
153+
name: 'level',
154+
required: false,
155+
description: 'Filter by log level (e.g., info, error)',
156+
})
157+
@ApiQuery({
158+
name: 'context',
159+
required: false,
160+
description: 'Filter by context (e.g., NotificationController)',
161+
})
162+
async getLogStats(
163+
@Query('level') level?: string,
164+
@Query('context') context?: string,
165+
): Promise<{ data: Record<string, unknown> }> {
166+
const stats = (await this.logService.getLogStats(level, context)) as Record<
167+
string,
168+
unknown
169+
>;
170+
return { data: stats };
171+
}
60172
}

0 commit comments

Comments
 (0)