Skip to content

Commit

Permalink
support to audit logs
Browse files Browse the repository at this point in the history
Signed-off-by: xuelichao <[email protected]>
  • Loading branch information
Lichao Xue committed Jan 6, 2025
1 parent b0c74a0 commit 69dd2df
Show file tree
Hide file tree
Showing 31 changed files with 1,501 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@
)
}">
<span class="font-style required flex-200"
>{{ 'CLEARANCES.INCLUDED_OPERATIONS' | translate
>{{ 'CLEARANCES.INCLUDED_RESOURCE_TYPES' | translate
}}<clr-tooltip>
<clr-icon
clrTooltipTrigger
Expand All @@ -152,37 +152,40 @@
clrSize="lg"
*clrIfOpen>
<span>{{
'CLEARANCES.INCLUDED_OPERATION_TOOLTIP' | translate
'CLEARANCES.INCLUDED_RESOURCE_TYPE_TOOLTIP'
| translate
}}</span>
</clr-tooltip-content>
</clr-tooltip></span
>
<div
class="clr-control-container clr-control-inline"
[class.clr-error]="!(selectedOperations?.length > 0)">
class="clr-control-container"
[class.clr-error]="!(selectedResourceTypes?.length > 0)">
<div
class="clr-checkbox-wrapper"
*ngFor="let item of operations">
*ngFor="let item of resourceTypes">
<input
type="checkbox"
id="{{ item }}"
name="operations"
value="{{ item }}"
id="{{ item.event_id }}"
name="resourceTypes"
value="{{ item.name }}"
class="clr-checkbox"
(change)="setOperation(item)"
[checked]="hasOperation(item)" />
<label for="{{ item }}" class="clr-control-label">{{
operationsToText(item) | translate
}}</label>
(change)="setResourceType(item.name)"
[checked]="hasResourceType(item.name)" />
<label
for="{{ item.event_id }}"
class="clr-control-label"
>{{ item.label }}</label
>
</div>
<div
class="clr-subtext-wrapper"
*ngIf="!(selectedOperations?.length > 0)">
*ngIf="!(selectedResourceTypes?.length > 0)">
<clr-icon
class="clr-validate-icon"
shape="exclamation-circle"></clr-icon>
<span class="clr-subtext">{{
'CLEARANCES.INCLUDED_OPERATION_ERROR' | translate
'CLEARANCES.INCLUDED_RESOURCE_TYPE_ERROR' | translate
}}</span>
</div>
</div>
Expand All @@ -197,7 +200,7 @@
[disabled]="
disableGC ||
purgeForm.invalid ||
!(selectedOperations?.length > 0)
!(selectedResourceTypes?.length > 0)
">
{{ 'CLEARANCES.PURGE_NOW' | translate }}
</button>
Expand All @@ -210,7 +213,7 @@
[disabled]="
dryRunOnGoing ||
purgeForm.invalid ||
!(selectedOperations?.length > 0)
!(selectedResourceTypes?.length > 0)
">
{{ 'TAG_RETENTION.WHAT_IF_RUN' | translate }}
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ErrorHandler } from '../../../../../shared/units/error-handler';
import { CronScheduleComponent } from '../../../../../shared/components/cron-schedule';
import { CronTooltipComponent } from '../../../../../shared/components/cron-schedule';
import { of } from 'rxjs';
import { delay, of } from 'rxjs';
import { SharedTestingModule } from '../../../../../shared/shared.module';
import { SetJobComponent } from './set-job.component';
import { PurgeService } from 'ng-swagger-gen/services/purge.service';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { AuditlogService } from 'ng-swagger-gen/services';
import { HttpHeaders, HttpResponse } from '@angular/common/http';

describe('GcComponent', () => {
let component: SetJobComponent;
let fixture: ComponentFixture<SetJobComponent>;
let purgeService: PurgeService;
let auditlogService: AuditlogService;
let mockSchedule = [];
const fakedErrorHandler = {
error(error) {
Expand All @@ -23,6 +26,29 @@ describe('GcComponent', () => {
};
let spySchedule: jasmine.Spy;
let spyGcNow: jasmine.Spy;
const mockedAuditLogs = [
{
event_type: 'create_artifact',
},
{
event_type: 'delete_artifact',
},
{
event_type: 'pull_artifact',
},
];
const fakedAuditlogService = {
listAuditLogEventTypesResponse() {
return of(
new HttpResponse({
body: mockedAuditLogs,
headers: new HttpHeaders({
'x-total-count': '18',
}),
})
).pipe(delay(0));
},
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [SharedTestingModule],
Expand All @@ -31,22 +57,26 @@ describe('GcComponent', () => {
CronScheduleComponent,
CronTooltipComponent,
],
providers: [{ provide: ErrorHandler, useValue: fakedErrorHandler }],
providers: [
{ provide: ErrorHandler, useValue: fakedErrorHandler },
{ provide: AuditlogService, useValue: fakedAuditlogService },
],
schemas: [NO_ERRORS_SCHEMA],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(SetJobComponent);
component = fixture.componentInstance;

auditlogService = fixture.debugElement.injector.get(AuditlogService);
purgeService = fixture.debugElement.injector.get(PurgeService);
spySchedule = spyOn(purgeService, 'getPurgeSchedule').and.returnValues(
of(mockSchedule as any)
);
spyGcNow = spyOn(purgeService, 'createPurgeSchedule').and.returnValues(
of(null)
);
component.selectedResourceTypes = ['create_artifact'];
fixture.detectChanges();
});
it('should create', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import { ExecHistory } from '../../../../../../../ng-swagger-gen/models/exec-his
import {
JOB_STATUS,
REFRESH_STATUS_TIME_DIFFERENCE,
RETENTION_OPERATIONS,
RETENTION_OPERATIONS_I18N_MAP,
RESOURCE_TYPES,
RESOURCE_TYPES_I18N_MAP,
RetentionTimeUnit,
} from '../../clearing-job-interfact';
import { clone } from '../../../../../shared/units/utils';
import { PurgeHistoryComponent } from '../history/purge-history.component';
import { NgForm } from '@angular/forms';
import { AuditlogService } from 'ng-swagger-gen/services';
import { AuditLogEventType } from 'ng-swagger-gen/models';

const ONE_MINUTE: number = 60000;
const ONE_DAY: number = 24;
Expand All @@ -30,6 +32,7 @@ const MAX_RETENTION_DAYS: number = 10000;
export class SetJobComponent implements OnInit, OnDestroy {
originCron: OriginCron;
disableGC: boolean = false;
loading: boolean = false;
getLabelCurrent = 'CLEARANCES.SCHEDULE_TO_PURGE';
loadingGcStatus = false;
@ViewChild(CronScheduleComponent)
Expand All @@ -43,27 +46,67 @@ export class SetJobComponent implements OnInit, OnDestroy {

retentionTime: number;
retentionUnit: string = RetentionTimeUnit.DAYS;
operations: string[] = clone(RETENTION_OPERATIONS);
selectedOperations: string[] = clone(RETENTION_OPERATIONS);

resourceTypes: Record<string, string>[] = [];
selectedResourceTypes: string[] = clone([]);
@ViewChild(PurgeHistoryComponent)
purgeHistoryComponent: PurgeHistoryComponent;
@ViewChild('purgeForm')
purgeForm: NgForm;
constructor(
private purgeService: PurgeService,
private logService: AuditlogService,
private errorHandler: ErrorHandler
) {}

ngOnInit() {
this.getCurrentSchedule(true);
this.getStatus();
this.initResourceTypes();
}
ngOnDestroy() {
if (this.statusTimeout) {
clearTimeout(this.statusTimeout);
this.statusTimeout = null;
}
}

initResourceTypes() {
this.loading = true;
this.logService
.listAuditLogEventTypesResponse()
.pipe(finalize(() => (this.loading = false)))
.subscribe(
response => {
const auditLogEventTypes =
response.body as AuditLogEventType[];
this.resourceTypes = [
...auditLogEventTypes
.filter(item =>
RESOURCE_TYPES.includes(item.event_type)
)
.map(event => ({
label:
event.event_type.charAt(0).toUpperCase() +
event.event_type
.slice(1)
.replace(/_/g, ' '),
value: event.event_type,
id: event.event_type,
})),
{
label: 'Other events',
value: 'other',
id: 'other_events',
},
];
},
error => {
this.errorHandler.error(error);
}
);
}

// get the latest non-dry-run execution to get the status
getStatus() {
this.loadingLastCompletedTime = true;
Expand Down Expand Up @@ -122,11 +165,11 @@ export class SetJobComponent implements OnInit, OnDestroy {
};
if (purgeHistory && purgeHistory.job_parameters) {
const obj = JSON.parse(purgeHistory.job_parameters);
if (obj?.include_operations) {
this.selectedOperations =
obj?.include_operations?.split(',');
if (obj?.include_resource_types) {
this.selectedResourceTypes =
obj?.include_resource_types?.split(',');
} else {
this.selectedOperations = [];
this.selectedResourceTypes = [];
}
if (
obj?.audit_retention_hour > ONE_DAY &&
Expand All @@ -140,7 +183,7 @@ export class SetJobComponent implements OnInit, OnDestroy {
}
} else {
this.retentionTime = null;
this.selectedOperations = clone(RETENTION_OPERATIONS);
this.selectedResourceTypes = clone([]);
this.retentionUnit = RetentionTimeUnit.DAYS;
}
} else {
Expand All @@ -165,7 +208,8 @@ export class SetJobComponent implements OnInit, OnDestroy {
schedule: {
parameters: {
audit_retention_hour: +retentionTime,
include_operations: this.selectedOperations.join(','),
include_resource_types:
this.selectedResourceTypes.join(','),
dry_run: false,
},
schedule: {
Expand Down Expand Up @@ -195,7 +239,8 @@ export class SetJobComponent implements OnInit, OnDestroy {
schedule: {
parameters: {
audit_retention_hour: +retentionTime,
include_operations: this.selectedOperations.join(','),
include_resource_types:
this.selectedResourceTypes.join(','),
dry_run: true,
},
schedule: {
Expand Down Expand Up @@ -231,8 +276,8 @@ export class SetJobComponent implements OnInit, OnDestroy {
schedule: {
parameters: {
audit_retention_hour: +retentionTime,
include_operations:
this.selectedOperations.join(','),
include_resource_types:
this.selectedResourceTypes.join(','),
dry_run: false,
},
schedule: {
Expand All @@ -259,8 +304,8 @@ export class SetJobComponent implements OnInit, OnDestroy {
schedule: {
parameters: {
audit_retention_hour: +retentionTime,
include_operations:
this.selectedOperations.join(','),
include_resource_types:
this.selectedResourceTypes.join(','),
dry_run: false,
},
schedule: {
Expand All @@ -283,21 +328,18 @@ export class SetJobComponent implements OnInit, OnDestroy {
});
}
}
hasOperation(operation: string): boolean {
return this.selectedOperations?.indexOf(operation) !== -1;
hasResourceType(resourceType: string): boolean {
return this.selectedResourceTypes?.indexOf(resourceType) !== -1;
}
operationsToText(operation: string): string {
if (RETENTION_OPERATIONS_I18N_MAP[operation]) {
return RETENTION_OPERATIONS_I18N_MAP[operation];
}
return operation;
}
setOperation(operation: string) {
if (this.selectedOperations.indexOf(operation) === -1) {
this.selectedOperations.push(operation);

setResourceType(resourceType: string) {
if (this.selectedResourceTypes.indexOf(resourceType) === -1) {
this.selectedResourceTypes.push(resourceType);
} else {
this.selectedOperations.splice(
this.selectedOperations.findIndex(item => item === operation),
this.selectedResourceTypes.splice(
this.selectedResourceTypes.findIndex(
item => item === resourceType
),
1
);
}
Expand All @@ -311,7 +353,7 @@ export class SetJobComponent implements OnInit, OnDestroy {
return true;
}
return !(
this.purgeForm?.invalid || !(this.selectedOperations?.length > 0)
this.purgeForm?.invalid || !(this.selectedResourceTypes?.length > 0)
);
}
isRetentionTimeValid() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@ export enum RetentionTimeUnit {
DAYS = 'days',
}

export const RETENTION_OPERATIONS = ['create', 'delete', 'pull'];
export const RESOURCE_TYPES = [
'create_artifact',
'delete_artifact',
'pull_artifact',
];

export const RETENTION_OPERATIONS_I18N_MAP = {
pull: 'AUDIT_LOG.PULL',
create: 'AUDIT_LOG.CREATE',
delete: 'AUDIT_LOG.DELETE',
export const RESOURCE_TYPES_I18N_MAP = {
artifact: 'AUDIT_LOG.ARTIFACT',
user_login_logout: 'AUDIT_LOG.USER_LOGIN_LOGOUT',
user: 'AUDIT_LOG.USER',
project: 'AUDIT_LOG.PROJECT',
configuration: 'AUDIT_LOG.CONFIGURATION',
project_member: 'AUDIT_LOG.PROJECT_MEMBER',
};

export const JOB_STATUS = {
Expand Down
Loading

0 comments on commit 69dd2df

Please sign in to comment.