Skip to content

Commit 69dd2df

Browse files
author
Lichao Xue
committed
support to audit logs
Signed-off-by: xuelichao <[email protected]>
1 parent b0c74a0 commit 69dd2df

31 files changed

+1501
-101
lines changed

Diff for: src/portal/src/app/base/left-side-nav/clearing-job/audit-log-purge/set-job/set-job.component.html

+20-17
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@
141141
)
142142
}">
143143
<span class="font-style required flex-200"
144-
>{{ 'CLEARANCES.INCLUDED_OPERATIONS' | translate
144+
>{{ 'CLEARANCES.INCLUDED_RESOURCE_TYPES' | translate
145145
}}<clr-tooltip>
146146
<clr-icon
147147
clrTooltipTrigger
@@ -152,37 +152,40 @@
152152
clrSize="lg"
153153
*clrIfOpen>
154154
<span>{{
155-
'CLEARANCES.INCLUDED_OPERATION_TOOLTIP' | translate
155+
'CLEARANCES.INCLUDED_RESOURCE_TYPE_TOOLTIP'
156+
| translate
156157
}}</span>
157158
</clr-tooltip-content>
158159
</clr-tooltip></span
159160
>
160161
<div
161-
class="clr-control-container clr-control-inline"
162-
[class.clr-error]="!(selectedOperations?.length > 0)">
162+
class="clr-control-container"
163+
[class.clr-error]="!(selectedResourceTypes?.length > 0)">
163164
<div
164165
class="clr-checkbox-wrapper"
165-
*ngFor="let item of operations">
166+
*ngFor="let item of resourceTypes">
166167
<input
167168
type="checkbox"
168-
id="{{ item }}"
169-
name="operations"
170-
value="{{ item }}"
169+
id="{{ item.event_id }}"
170+
name="resourceTypes"
171+
value="{{ item.name }}"
171172
class="clr-checkbox"
172-
(change)="setOperation(item)"
173-
[checked]="hasOperation(item)" />
174-
<label for="{{ item }}" class="clr-control-label">{{
175-
operationsToText(item) | translate
176-
}}</label>
173+
(change)="setResourceType(item.name)"
174+
[checked]="hasResourceType(item.name)" />
175+
<label
176+
for="{{ item.event_id }}"
177+
class="clr-control-label"
178+
>{{ item.label }}</label
179+
>
177180
</div>
178181
<div
179182
class="clr-subtext-wrapper"
180-
*ngIf="!(selectedOperations?.length > 0)">
183+
*ngIf="!(selectedResourceTypes?.length > 0)">
181184
<clr-icon
182185
class="clr-validate-icon"
183186
shape="exclamation-circle"></clr-icon>
184187
<span class="clr-subtext">{{
185-
'CLEARANCES.INCLUDED_OPERATION_ERROR' | translate
188+
'CLEARANCES.INCLUDED_RESOURCE_TYPE_ERROR' | translate
186189
}}</span>
187190
</div>
188191
</div>
@@ -197,7 +200,7 @@
197200
[disabled]="
198201
disableGC ||
199202
purgeForm.invalid ||
200-
!(selectedOperations?.length > 0)
203+
!(selectedResourceTypes?.length > 0)
201204
">
202205
{{ 'CLEARANCES.PURGE_NOW' | translate }}
203206
</button>
@@ -210,7 +213,7 @@
210213
[disabled]="
211214
dryRunOnGoing ||
212215
purgeForm.invalid ||
213-
!(selectedOperations?.length > 0)
216+
!(selectedResourceTypes?.length > 0)
214217
">
215218
{{ 'TAG_RETENTION.WHAT_IF_RUN' | translate }}
216219
</button>

Diff for: src/portal/src/app/base/left-side-nav/clearing-job/audit-log-purge/set-job/set-job.component.spec.ts

+33-3
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
22
import { ErrorHandler } from '../../../../../shared/units/error-handler';
33
import { CronScheduleComponent } from '../../../../../shared/components/cron-schedule';
44
import { CronTooltipComponent } from '../../../../../shared/components/cron-schedule';
5-
import { of } from 'rxjs';
5+
import { delay, of } from 'rxjs';
66
import { SharedTestingModule } from '../../../../../shared/shared.module';
77
import { SetJobComponent } from './set-job.component';
88
import { PurgeService } from 'ng-swagger-gen/services/purge.service';
99
import { NO_ERRORS_SCHEMA } from '@angular/core';
10+
import { AuditlogService } from 'ng-swagger-gen/services';
11+
import { HttpHeaders, HttpResponse } from '@angular/common/http';
1012

1113
describe('GcComponent', () => {
1214
let component: SetJobComponent;
1315
let fixture: ComponentFixture<SetJobComponent>;
1416
let purgeService: PurgeService;
17+
let auditlogService: AuditlogService;
1518
let mockSchedule = [];
1619
const fakedErrorHandler = {
1720
error(error) {
@@ -23,6 +26,29 @@ describe('GcComponent', () => {
2326
};
2427
let spySchedule: jasmine.Spy;
2528
let spyGcNow: jasmine.Spy;
29+
const mockedAuditLogs = [
30+
{
31+
event_type: 'create_artifact',
32+
},
33+
{
34+
event_type: 'delete_artifact',
35+
},
36+
{
37+
event_type: 'pull_artifact',
38+
},
39+
];
40+
const fakedAuditlogService = {
41+
listAuditLogEventTypesResponse() {
42+
return of(
43+
new HttpResponse({
44+
body: mockedAuditLogs,
45+
headers: new HttpHeaders({
46+
'x-total-count': '18',
47+
}),
48+
})
49+
).pipe(delay(0));
50+
},
51+
};
2652
beforeEach(() => {
2753
TestBed.configureTestingModule({
2854
imports: [SharedTestingModule],
@@ -31,22 +57,26 @@ describe('GcComponent', () => {
3157
CronScheduleComponent,
3258
CronTooltipComponent,
3359
],
34-
providers: [{ provide: ErrorHandler, useValue: fakedErrorHandler }],
60+
providers: [
61+
{ provide: ErrorHandler, useValue: fakedErrorHandler },
62+
{ provide: AuditlogService, useValue: fakedAuditlogService },
63+
],
3564
schemas: [NO_ERRORS_SCHEMA],
3665
}).compileComponents();
3766
});
3867

3968
beforeEach(() => {
4069
fixture = TestBed.createComponent(SetJobComponent);
4170
component = fixture.componentInstance;
42-
71+
auditlogService = fixture.debugElement.injector.get(AuditlogService);
4372
purgeService = fixture.debugElement.injector.get(PurgeService);
4473
spySchedule = spyOn(purgeService, 'getPurgeSchedule').and.returnValues(
4574
of(mockSchedule as any)
4675
);
4776
spyGcNow = spyOn(purgeService, 'createPurgeSchedule').and.returnValues(
4877
of(null)
4978
);
79+
component.selectedResourceTypes = ['create_artifact'];
5080
fixture.detectChanges();
5181
});
5282
it('should create', () => {

Diff for: src/portal/src/app/base/left-side-nav/clearing-job/audit-log-purge/set-job/set-job.component.ts

+71-29
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ import { ExecHistory } from '../../../../../../../ng-swagger-gen/models/exec-his
1010
import {
1111
JOB_STATUS,
1212
REFRESH_STATUS_TIME_DIFFERENCE,
13-
RETENTION_OPERATIONS,
14-
RETENTION_OPERATIONS_I18N_MAP,
13+
RESOURCE_TYPES,
14+
RESOURCE_TYPES_I18N_MAP,
1515
RetentionTimeUnit,
1616
} from '../../clearing-job-interfact';
1717
import { clone } from '../../../../../shared/units/utils';
1818
import { PurgeHistoryComponent } from '../history/purge-history.component';
1919
import { NgForm } from '@angular/forms';
20+
import { AuditlogService } from 'ng-swagger-gen/services';
21+
import { AuditLogEventType } from 'ng-swagger-gen/models';
2022

2123
const ONE_MINUTE: number = 60000;
2224
const ONE_DAY: number = 24;
@@ -30,6 +32,7 @@ const MAX_RETENTION_DAYS: number = 10000;
3032
export class SetJobComponent implements OnInit, OnDestroy {
3133
originCron: OriginCron;
3234
disableGC: boolean = false;
35+
loading: boolean = false;
3336
getLabelCurrent = 'CLEARANCES.SCHEDULE_TO_PURGE';
3437
loadingGcStatus = false;
3538
@ViewChild(CronScheduleComponent)
@@ -43,27 +46,67 @@ export class SetJobComponent implements OnInit, OnDestroy {
4346

4447
retentionTime: number;
4548
retentionUnit: string = RetentionTimeUnit.DAYS;
46-
operations: string[] = clone(RETENTION_OPERATIONS);
47-
selectedOperations: string[] = clone(RETENTION_OPERATIONS);
49+
50+
resourceTypes: Record<string, string>[] = [];
51+
selectedResourceTypes: string[] = clone([]);
4852
@ViewChild(PurgeHistoryComponent)
4953
purgeHistoryComponent: PurgeHistoryComponent;
5054
@ViewChild('purgeForm')
5155
purgeForm: NgForm;
5256
constructor(
5357
private purgeService: PurgeService,
58+
private logService: AuditlogService,
5459
private errorHandler: ErrorHandler
5560
) {}
5661

5762
ngOnInit() {
5863
this.getCurrentSchedule(true);
5964
this.getStatus();
65+
this.initResourceTypes();
6066
}
6167
ngOnDestroy() {
6268
if (this.statusTimeout) {
6369
clearTimeout(this.statusTimeout);
6470
this.statusTimeout = null;
6571
}
6672
}
73+
74+
initResourceTypes() {
75+
this.loading = true;
76+
this.logService
77+
.listAuditLogEventTypesResponse()
78+
.pipe(finalize(() => (this.loading = false)))
79+
.subscribe(
80+
response => {
81+
const auditLogEventTypes =
82+
response.body as AuditLogEventType[];
83+
this.resourceTypes = [
84+
...auditLogEventTypes
85+
.filter(item =>
86+
RESOURCE_TYPES.includes(item.event_type)
87+
)
88+
.map(event => ({
89+
label:
90+
event.event_type.charAt(0).toUpperCase() +
91+
event.event_type
92+
.slice(1)
93+
.replace(/_/g, ' '),
94+
value: event.event_type,
95+
id: event.event_type,
96+
})),
97+
{
98+
label: 'Other events',
99+
value: 'other',
100+
id: 'other_events',
101+
},
102+
];
103+
},
104+
error => {
105+
this.errorHandler.error(error);
106+
}
107+
);
108+
}
109+
67110
// get the latest non-dry-run execution to get the status
68111
getStatus() {
69112
this.loadingLastCompletedTime = true;
@@ -122,11 +165,11 @@ export class SetJobComponent implements OnInit, OnDestroy {
122165
};
123166
if (purgeHistory && purgeHistory.job_parameters) {
124167
const obj = JSON.parse(purgeHistory.job_parameters);
125-
if (obj?.include_operations) {
126-
this.selectedOperations =
127-
obj?.include_operations?.split(',');
168+
if (obj?.include_resource_types) {
169+
this.selectedResourceTypes =
170+
obj?.include_resource_types?.split(',');
128171
} else {
129-
this.selectedOperations = [];
172+
this.selectedResourceTypes = [];
130173
}
131174
if (
132175
obj?.audit_retention_hour > ONE_DAY &&
@@ -140,7 +183,7 @@ export class SetJobComponent implements OnInit, OnDestroy {
140183
}
141184
} else {
142185
this.retentionTime = null;
143-
this.selectedOperations = clone(RETENTION_OPERATIONS);
186+
this.selectedResourceTypes = clone([]);
144187
this.retentionUnit = RetentionTimeUnit.DAYS;
145188
}
146189
} else {
@@ -165,7 +208,8 @@ export class SetJobComponent implements OnInit, OnDestroy {
165208
schedule: {
166209
parameters: {
167210
audit_retention_hour: +retentionTime,
168-
include_operations: this.selectedOperations.join(','),
211+
include_resource_types:
212+
this.selectedResourceTypes.join(','),
169213
dry_run: false,
170214
},
171215
schedule: {
@@ -195,7 +239,8 @@ export class SetJobComponent implements OnInit, OnDestroy {
195239
schedule: {
196240
parameters: {
197241
audit_retention_hour: +retentionTime,
198-
include_operations: this.selectedOperations.join(','),
242+
include_resource_types:
243+
this.selectedResourceTypes.join(','),
199244
dry_run: true,
200245
},
201246
schedule: {
@@ -231,8 +276,8 @@ export class SetJobComponent implements OnInit, OnDestroy {
231276
schedule: {
232277
parameters: {
233278
audit_retention_hour: +retentionTime,
234-
include_operations:
235-
this.selectedOperations.join(','),
279+
include_resource_types:
280+
this.selectedResourceTypes.join(','),
236281
dry_run: false,
237282
},
238283
schedule: {
@@ -259,8 +304,8 @@ export class SetJobComponent implements OnInit, OnDestroy {
259304
schedule: {
260305
parameters: {
261306
audit_retention_hour: +retentionTime,
262-
include_operations:
263-
this.selectedOperations.join(','),
307+
include_resource_types:
308+
this.selectedResourceTypes.join(','),
264309
dry_run: false,
265310
},
266311
schedule: {
@@ -283,21 +328,18 @@ export class SetJobComponent implements OnInit, OnDestroy {
283328
});
284329
}
285330
}
286-
hasOperation(operation: string): boolean {
287-
return this.selectedOperations?.indexOf(operation) !== -1;
331+
hasResourceType(resourceType: string): boolean {
332+
return this.selectedResourceTypes?.indexOf(resourceType) !== -1;
288333
}
289-
operationsToText(operation: string): string {
290-
if (RETENTION_OPERATIONS_I18N_MAP[operation]) {
291-
return RETENTION_OPERATIONS_I18N_MAP[operation];
292-
}
293-
return operation;
294-
}
295-
setOperation(operation: string) {
296-
if (this.selectedOperations.indexOf(operation) === -1) {
297-
this.selectedOperations.push(operation);
334+
335+
setResourceType(resourceType: string) {
336+
if (this.selectedResourceTypes.indexOf(resourceType) === -1) {
337+
this.selectedResourceTypes.push(resourceType);
298338
} else {
299-
this.selectedOperations.splice(
300-
this.selectedOperations.findIndex(item => item === operation),
339+
this.selectedResourceTypes.splice(
340+
this.selectedResourceTypes.findIndex(
341+
item => item === resourceType
342+
),
301343
1
302344
);
303345
}
@@ -311,7 +353,7 @@ export class SetJobComponent implements OnInit, OnDestroy {
311353
return true;
312354
}
313355
return !(
314-
this.purgeForm?.invalid || !(this.selectedOperations?.length > 0)
356+
this.purgeForm?.invalid || !(this.selectedResourceTypes?.length > 0)
315357
);
316358
}
317359
isRetentionTimeValid() {

Diff for: src/portal/src/app/base/left-side-nav/clearing-job/clearing-job-interfact.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,19 @@ export enum RetentionTimeUnit {
33
DAYS = 'days',
44
}
55

6-
export const RETENTION_OPERATIONS = ['create', 'delete', 'pull'];
6+
export const RESOURCE_TYPES = [
7+
'create_artifact',
8+
'delete_artifact',
9+
'pull_artifact',
10+
];
711

8-
export const RETENTION_OPERATIONS_I18N_MAP = {
9-
pull: 'AUDIT_LOG.PULL',
10-
create: 'AUDIT_LOG.CREATE',
11-
delete: 'AUDIT_LOG.DELETE',
12+
export const RESOURCE_TYPES_I18N_MAP = {
13+
artifact: 'AUDIT_LOG.ARTIFACT',
14+
user_login_logout: 'AUDIT_LOG.USER_LOGIN_LOGOUT',
15+
user: 'AUDIT_LOG.USER',
16+
project: 'AUDIT_LOG.PROJECT',
17+
configuration: 'AUDIT_LOG.CONFIGURATION',
18+
project_member: 'AUDIT_LOG.PROJECT_MEMBER',
1219
};
1320

1421
export const JOB_STATUS = {

0 commit comments

Comments
 (0)