diff --git a/services/audit-service/openapi.json b/services/audit-service/openapi.json
index bf437688bd..f76a516454 100644
--- a/services/audit-service/openapi.json
+++ b/services/audit-service/openapi.json
@@ -3,7 +3,7 @@
"info": {
"title": "Audit Service",
"version": "1.0.0",
- "description": "Audit logging microservice",
+ "description": "Audit logging Microservice",
"contact": {
"name": "Sourcefuse"
}
@@ -544,6 +544,20 @@
},
"actedOn": {
"type": "string"
+ },
+ "actedOnList": {
+ "type": "array",
+ "uniqueItems": true,
+ "items": {
+ "type": "string"
+ }
+ },
+ "actionGroupList": {
+ "type": "array",
+ "uniqueItems": true,
+ "items": {
+ "type": "string"
+ }
}
},
"additionalProperties": false,
diff --git a/services/audit-service/openapi.md b/services/audit-service/openapi.md
index 4c54ea84ad..4fd5db2f56 100644
--- a/services/audit-service/openapi.md
+++ b/services/audit-service/openapi.md
@@ -20,7 +20,7 @@ headingLevel: 2
> Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu.
-Audit logging microservice
+Audit logging Microservice
Base URLs:
@@ -46,7 +46,13 @@ const inputBody = '{
},
"deleted": true,
"entityId": "string",
- "actedOn": "string"
+ "actedOn": "string",
+ "actedOnList": [
+ "string"
+ ],
+ "actionGroupList": [
+ "string"
+ ]
}';
const headers = {
'Content-Type':'application/json',
@@ -77,7 +83,13 @@ const inputBody = {
},
"deleted": true,
"entityId": "string",
- "actedOn": "string"
+ "actedOn": "string",
+ "actedOnList": [
+ "string"
+ ],
+ "actionGroupList": [
+ "string"
+ ]
};
const headers = {
'Content-Type':'application/json',
@@ -116,7 +128,13 @@ fetch('/audit-logs/archive',
},
"deleted": true,
"entityId": "string",
- "actedOn": "string"
+ "actedOn": "string",
+ "actedOnList": [
+ "string"
+ ],
+ "actionGroupList": [
+ "string"
+ ]
}
```
@@ -953,7 +971,13 @@ AuditLogWithRelations
},
"deleted": true,
"entityId": "string",
- "actedOn": "string"
+ "actedOn": "string",
+ "actedOnList": [
+ "string"
+ ],
+ "actionGroupList": [
+ "string"
+ ]
}
```
@@ -970,6 +994,8 @@ CustomFilter
|deleted|boolean|false|none|none|
|entityId|string|false|none|none|
|actedOn|string|false|none|none|
+|actedOnList|[string]|false|none|none|
+|actionGroupList|[string]|false|none|none|
loopback.Count
diff --git a/services/audit-service/src/__tests__/integration/archive-log.controller.unit.ts b/services/audit-service/src/__tests__/integration/archive-log.controller.unit.ts
index 22eb2176ab..9b3f707b35 100644
--- a/services/audit-service/src/__tests__/integration/archive-log.controller.unit.ts
+++ b/services/audit-service/src/__tests__/integration/archive-log.controller.unit.ts
@@ -41,7 +41,7 @@ describe('POST /audit-logs/archive', () => {
afterEach(async () => {
await app.stop();
});
- it('archive logs when all 3 parameters are provided and deleted is false', async () => {
+ it('archive logs when 3 parameters are provided and deleted is false', async () => {
const customFilter: CustomFilter = new CustomFilter({
date: {
fromDate: testFromDate,
@@ -67,7 +67,7 @@ describe('POST /audit-logs/archive', () => {
actualResult.length + controllerResult.numberOfEntriesArchived,
).to.be.equal(archiveLogs.length);
});
- it('archive logs when all 3 parameters are provided and deleted is true', async () => {
+ it('archive logs when 3 parameters are provided and deleted is true', async () => {
const customFilter: CustomFilter = new CustomFilter({
date: {
fromDate: testFromDate,
@@ -176,7 +176,7 @@ describe('POST /audit-logs/archive', () => {
actualResult.length + controllerResult.numberOfEntriesArchived,
).to.be.equal(archiveLogs.length);
});
- it('archive logs when only actedOn parameter is provided', async () => {
+ it('archive logs when only date parameter is provided', async () => {
const customFilter: CustomFilter = new CustomFilter({
date: {
fromDate: testFromDate,
@@ -204,6 +204,58 @@ describe('POST /audit-logs/archive', () => {
const mappingLogFetch = mappingLogRepositoryStub.stubs.create;
mappingLogFetch.resolves(mappingLog);
+ const controllerResult = await auditLogController.archive(customFilter);
+ const actualResult = await auditLogRepository.find();
+ expect(
+ actualResult.length + controllerResult.numberOfEntriesArchived,
+ ).to.be.equal(archiveLogs.length);
+ });
+ it('archive logs when actedOnList parameter is provided', async () => {
+ const customFilter: CustomFilter = new CustomFilter({
+ actedOnList: ['Product'],
+ });
+ const {auditLogController} = getTestAuditController(app);
+ const mappingLogRepositoryStub = createStubInstance(MappingLogRepository);
+ const mappingLogFetch = mappingLogRepositoryStub.stubs.create;
+ mappingLogFetch.resolves(mappingLog);
+
+ const controllerResult = await auditLogController.archive(customFilter);
+ const actualResult = await auditLogRepository.find();
+ const expectedIds = ['6'];
+
+ expect(actualResult).to.be.containDeep(expectedIds.map(id => ({id})));
+ expect(
+ actualResult.length + controllerResult.numberOfEntriesArchived,
+ ).to.be.equal(archiveLogs.length);
+ });
+ it('archive logs when actedOnList parameter is provided and deleted is true', async () => {
+ const customFilter: CustomFilter = new CustomFilter({
+ actedOnList: ['Product'],
+ deleted: true,
+ });
+ const {auditLogController} = getTestAuditController(app);
+ const mappingLogRepositoryStub = createStubInstance(MappingLogRepository);
+ const mappingLogFetch = mappingLogRepositoryStub.stubs.create;
+ mappingLogFetch.resolves(mappingLog);
+
+ const controllerResult = await auditLogController.archive(customFilter);
+ const actualResult = await auditLogRepository.find();
+ const expectedIds = ['3', '6'];
+
+ expect(actualResult).to.be.containDeep(expectedIds.map(id => ({id})));
+ expect(
+ actualResult.length + controllerResult.numberOfEntriesArchived,
+ ).to.be.equal(archiveLogs.length);
+ });
+ it('archive logs when actionGroupList parameter is provided', async () => {
+ const customFilter: CustomFilter = new CustomFilter({
+ actionGroupList: ['Product_group'],
+ });
+ const {auditLogController} = getTestAuditController(app);
+ const mappingLogRepositoryStub = createStubInstance(MappingLogRepository);
+ const mappingLogFetch = mappingLogRepositoryStub.stubs.create;
+ mappingLogFetch.resolves(mappingLog);
+
const controllerResult = await auditLogController.archive(customFilter);
const actualResult = await auditLogRepository.find();
expect(
diff --git a/services/audit-service/src/__tests__/sample-data/archive-log.ts b/services/audit-service/src/__tests__/sample-data/archive-log.ts
index f9dc021788..b3c33ec853 100644
--- a/services/audit-service/src/__tests__/sample-data/archive-log.ts
+++ b/services/audit-service/src/__tests__/sample-data/archive-log.ts
@@ -131,6 +131,7 @@ export const archive1: AuditLog[] = [
qty: 0,
deleted: false,
} as JSONObject,
+ actionGroup: 'Product_group',
}),
new AuditLog({
id: '12',
@@ -155,6 +156,7 @@ export const archive1: AuditLog[] = [
qty: 0,
deleted: false,
} as JSONObject,
+ actionGroup: 'Product_group',
}),
new AuditLog({
id: '13',
@@ -172,6 +174,7 @@ export const archive1: AuditLog[] = [
qty: 0,
deleted: false,
} as JSONObject,
+ actionGroup: 'Product_group',
}),
];
export const archive2: AuditLog[] = [
diff --git a/services/audit-service/src/models/custom-filter.model.ts b/services/audit-service/src/models/custom-filter.model.ts
index aa5dec9a1e..35b78cebf1 100644
--- a/services/audit-service/src/models/custom-filter.model.ts
+++ b/services/audit-service/src/models/custom-filter.model.ts
@@ -39,6 +39,31 @@ export class CustomFilter extends CoreModel {
type: 'string',
})
actedOn?: string;
+
+ /** Both actedOnList and actionGroupList parameters accepts a
+ * list of values that you want to archive */
+
+ @property({
+ jsonSchema: {
+ type: 'array',
+ uniqueItems: true,
+ items: {
+ type: 'string',
+ },
+ },
+ })
+ actedOnList?: string[]; //to avoid breaking change
+
+ @property({
+ jsonSchema: {
+ type: 'array',
+ uniqueItems: true,
+ items: {
+ type: 'string',
+ },
+ },
+ })
+ actionGroupList?: string[];
}
export type CustomFilterWithRelations = CustomFilter;
diff --git a/services/audit-service/src/services/job-processing.service.ts b/services/audit-service/src/services/job-processing.service.ts
index 907144e556..f65918fc68 100644
--- a/services/audit-service/src/services/job-processing.service.ts
+++ b/services/audit-service/src/services/job-processing.service.ts
@@ -1,5 +1,10 @@
import {BindingScope, inject, injectable} from '@loopback/core';
-import {EntityCrudRepository, Filter, repository} from '@loopback/repository';
+import {
+ AnyObject,
+ EntityCrudRepository,
+ Filter,
+ repository,
+} from '@loopback/repository';
import {HttpErrors} from '@loopback/rest';
import {FileStatusKey} from '../enums/file-status-key.enum';
import {OperationKey} from '../enums/operation-key.enum';
@@ -24,6 +29,12 @@ import {
ColumnBuilderFn,
QuerySelectedFilesFn,
} from '../types';
+import {
+ checkActedOn,
+ checkActionGroup,
+ checkDates,
+ checkEntityId,
+} from '../utils/file-check';
@injectable({scope: BindingScope.TRANSIENT})
export class JobProcessingService {
constructor(
@@ -51,21 +62,7 @@ export class JobProcessingService {
const customFilter = new CustomFilter();
if (filter?.where && 'and' in filter.where) {
const andArray = filter.where?.and;
- for (const condition of andArray) {
- if (condition.actedAt?.between) {
- const [start, end] = condition.actedAt.between;
- customFilter.date = {
- fromDate: start,
- toDate: end,
- };
- }
- if (condition.actedOn) {
- customFilter.actedOn = condition.actedOn;
- }
- if (condition.entityId) {
- customFilter.entityId = condition.entityId;
- }
- }
+ this.buildCustomFilter(andArray, customFilter);
}
const mappingLogs: MappingLog[] = await this.mappingLogRepository.find();
const finalData: AuditLog[] = [];
@@ -73,16 +70,10 @@ export class JobProcessingService {
for (const mappingLog of mappingLogs) {
const filterUsed: CustomFilter = mappingLog.filterUsed as CustomFilter;
if (
- (customFilter.actedOn == null ||
- filterUsed.actedOn == null ||
- filterUsed.actedOn === customFilter.actedOn) &&
- (customFilter.entityId ??
- filterUsed.entityId ??
- filterUsed.entityId === customFilter.entityId) &&
- (customFilter.date == null ||
- filterUsed.date == null ||
- (filterUsed.date?.fromDate <= customFilter.date?.toDate &&
- filterUsed.date?.toDate >= customFilter.date?.fromDate))
+ checkActedOn(filterUsed, customFilter) &&
+ checkActionGroup(filterUsed, customFilter) &&
+ checkEntityId(filterUsed, customFilter) &&
+ checkDates(filterUsed, customFilter)
) {
//logs from s3
finalData.push(
@@ -106,4 +97,35 @@ export class JobProcessingService {
throw new HttpErrors.UnprocessableEntity(error.message);
}
}
+ getFilter(inquiredFilter: string | AnyObject): string[] {
+ if (typeof inquiredFilter === 'string') {
+ return [inquiredFilter];
+ } else return inquiredFilter.inq;
+ }
+
+ haveCommonElements(arr1: string[], arr2: string[]): boolean {
+ return arr1.some(item => arr2.includes(item));
+ }
+
+ buildCustomFilter(andArray: AnyObject[], customFilter: CustomFilter) {
+ for (const condition of andArray) {
+ if (condition.actedAt?.between) {
+ const [start, end] = condition.actedAt.between;
+ customFilter.date = {
+ fromDate: start,
+ toDate: end,
+ };
+ }
+ if (condition.actedOn) {
+ //even if actedOn is a string, it is converted to an array for easy comparision
+ customFilter.actedOnList = this.getFilter(condition.actedOn);
+ }
+ if (condition.actionGroup) {
+ customFilter.actionGroupList = this.getFilter(condition.actionGroup);
+ }
+ if (condition.entityId) {
+ customFilter.entityId = condition.entityId;
+ }
+ }
+ }
}
diff --git a/services/audit-service/src/utils/construct-where.ts b/services/audit-service/src/utils/construct-where.ts
index 80bfdfad77..887451dafd 100644
--- a/services/audit-service/src/utils/construct-where.ts
+++ b/services/audit-service/src/utils/construct-where.ts
@@ -43,9 +43,19 @@ export async function constructWhere(customFilter: CustomFilter) {
});
}
- if (customFilter.actedOn) {
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ if (customFilter.actedOn || customFilter.actedOnList) {
+ const array = customFilter.actedOn
+ ? [customFilter.actedOn]
+ : customFilter.actedOnList;
where.and.push({
- actedOn: customFilter.actedOn,
+ actedOn: {inq: array},
+ });
+ }
+
+ if (customFilter.actionGroupList) {
+ where.and.push({
+ actionGroup: {inq: customFilter.actionGroupList},
});
}
return where;
diff --git a/services/audit-service/src/utils/file-check.ts b/services/audit-service/src/utils/file-check.ts
new file mode 100644
index 0000000000..d49f3b73a5
--- /dev/null
+++ b/services/audit-service/src/utils/file-check.ts
@@ -0,0 +1,63 @@
+import {CustomFilter} from '../models';
+
+export function checkActedOn(
+ filterUsed: CustomFilter,
+ customFilter: CustomFilter,
+): boolean | undefined {
+ // eslint-disable-next-line prefer-const
+ let {actedOn, actedOnList} = filterUsed;
+ const customActedOnList = customFilter.actedOnList;
+ actedOnList = actedOnList ?? [];
+ // Check if both actedOn and actedOnList are null or if customFilter's actedOnList is null
+ // Check if filterUsed.actedOn is defined and included in customFilter.actedOnList
+ // Check if both actedOnLists have common elements
+ if (!!actedOn) {
+ actedOnList = [...new Set([...actedOnList, actedOn])];
+ }
+
+ return (
+ actedOnList.length === 0 ||
+ !customActedOnList ||
+ haveCommonElements(customActedOnList, actedOnList)
+ );
+}
+
+export function checkActionGroup(
+ filterUsed: CustomFilter,
+ customFilter: CustomFilter,
+): boolean {
+ const actionGroupList = filterUsed.actionGroupList;
+ const customActionGroupList = customFilter.actionGroupList;
+ return (
+ !customActionGroupList ||
+ !actionGroupList ||
+ haveCommonElements(actionGroupList, customActionGroupList)
+ );
+}
+
+export function checkEntityId(
+ filterUsed: CustomFilter,
+ customFilter: CustomFilter,
+): boolean {
+ return (
+ !customFilter.entityId ||
+ !filterUsed.entityId ||
+ filterUsed.entityId === customFilter.entityId
+ );
+}
+
+export function checkDates(
+ filterUsed: CustomFilter,
+ customFilter: CustomFilter,
+) {
+ return (
+ !customFilter.date ||
+ !filterUsed.date ||
+ (customFilter.date.toDate >= filterUsed.date.fromDate &&
+ customFilter.date.fromDate <= filterUsed.date.toDate)
+ );
+}
+
+export function haveCommonElements(arr1: string[], arr2: string[]): boolean {
+ return arr1.some(item => arr2.includes(item));
+}