Skip to content

Commit 2a2891d

Browse files
feat(audit-service): add support for multiple actedon and actiongroup (#2124)
add support for multiple actedon and actiongroup GH-2119 Co-authored-by: yeshamavani <[email protected]>
1 parent 5ef082c commit 2a2891d

File tree

8 files changed

+252
-37
lines changed

8 files changed

+252
-37
lines changed

services/audit-service/openapi.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"info": {
44
"title": "Audit Service",
55
"version": "1.0.0",
6-
"description": "Audit logging microservice",
6+
"description": "Audit logging Microservice",
77
"contact": {
88
"name": "Sourcefuse"
99
}
@@ -544,6 +544,20 @@
544544
},
545545
"actedOn": {
546546
"type": "string"
547+
},
548+
"actedOnList": {
549+
"type": "array",
550+
"uniqueItems": true,
551+
"items": {
552+
"type": "string"
553+
}
554+
},
555+
"actionGroupList": {
556+
"type": "array",
557+
"uniqueItems": true,
558+
"items": {
559+
"type": "string"
560+
}
547561
}
548562
},
549563
"additionalProperties": false,

services/audit-service/openapi.md

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ headingLevel: 2
2020

2121
> Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu.
2222
23-
Audit logging microservice
23+
Audit logging Microservice
2424

2525
Base URLs:
2626

@@ -46,7 +46,13 @@ const inputBody = '{
4646
},
4747
"deleted": true,
4848
"entityId": "string",
49-
"actedOn": "string"
49+
"actedOn": "string",
50+
"actedOnList": [
51+
"string"
52+
],
53+
"actionGroupList": [
54+
"string"
55+
]
5056
}';
5157
const headers = {
5258
'Content-Type':'application/json',
@@ -77,7 +83,13 @@ const inputBody = {
7783
},
7884
"deleted": true,
7985
"entityId": "string",
80-
"actedOn": "string"
86+
"actedOn": "string",
87+
"actedOnList": [
88+
"string"
89+
],
90+
"actionGroupList": [
91+
"string"
92+
]
8193
};
8294
const headers = {
8395
'Content-Type':'application/json',
@@ -116,7 +128,13 @@ fetch('/audit-logs/archive',
116128
},
117129
"deleted": true,
118130
"entityId": "string",
119-
"actedOn": "string"
131+
"actedOn": "string",
132+
"actedOnList": [
133+
"string"
134+
],
135+
"actionGroupList": [
136+
"string"
137+
]
120138
}
121139
```
122140

@@ -953,7 +971,13 @@ AuditLogWithRelations
953971
},
954972
"deleted": true,
955973
"entityId": "string",
956-
"actedOn": "string"
974+
"actedOn": "string",
975+
"actedOnList": [
976+
"string"
977+
],
978+
"actionGroupList": [
979+
"string"
980+
]
957981
}
958982

959983
```
@@ -970,6 +994,8 @@ CustomFilter
970994
|deleted|boolean|false|none|none|
971995
|entityId|string|false|none|none|
972996
|actedOn|string|false|none|none|
997+
|actedOnList|[string]|false|none|none|
998+
|actionGroupList|[string]|false|none|none|
973999

9741000
<h2 id="tocS_loopback.Count">loopback.Count</h2>
9751001
<!-- backwards compatibility -->

services/audit-service/src/__tests__/integration/archive-log.controller.unit.ts

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe('POST /audit-logs/archive', () => {
4141
afterEach(async () => {
4242
await app.stop();
4343
});
44-
it('archive logs when all 3 parameters are provided and deleted is false', async () => {
44+
it('archive logs when 3 parameters are provided and deleted is false', async () => {
4545
const customFilter: CustomFilter = new CustomFilter({
4646
date: {
4747
fromDate: testFromDate,
@@ -67,7 +67,7 @@ describe('POST /audit-logs/archive', () => {
6767
actualResult.length + controllerResult.numberOfEntriesArchived,
6868
).to.be.equal(archiveLogs.length);
6969
});
70-
it('archive logs when all 3 parameters are provided and deleted is true', async () => {
70+
it('archive logs when 3 parameters are provided and deleted is true', async () => {
7171
const customFilter: CustomFilter = new CustomFilter({
7272
date: {
7373
fromDate: testFromDate,
@@ -176,7 +176,7 @@ describe('POST /audit-logs/archive', () => {
176176
actualResult.length + controllerResult.numberOfEntriesArchived,
177177
).to.be.equal(archiveLogs.length);
178178
});
179-
it('archive logs when only actedOn parameter is provided', async () => {
179+
it('archive logs when only date parameter is provided', async () => {
180180
const customFilter: CustomFilter = new CustomFilter({
181181
date: {
182182
fromDate: testFromDate,
@@ -204,6 +204,58 @@ describe('POST /audit-logs/archive', () => {
204204
const mappingLogFetch = mappingLogRepositoryStub.stubs.create;
205205
mappingLogFetch.resolves(mappingLog);
206206

207+
const controllerResult = await auditLogController.archive(customFilter);
208+
const actualResult = await auditLogRepository.find();
209+
expect(
210+
actualResult.length + controllerResult.numberOfEntriesArchived,
211+
).to.be.equal(archiveLogs.length);
212+
});
213+
it('archive logs when actedOnList parameter is provided', async () => {
214+
const customFilter: CustomFilter = new CustomFilter({
215+
actedOnList: ['Product'],
216+
});
217+
const {auditLogController} = getTestAuditController(app);
218+
const mappingLogRepositoryStub = createStubInstance(MappingLogRepository);
219+
const mappingLogFetch = mappingLogRepositoryStub.stubs.create;
220+
mappingLogFetch.resolves(mappingLog);
221+
222+
const controllerResult = await auditLogController.archive(customFilter);
223+
const actualResult = await auditLogRepository.find();
224+
const expectedIds = ['6'];
225+
226+
expect(actualResult).to.be.containDeep(expectedIds.map(id => ({id})));
227+
expect(
228+
actualResult.length + controllerResult.numberOfEntriesArchived,
229+
).to.be.equal(archiveLogs.length);
230+
});
231+
it('archive logs when actedOnList parameter is provided and deleted is true', async () => {
232+
const customFilter: CustomFilter = new CustomFilter({
233+
actedOnList: ['Product'],
234+
deleted: true,
235+
});
236+
const {auditLogController} = getTestAuditController(app);
237+
const mappingLogRepositoryStub = createStubInstance(MappingLogRepository);
238+
const mappingLogFetch = mappingLogRepositoryStub.stubs.create;
239+
mappingLogFetch.resolves(mappingLog);
240+
241+
const controllerResult = await auditLogController.archive(customFilter);
242+
const actualResult = await auditLogRepository.find();
243+
const expectedIds = ['3', '6'];
244+
245+
expect(actualResult).to.be.containDeep(expectedIds.map(id => ({id})));
246+
expect(
247+
actualResult.length + controllerResult.numberOfEntriesArchived,
248+
).to.be.equal(archiveLogs.length);
249+
});
250+
it('archive logs when actionGroupList parameter is provided', async () => {
251+
const customFilter: CustomFilter = new CustomFilter({
252+
actionGroupList: ['Product_group'],
253+
});
254+
const {auditLogController} = getTestAuditController(app);
255+
const mappingLogRepositoryStub = createStubInstance(MappingLogRepository);
256+
const mappingLogFetch = mappingLogRepositoryStub.stubs.create;
257+
mappingLogFetch.resolves(mappingLog);
258+
207259
const controllerResult = await auditLogController.archive(customFilter);
208260
const actualResult = await auditLogRepository.find();
209261
expect(

services/audit-service/src/__tests__/sample-data/archive-log.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export const archive1: AuditLog[] = [
131131
qty: 0,
132132
deleted: false,
133133
} as JSONObject,
134+
actionGroup: 'Product_group',
134135
}),
135136
new AuditLog({
136137
id: '12',
@@ -155,6 +156,7 @@ export const archive1: AuditLog[] = [
155156
qty: 0,
156157
deleted: false,
157158
} as JSONObject,
159+
actionGroup: 'Product_group',
158160
}),
159161
new AuditLog({
160162
id: '13',
@@ -172,6 +174,7 @@ export const archive1: AuditLog[] = [
172174
qty: 0,
173175
deleted: false,
174176
} as JSONObject,
177+
actionGroup: 'Product_group',
175178
}),
176179
];
177180
export const archive2: AuditLog[] = [

services/audit-service/src/models/custom-filter.model.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,31 @@ export class CustomFilter extends CoreModel<CustomFilter> {
3939
type: 'string',
4040
})
4141
actedOn?: string;
42+
43+
/** Both actedOnList and actionGroupList parameters accepts a
44+
* list of values that you want to archive */
45+
46+
@property({
47+
jsonSchema: {
48+
type: 'array',
49+
uniqueItems: true,
50+
items: {
51+
type: 'string',
52+
},
53+
},
54+
})
55+
actedOnList?: string[]; //to avoid breaking change
56+
57+
@property({
58+
jsonSchema: {
59+
type: 'array',
60+
uniqueItems: true,
61+
items: {
62+
type: 'string',
63+
},
64+
},
65+
})
66+
actionGroupList?: string[];
4267
}
4368

4469
export type CustomFilterWithRelations = CustomFilter;

services/audit-service/src/services/job-processing.service.ts

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import {BindingScope, inject, injectable} from '@loopback/core';
2-
import {EntityCrudRepository, Filter, repository} from '@loopback/repository';
2+
import {
3+
AnyObject,
4+
EntityCrudRepository,
5+
Filter,
6+
repository,
7+
} from '@loopback/repository';
38
import {HttpErrors} from '@loopback/rest';
49
import {FileStatusKey} from '../enums/file-status-key.enum';
510
import {OperationKey} from '../enums/operation-key.enum';
@@ -24,6 +29,12 @@ import {
2429
ColumnBuilderFn,
2530
QuerySelectedFilesFn,
2631
} from '../types';
32+
import {
33+
checkActedOn,
34+
checkActionGroup,
35+
checkDates,
36+
checkEntityId,
37+
} from '../utils/file-check';
2738
@injectable({scope: BindingScope.TRANSIENT})
2839
export class JobProcessingService {
2940
constructor(
@@ -51,38 +62,18 @@ export class JobProcessingService {
5162
const customFilter = new CustomFilter();
5263
if (filter?.where && 'and' in filter.where) {
5364
const andArray = filter.where?.and;
54-
for (const condition of andArray) {
55-
if (condition.actedAt?.between) {
56-
const [start, end] = condition.actedAt.between;
57-
customFilter.date = {
58-
fromDate: start,
59-
toDate: end,
60-
};
61-
}
62-
if (condition.actedOn) {
63-
customFilter.actedOn = condition.actedOn;
64-
}
65-
if (condition.entityId) {
66-
customFilter.entityId = condition.entityId;
67-
}
68-
}
65+
this.buildCustomFilter(andArray, customFilter);
6966
}
7067
const mappingLogs: MappingLog[] = await this.mappingLogRepository.find();
7168
const finalData: AuditLog[] = [];
7269
/*Creating a for loop over all the archived files*/
7370
for (const mappingLog of mappingLogs) {
7471
const filterUsed: CustomFilter = mappingLog.filterUsed as CustomFilter;
7572
if (
76-
(customFilter.actedOn == null ||
77-
filterUsed.actedOn == null ||
78-
filterUsed.actedOn === customFilter.actedOn) &&
79-
(customFilter.entityId ??
80-
filterUsed.entityId ??
81-
filterUsed.entityId === customFilter.entityId) &&
82-
(customFilter.date == null ||
83-
filterUsed.date == null ||
84-
(filterUsed.date?.fromDate <= customFilter.date?.toDate &&
85-
filterUsed.date?.toDate >= customFilter.date?.fromDate))
73+
checkActedOn(filterUsed, customFilter) &&
74+
checkActionGroup(filterUsed, customFilter) &&
75+
checkEntityId(filterUsed, customFilter) &&
76+
checkDates(filterUsed, customFilter)
8677
) {
8778
//logs from s3
8879
finalData.push(
@@ -106,4 +97,35 @@ export class JobProcessingService {
10697
throw new HttpErrors.UnprocessableEntity(error.message);
10798
}
10899
}
100+
getFilter(inquiredFilter: string | AnyObject): string[] {
101+
if (typeof inquiredFilter === 'string') {
102+
return [inquiredFilter];
103+
} else return inquiredFilter.inq;
104+
}
105+
106+
haveCommonElements(arr1: string[], arr2: string[]): boolean {
107+
return arr1.some(item => arr2.includes(item));
108+
}
109+
110+
buildCustomFilter(andArray: AnyObject[], customFilter: CustomFilter) {
111+
for (const condition of andArray) {
112+
if (condition.actedAt?.between) {
113+
const [start, end] = condition.actedAt.between;
114+
customFilter.date = {
115+
fromDate: start,
116+
toDate: end,
117+
};
118+
}
119+
if (condition.actedOn) {
120+
//even if actedOn is a string, it is converted to an array for easy comparision
121+
customFilter.actedOnList = this.getFilter(condition.actedOn);
122+
}
123+
if (condition.actionGroup) {
124+
customFilter.actionGroupList = this.getFilter(condition.actionGroup);
125+
}
126+
if (condition.entityId) {
127+
customFilter.entityId = condition.entityId;
128+
}
129+
}
130+
}
109131
}

services/audit-service/src/utils/construct-where.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,19 @@ export async function constructWhere(customFilter: CustomFilter) {
4343
});
4444
}
4545

46-
if (customFilter.actedOn) {
46+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
47+
if (customFilter.actedOn || customFilter.actedOnList) {
48+
const array = customFilter.actedOn
49+
? [customFilter.actedOn]
50+
: customFilter.actedOnList;
4751
where.and.push({
48-
actedOn: customFilter.actedOn,
52+
actedOn: {inq: array},
53+
});
54+
}
55+
56+
if (customFilter.actionGroupList) {
57+
where.and.push({
58+
actionGroup: {inq: customFilter.actionGroupList},
4959
});
5060
}
5161
return where;

0 commit comments

Comments
 (0)