mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-22 23:01:33 +01:00
Refactor tag-retention page (#14749)
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
c946457eef
commit
af12f9aa01
@ -8,6 +8,7 @@ import { TagRetentionService } from './tag-retention/tag-retention.service';
|
|||||||
import { ImmutableTagComponent } from "./immutable-tag/immutable-tag.component";
|
import { ImmutableTagComponent } from "./immutable-tag/immutable-tag.component";
|
||||||
import { ImmutableTagService } from "./immutable-tag/immutable-tag.service";
|
import { ImmutableTagService } from "./immutable-tag/immutable-tag.service";
|
||||||
import { AddImmutableRuleComponent } from "./immutable-tag/add-rule/add-immutable-rule.component";
|
import { AddImmutableRuleComponent } from "./immutable-tag/add-rule/add-immutable-rule.component";
|
||||||
|
import { TagRetentionTasksComponent } from './tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component';
|
||||||
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
@ -37,7 +38,8 @@ const routes: Routes = [
|
|||||||
TagRetentionComponent,
|
TagRetentionComponent,
|
||||||
AddRuleComponent,
|
AddRuleComponent,
|
||||||
ImmutableTagComponent,
|
ImmutableTagComponent,
|
||||||
AddImmutableRuleComponent
|
AddImmutableRuleComponent,
|
||||||
|
TagRetentionTasksComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
TagRetentionService,
|
TagRetentionService,
|
||||||
|
@ -155,3 +155,6 @@ export class RuleMetadate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const RUNNING: string = "Running";
|
||||||
|
export const PENDING: string = "pending";
|
||||||
|
export const TIMEOUT: number = 5000;
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
.hand {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.color-79b {
|
||||||
|
color: #0079b8;
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
<clr-datagrid (clrDgRefresh)="loadLog()" [clrDgLoading]="loading" class="w-100">
|
||||||
|
<clr-dg-column>{{'TAG_RETENTION.REPOSITORY' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'TAG_RETENTION.STATUS' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'TAG_RETENTION.RETAINED' | translate}}/{{'TAG_RETENTION.TOTAL' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'TAG_RETENTION.START_TIME' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'TAG_RETENTION.DURATION' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'TAG_RETENTION.LOG' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-placeholder>
|
||||||
|
{{'TAG_RETENTION.NO_HISTORY' | translate}}
|
||||||
|
</clr-dg-placeholder>
|
||||||
|
<clr-dg-row *ngFor="let task of tasks" [clrDgItem]="task">
|
||||||
|
<clr-dg-cell>{{task.repository}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{task.status}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{task?.retained?task?.retained:0}}/{{task?.total?task?.total:0}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{task.start_time| harborDatetime:'medium'}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{task.duration}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell><span (click)="seeLog(task.execution_id,task.id)"
|
||||||
|
class="hand color-79b"><clr-icon shape="list"></clr-icon></span>
|
||||||
|
</clr-dg-cell>
|
||||||
|
</clr-dg-row>
|
||||||
|
<clr-dg-footer>
|
||||||
|
<span *ngIf="innerPagination">{{innerPagination.firstItem + 1}}
|
||||||
|
-
|
||||||
|
{{innerPagination.lastItem + 1 }} {{'ROBOT_ACCOUNT.OF' |
|
||||||
|
translate}} </span>
|
||||||
|
{{ total }} {{'ROBOT_ACCOUNT.ITEMS' | translate}}
|
||||||
|
<clr-dg-pagination [clrDgTotalItems]="tasksTimeout" [(clrDgPage)]="page" #innerPagination [clrDgPageSize]="pageSize"></clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
@ -0,0 +1,89 @@
|
|||||||
|
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
|
import { TagRetentionTasksComponent } from './tag-retention-tasks.component';
|
||||||
|
import { SharedTestingModule } from "../../../../../../shared/shared.module";
|
||||||
|
import { TagRetentionService } from "../../tag-retention.service";
|
||||||
|
import { HttpHeaders, HttpResponse } from "@angular/common/http";
|
||||||
|
import { Registry } from "../../../../../../../../ng-swagger-gen/models/registry";
|
||||||
|
import { of } from "rxjs";
|
||||||
|
import { delay } from "rxjs/operators";
|
||||||
|
import { TIMEOUT } from "../../retention";
|
||||||
|
|
||||||
|
describe('TagRetentionTasksComponent', () => {
|
||||||
|
let component: TagRetentionTasksComponent;
|
||||||
|
let fixture: ComponentFixture<TagRetentionTasksComponent>;
|
||||||
|
const mockedRunningTasks = [{
|
||||||
|
"end_time": "2021-04-26T04:32:21Z",
|
||||||
|
"execution_id": 57,
|
||||||
|
"id": 55,
|
||||||
|
"job_id": "85f5d7edab421456aae0159f",
|
||||||
|
"repository": "hello-world",
|
||||||
|
"retained": 1,
|
||||||
|
"start_time": "2021-04-26T04:32:18Z",
|
||||||
|
"status": "Running",
|
||||||
|
"status_code": 3,
|
||||||
|
"total": 1
|
||||||
|
}];
|
||||||
|
const mockedSuccessTasks = [{
|
||||||
|
"end_time": "2021-04-26T04:32:21Z",
|
||||||
|
"execution_id": 57,
|
||||||
|
"id": 55,
|
||||||
|
"job_id": "85f5d7edab421456aae0159f",
|
||||||
|
"repository": "hello-world",
|
||||||
|
"retained": 1,
|
||||||
|
"start_time": "2021-04-26T04:32:18Z",
|
||||||
|
"status": "Success",
|
||||||
|
"status_code": 3,
|
||||||
|
"total": 1
|
||||||
|
}];
|
||||||
|
|
||||||
|
const mockTagRetentionService = {
|
||||||
|
count: 0,
|
||||||
|
getExecutionHistory() {
|
||||||
|
if (this.count === 0) {
|
||||||
|
this.count += 1;
|
||||||
|
const response: HttpResponse<Array<Registry>> = new HttpResponse<Array<Registry>>({
|
||||||
|
headers: new HttpHeaders({'x-total-count': mockedRunningTasks.length.toString()}),
|
||||||
|
body: mockedRunningTasks
|
||||||
|
});
|
||||||
|
return of(response).pipe(delay(0));
|
||||||
|
} else {
|
||||||
|
this.count += 1;
|
||||||
|
const response: HttpResponse<Array<Registry>> = new HttpResponse<Array<Registry>>({
|
||||||
|
headers: new HttpHeaders({'x-total-count': mockedSuccessTasks.length.toString()}),
|
||||||
|
body: mockedSuccessTasks
|
||||||
|
});
|
||||||
|
return of(response).pipe(delay(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
SharedTestingModule
|
||||||
|
],
|
||||||
|
declarations: [TagRetentionTasksComponent],
|
||||||
|
providers: [
|
||||||
|
{provide: TagRetentionService, useValue: mockTagRetentionService},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(TagRetentionTasksComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should retry getting tasks', fakeAsync(() => {
|
||||||
|
tick(TIMEOUT);
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
expect(component.tasks[0].status).toEqual('Success');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
@ -0,0 +1,82 @@
|
|||||||
|
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { finalize } from "rxjs/operators";
|
||||||
|
import { TagRetentionComponent } from "../../tag-retention.component";
|
||||||
|
import { TagRetentionService } from "../../tag-retention.service";
|
||||||
|
import { ErrorHandler } from "../../../../../../shared/units/error-handler";
|
||||||
|
import { PENDING, RUNNING, TIMEOUT } from "../../retention";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-tag-retention-tasks',
|
||||||
|
templateUrl: './tag-retention-tasks.component.html',
|
||||||
|
styleUrls: ['./tag-retention-tasks.component.css']
|
||||||
|
})
|
||||||
|
export class TagRetentionTasksComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
retentionId;
|
||||||
|
@Input()
|
||||||
|
executionId;
|
||||||
|
loading: boolean = true;
|
||||||
|
page: number = 1;
|
||||||
|
pageSize: number = 5;
|
||||||
|
total: number = 0;
|
||||||
|
tasks = [];
|
||||||
|
tasksTimeout;
|
||||||
|
constructor(private tagRetentionService: TagRetentionService,
|
||||||
|
private errorHandler: ErrorHandler) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.tasksTimeout) {
|
||||||
|
clearTimeout(this.tasksTimeout);
|
||||||
|
this.tasksTimeout = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadLog() {
|
||||||
|
this.loading = true;
|
||||||
|
this.tagRetentionService.getExecutionHistory(this.retentionId, this.executionId, this.page, this.pageSize)
|
||||||
|
.pipe(finalize(() => this.loading = false))
|
||||||
|
.subscribe(
|
||||||
|
(response: any) => {
|
||||||
|
// Get total count
|
||||||
|
if (response.headers) {
|
||||||
|
let xHeader: string = response.headers.get("x-total-count");
|
||||||
|
if (xHeader) {
|
||||||
|
this.total = parseInt(xHeader, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tasks = response.body as Array<any>;
|
||||||
|
TagRetentionComponent.calculateDuration(this.tasks);
|
||||||
|
this.loopGettingTasks();
|
||||||
|
}, error => {
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
seeLog(executionId, taskId) {
|
||||||
|
this.tagRetentionService.seeLog(this.retentionId, executionId, taskId);
|
||||||
|
}
|
||||||
|
loopGettingTasks() {
|
||||||
|
if (this.tasks && this.tasks.length
|
||||||
|
&& this.tasks.some(item => {
|
||||||
|
return item.status === RUNNING || item.status === PENDING;
|
||||||
|
})) {
|
||||||
|
this.tasksTimeout = setTimeout(() => {
|
||||||
|
this.loading = true;
|
||||||
|
this.tagRetentionService.getExecutionHistory(this.retentionId, this.executionId, this.page, this.pageSize)
|
||||||
|
.pipe(finalize(() => this.loading = false))
|
||||||
|
.subscribe(res => {
|
||||||
|
// Get total count
|
||||||
|
if (res.headers) {
|
||||||
|
let xHeader: string = res.headers.get("x-total-count");
|
||||||
|
if (xHeader) {
|
||||||
|
this.total = parseInt(xHeader, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tasks = res.body as Array<any>;
|
||||||
|
TagRetentionComponent.calculateDuration(this.tasks);
|
||||||
|
this.loopGettingTasks();
|
||||||
|
});
|
||||||
|
}, TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -110,50 +110,16 @@
|
|||||||
<clr-dg-placeholder>
|
<clr-dg-placeholder>
|
||||||
{{'TAG_RETENTION.NO_EXECUTION' | translate}}
|
{{'TAG_RETENTION.NO_EXECUTION' | translate}}
|
||||||
</clr-dg-placeholder>
|
</clr-dg-placeholder>
|
||||||
<clr-dg-row *ngFor="let execution of executionList;let i = index;" [clrDgItem]="execution">
|
<clr-dg-row *ngFor="let execution of executionList;" [clrDgItem]="execution">
|
||||||
<clr-dg-cell class="hand" (click)="openDetail(i,execution.id)">
|
<clr-dg-cell>
|
||||||
<clr-icon shape="angle" [dir]="index===i?'down':'right'"></clr-icon>
|
<span>{{execution.id}}</span>
|
||||||
<span class="ml-1">{{execution.id}}</span>
|
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell class="hand" (click)="openDetail(i,execution.id)">{{execution.status}}</clr-dg-cell>
|
<clr-dg-cell>{{execution.status}}</clr-dg-cell>
|
||||||
<clr-dg-cell class="hand"
|
<clr-dg-cell>{{(execution.dry_run ? 'TAG_RETENTION.YES' : 'TAG_RETENTION.NO') | translate}}</clr-dg-cell>
|
||||||
(click)="openDetail(i,execution.id)">{{(execution.dry_run ? 'TAG_RETENTION.YES' : 'TAG_RETENTION.NO') | translate}}</clr-dg-cell>
|
<clr-dg-cell>{{(execution?.trigger ? 'SCHEDULE.' + execution?.trigger.toUpperCase() : '') | translate }}</clr-dg-cell>
|
||||||
<clr-dg-cell class="hand"
|
<clr-dg-cell>{{execution.start_time| harborDatetime:'medium'}}</clr-dg-cell>
|
||||||
(click)="openDetail(i,execution.id)">{{execution.trigger}}</clr-dg-cell>
|
<clr-dg-cell>{{execution.duration}}</clr-dg-cell>
|
||||||
<clr-dg-cell class="hand"
|
<app-tag-retention-tasks *clrIfExpanded [executionId]="execution.id" [retentionId]="retentionId" ngProjectAs="clr-dg-row-detail"></app-tag-retention-tasks>
|
||||||
(click)="openDetail(i,execution.id)">{{execution.start_time| harborDatetime:'medium'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell class="hand" (click)="openDetail(i,execution.id)">{{execution.duration}}</clr-dg-cell>
|
|
||||||
<clr-dg-row-detail *ngIf="index===i">
|
|
||||||
<clr-datagrid (clrDgRefresh)="loadLog()" [clrDgLoading]="loadingHistories" class="w-100">
|
|
||||||
<clr-dg-column>{{'TAG_RETENTION.REPOSITORY' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'TAG_RETENTION.STATUS' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'TAG_RETENTION.RETAINED' | translate}}/{{'TAG_RETENTION.TOTAL' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'TAG_RETENTION.START_TIME' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'TAG_RETENTION.DURATION' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-column>{{'TAG_RETENTION.LOG' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-placeholder>
|
|
||||||
{{'TAG_RETENTION.NO_HISTORY' | translate}}
|
|
||||||
</clr-dg-placeholder>
|
|
||||||
<clr-dg-row *ngFor="let task of historyList" [clrDgItem]="task">
|
|
||||||
<clr-dg-cell>{{task.repository}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{task.status}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{task?.retained?task?.retained:0}}/{{task?.total?task?.total:0}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{task.start_time| harborDatetime:'medium'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{task.duration}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell><span (click)="seeLog(task.execution_id,task.id)"
|
|
||||||
class="hand color-79b">{{'TAG_RETENTION.LOG' | translate}}</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
<clr-dg-footer>
|
|
||||||
<span *ngIf="logPageSize">{{innerPagination.firstItem + 1}}
|
|
||||||
-
|
|
||||||
{{innerPagination.lastItem + 1 }} {{'ROBOT_ACCOUNT.OF' |
|
|
||||||
translate}} </span>
|
|
||||||
{{ totalLogCount }} {{'ROBOT_ACCOUNT.ITEMS' | translate}}
|
|
||||||
<clr-dg-pagination [clrDgTotalItems]="totalLogCount" [(clrDgPage)]="currentLogPage" #innerPagination [clrDgPageSize]="logPageSize"></clr-dg-pagination>
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</clr-dg-row-detail>
|
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
<clr-dg-footer>
|
<clr-dg-footer>
|
||||||
<clr-dg-pagination [clrDgTotalItems]="totalCount" #pagination [(clrDgPage)]="currentPage" [clrDgPageSize]="pageSize">
|
<clr-dg-pagination [clrDgTotalItems]="totalCount" #pagination [(clrDgPage)]="currentPage" [clrDgPageSize]="pageSize">
|
||||||
|
@ -1,91 +1,124 @@
|
|||||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { TagRetentionComponent } from './tag-retention.component';
|
import { TagRetentionComponent } from './tag-retention.component';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { AddRuleComponent } from "./add-rule/add-rule.component";
|
import { AddRuleComponent } from "./add-rule/add-rule.component";
|
||||||
import { TagRetentionService } from "./tag-retention.service";
|
import { TagRetentionService } from "./tag-retention.service";
|
||||||
import { RuleMetadate, Retention } from './retention';
|
import { RuleMetadate, Retention, TIMEOUT } from './retention';
|
||||||
import { delay } from 'rxjs/operators';
|
import { delay } from 'rxjs/operators';
|
||||||
import { ErrorHandler } from "../../../../shared/units/error-handler";
|
|
||||||
import { SharedTestingModule } from "../../../../shared/shared.module";
|
import { SharedTestingModule } from "../../../../shared/shared.module";
|
||||||
|
import { HttpHeaders, HttpResponse } from "@angular/common/http";
|
||||||
|
import { Registry } from "../../../../../../ng-swagger-gen/models/registry";
|
||||||
|
|
||||||
describe('TagRetentionComponent', () => {
|
describe('TagRetentionComponent', () => {
|
||||||
let component: TagRetentionComponent;
|
const mockedRunningExecutions = [{
|
||||||
let fixture: ComponentFixture<TagRetentionComponent>;
|
"dry_run": true,
|
||||||
const mockTagRetentionService = {
|
"end_time": "2021-04-26T04:32:21Z",
|
||||||
createRetention: () => of(null).pipe(delay(0)),
|
"id": 57,
|
||||||
updateRetention: () => of(null).pipe(delay(0)),
|
"policy_id": 1,
|
||||||
runNowTrigger: () => of(null).pipe(delay(0)),
|
"start_time": "2021-04-26T04:32:18.032419Z",
|
||||||
whatIfRunTrigger: () => of(null).pipe(delay(0)),
|
"status": "Running",
|
||||||
AbortRun: () => of(null).pipe(delay(0)),
|
"trigger": "MANUAL"
|
||||||
seeLog: () => of(null).pipe(delay(0)),
|
}];
|
||||||
getExecutionHistory: () => of({
|
const mockedSuccessExecutions = [{
|
||||||
body: []
|
"dry_run": true,
|
||||||
}).pipe(delay(0)),
|
"end_time": "2021-04-26T04:32:21Z",
|
||||||
getRunNowList: () => of({
|
"id": 57,
|
||||||
body: []
|
"policy_id": 1,
|
||||||
}).pipe(delay(0)),
|
"start_time": "2021-04-26T04:32:18.032419Z",
|
||||||
getProjectInfo: () => of({
|
"status": "Success",
|
||||||
metadata: {
|
"trigger": "MANUAL"
|
||||||
retention_id: 1
|
}];
|
||||||
}
|
let component: TagRetentionComponent;
|
||||||
}).pipe(delay(0)),
|
let fixture: ComponentFixture<TagRetentionComponent>;
|
||||||
getRetentionMetadata: () => of(new RuleMetadate()).pipe(delay(0)),
|
const mockTagRetentionService = {
|
||||||
getRetention: () => of(new Retention()).pipe(delay(0)),
|
createRetention: () => of(null).pipe(delay(0)),
|
||||||
};
|
updateRetention: () => of(null).pipe(delay(0)),
|
||||||
const mockActivatedRoute = {
|
runNowTrigger: () => of(null).pipe(delay(0)),
|
||||||
snapshot: {
|
whatIfRunTrigger: () => of(null).pipe(delay(0)),
|
||||||
parent: {
|
AbortRun: () => of(null).pipe(delay(0)),
|
||||||
parent: {
|
seeLog: () => of(null).pipe(delay(0)),
|
||||||
parent: {
|
getExecutionHistory: () => of({
|
||||||
params: { id: 1 },
|
body: []
|
||||||
data: {
|
}).pipe(delay(0)),
|
||||||
projectResolver: {
|
count: 0,
|
||||||
metadata: {
|
getRunNowList() {
|
||||||
retention_id: 1
|
if (this.count === 0) {
|
||||||
}
|
this.count += 1;
|
||||||
}
|
const response: HttpResponse<Array<Registry>> = new HttpResponse<Array<Registry>>({
|
||||||
}
|
headers: new HttpHeaders({'x-total-count': mockedRunningExecutions.length.toString()}),
|
||||||
}
|
body: mockedRunningExecutions
|
||||||
|
});
|
||||||
|
return of(response).pipe(delay(0));
|
||||||
|
} else {
|
||||||
|
this.count += 1;
|
||||||
|
const response: HttpResponse<Array<Registry>> = new HttpResponse<Array<Registry>>({
|
||||||
|
headers: new HttpHeaders({'x-total-count': mockedSuccessExecutions.length.toString()}),
|
||||||
|
body: mockedSuccessExecutions
|
||||||
|
});
|
||||||
|
return of(response).pipe(delay(0));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getProjectInfo: () => of({
|
||||||
|
metadata: {
|
||||||
|
retention_id: 1
|
||||||
|
}
|
||||||
|
}).pipe(delay(0)),
|
||||||
|
getRetentionMetadata: () => of(new RuleMetadate()).pipe(delay(0)),
|
||||||
|
getRetention: () => of(new Retention()).pipe(delay(0)),
|
||||||
|
};
|
||||||
|
const mockActivatedRoute = {
|
||||||
|
snapshot: {
|
||||||
|
parent: {
|
||||||
|
parent: {
|
||||||
|
parent: {
|
||||||
|
params: {id: 1},
|
||||||
|
data: {
|
||||||
|
projectResolver: {
|
||||||
|
metadata: {
|
||||||
|
retention_id: 1
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
const mockErrorHandler = {
|
}
|
||||||
error: () => { }
|
};
|
||||||
};
|
beforeEach(waitForAsync(() => {
|
||||||
beforeEach(waitForAsync(() => {
|
TestBed.configureTestingModule({
|
||||||
TestBed.configureTestingModule({
|
schemas: [
|
||||||
schemas: [
|
CUSTOM_ELEMENTS_SCHEMA
|
||||||
CUSTOM_ELEMENTS_SCHEMA
|
],
|
||||||
],
|
imports: [
|
||||||
imports: [
|
SharedTestingModule
|
||||||
SharedTestingModule
|
],
|
||||||
],
|
declarations: [TagRetentionComponent, AddRuleComponent],
|
||||||
declarations: [TagRetentionComponent, AddRuleComponent],
|
providers: [
|
||||||
providers: [
|
{provide: TagRetentionService, useValue: mockTagRetentionService},
|
||||||
TranslateService,
|
{provide: ActivatedRoute, useValue: mockActivatedRoute},
|
||||||
{ provide: TagRetentionService, useValue: mockTagRetentionService },
|
]
|
||||||
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
})
|
||||||
{ provide: ErrorHandler, useValue: mockErrorHandler }
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
]
|
beforeEach(() => {
|
||||||
})
|
fixture = TestBed.createComponent(TagRetentionComponent);
|
||||||
.compileComponents();
|
component = fixture.componentInstance;
|
||||||
}));
|
component.loadingRule = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
it('should create', () => {
|
||||||
fixture = TestBed.createComponent(TagRetentionComponent);
|
expect(component).toBeTruthy();
|
||||||
component = fixture.componentInstance;
|
});
|
||||||
component.loadingHistories = false;
|
|
||||||
component.loadingRule = false;
|
it('should retry getting executions', fakeAsync(() => {
|
||||||
component.loadingHistories = false;
|
tick(TIMEOUT);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
fixture.whenStable().then(() => {
|
||||||
|
expect(component.executionList[0].status).toEqual('Success');
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
@ -11,17 +11,17 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { AddRuleComponent } from "./add-rule/add-rule.component";
|
import { AddRuleComponent } from "./add-rule/add-rule.component";
|
||||||
import {ClrDatagridStateInterface, ClrDatagridStringFilterInterface} from "@clr/angular";
|
import { ClrDatagridStateInterface, ClrDatagridStringFilterInterface } from "@clr/angular";
|
||||||
import { TagRetentionService } from "./tag-retention.service";
|
import { TagRetentionService } from "./tag-retention.service";
|
||||||
import { Retention, Rule } from "./retention";
|
import { PENDING, Retention, Rule, RUNNING, TIMEOUT } from "./retention";
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import { CronScheduleComponent } from "../../../../shared/components/cron-schedule";
|
import { CronScheduleComponent } from "../../../../shared/components/cron-schedule";
|
||||||
import { ErrorHandler } from "../../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../../shared/units/error-handler";
|
||||||
import { OriginCron } from "../../../../shared/services";
|
import { OriginCron } from "../../../../shared/services";
|
||||||
import {clone, DEFAULT_PAGE_SIZE} from "../../../../shared/units/utils";
|
import { clone, DEFAULT_PAGE_SIZE} from "../../../../shared/units/utils";
|
||||||
|
|
||||||
const MIN = 60000;
|
const MIN = 60000;
|
||||||
const SEC = 1000;
|
const SEC = 1000;
|
||||||
@ -38,15 +38,12 @@ const DECORATION = {
|
|||||||
MATCHES: "matches",
|
MATCHES: "matches",
|
||||||
EXCLUDES: "excludes",
|
EXCLUDES: "excludes",
|
||||||
};
|
};
|
||||||
const RUNNING: string = "Running";
|
|
||||||
const PENDING: string = "pending";
|
|
||||||
const TIMEOUT: number = 5000;
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tag-retention',
|
selector: 'tag-retention',
|
||||||
templateUrl: './tag-retention.component.html',
|
templateUrl: './tag-retention.component.html',
|
||||||
styleUrls: ['./tag-retention.component.scss']
|
styleUrls: ['./tag-retention.component.scss']
|
||||||
})
|
})
|
||||||
export class TagRetentionComponent implements OnInit {
|
export class TagRetentionComponent implements OnInit, OnDestroy {
|
||||||
serialFilter: ClrDatagridStringFilterInterface<any> = {
|
serialFilter: ClrDatagridStringFilterInterface<any> = {
|
||||||
accepts(item: any, search: string): boolean {
|
accepts(item: any, search: string): boolean {
|
||||||
return item.id.toString().indexOf(search) !== -1;
|
return item.id.toString().indexOf(search) !== -1;
|
||||||
@ -70,27 +67,21 @@ export class TagRetentionComponent implements OnInit {
|
|||||||
cron: string;
|
cron: string;
|
||||||
selectedItem: any = null;
|
selectedItem: any = null;
|
||||||
ruleIndex: number = -1;
|
ruleIndex: number = -1;
|
||||||
index: number = -1;
|
|
||||||
retentionId: number;
|
retentionId: number;
|
||||||
retention: Retention = new Retention();
|
retention: Retention = new Retention();
|
||||||
editIndex: number;
|
editIndex: number;
|
||||||
executionList = [];
|
executionList = [];
|
||||||
executionId: number;
|
executionId: number;
|
||||||
historyList = [];
|
|
||||||
loadingExecutions: boolean = true;
|
loadingExecutions: boolean = true;
|
||||||
loadingHistories: boolean = true;
|
|
||||||
label: string = 'TAG_RETENTION.TRIGGER';
|
label: string = 'TAG_RETENTION.TRIGGER';
|
||||||
loadingRule: boolean = false;
|
loadingRule: boolean = false;
|
||||||
currentPage: number = 1;
|
currentPage: number = 1;
|
||||||
pageSize: number = DEFAULT_PAGE_SIZE;
|
pageSize: number = DEFAULT_PAGE_SIZE;
|
||||||
totalCount: number = 0;
|
totalCount: number = 0;
|
||||||
currentLogPage: number = 1;
|
|
||||||
totalLogCount: number = 0;
|
|
||||||
logPageSize: number = 5;
|
|
||||||
isDetailOpened: boolean = false;
|
|
||||||
@ViewChild('cronScheduleComponent')
|
@ViewChild('cronScheduleComponent')
|
||||||
cronScheduleComponent: CronScheduleComponent;
|
cronScheduleComponent: CronScheduleComponent;
|
||||||
@ViewChild('addRule') addRuleComponent: AddRuleComponent;
|
@ViewChild('addRule') addRuleComponent: AddRuleComponent;
|
||||||
|
executionTimeout;
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private tagRetentionService: TagRetentionService,
|
private tagRetentionService: TagRetentionService,
|
||||||
@ -126,6 +117,13 @@ export class TagRetentionComponent implements OnInit {
|
|||||||
this.refreshAfterCreatRetention();
|
this.refreshAfterCreatRetention();
|
||||||
this.getMetadata();
|
this.getMetadata();
|
||||||
}
|
}
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.executionTimeout) {
|
||||||
|
clearTimeout(this.executionTimeout);
|
||||||
|
this.executionTimeout = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
openConfirm(cron: string) {
|
openConfirm(cron: string) {
|
||||||
if (cron) {
|
if (cron) {
|
||||||
this.isConfirmOpened = true;
|
this.isConfirmOpened = true;
|
||||||
@ -274,15 +272,36 @@ export class TagRetentionComponent implements OnInit {
|
|||||||
this.errorHandler.error(error);
|
this.errorHandler.error(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
loopGettingExecutions() {
|
||||||
|
if (this.executionList && this.executionList.length && this.executionList.some(item => {
|
||||||
|
return item.status === RUNNING || item.status === PENDING;
|
||||||
|
})) {
|
||||||
|
this.executionTimeout = setTimeout(() => {
|
||||||
|
this.loadingExecutions = true;
|
||||||
|
this.tagRetentionService.getRunNowList(this.retentionId, this.currentPage, this.pageSize)
|
||||||
|
.pipe(finalize(() => this.loadingExecutions = false))
|
||||||
|
.subscribe(res => {
|
||||||
|
// Get total count
|
||||||
|
if (res.headers) {
|
||||||
|
let xHeader: string = res.headers.get("x-total-count");
|
||||||
|
if (xHeader) {
|
||||||
|
this.totalCount = parseInt(xHeader, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.executionList = res.body as Array<any>;
|
||||||
|
TagRetentionComponent.calculateDuration(this.executionList);
|
||||||
|
this.loopGettingExecutions();
|
||||||
|
});
|
||||||
|
}, TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
refreshList(state?: ClrDatagridStateInterface) {
|
refreshList(state?: ClrDatagridStateInterface) {
|
||||||
this.index = -1 ;
|
|
||||||
this.selectedItem = null;
|
this.selectedItem = null;
|
||||||
this.loadingExecutions = true;
|
|
||||||
if (this.retentionId) {
|
if (this.retentionId) {
|
||||||
if (state && state.page) {
|
if (state && state.page) {
|
||||||
this.pageSize = state.page.size;
|
this.pageSize = state.page.size;
|
||||||
}
|
}
|
||||||
|
this.loadingExecutions = true;
|
||||||
this.tagRetentionService.getRunNowList(this.retentionId, this.currentPage, this.pageSize)
|
this.tagRetentionService.getRunNowList(this.retentionId, this.currentPage, this.pageSize)
|
||||||
.pipe(finalize(() => this.loadingExecutions = false))
|
.pipe(finalize(() => this.loadingExecutions = false))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
@ -296,13 +315,14 @@ export class TagRetentionComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
this.executionList = response.body as Array<any>;
|
this.executionList = response.body as Array<any>;
|
||||||
TagRetentionComponent.calculateDuration(this.executionList);
|
TagRetentionComponent.calculateDuration(this.executionList);
|
||||||
|
this.loopGettingExecutions();
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandler.error(error);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.loadingExecutions = false;
|
this.loadingExecutions = false;
|
||||||
});
|
}, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,50 +372,6 @@ export class TagRetentionComponent implements OnInit {
|
|||||||
this.ruleIndex = -1;
|
this.ruleIndex = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadLog() {
|
|
||||||
if (this.isDetailOpened) {
|
|
||||||
setTimeout(() => {// when this.isDetailOpened is true, need to wait ngCheck finished
|
|
||||||
this.loadingHistories = true;
|
|
||||||
this.tagRetentionService.getExecutionHistory(this.retentionId, this.executionId, this.currentLogPage, this.logPageSize)
|
|
||||||
.pipe(finalize(() => this.loadingHistories = false))
|
|
||||||
.subscribe(
|
|
||||||
(response: any) => {
|
|
||||||
// Get total count
|
|
||||||
if (response.headers) {
|
|
||||||
let xHeader: string = response.headers.get("x-total-count");
|
|
||||||
if (xHeader) {
|
|
||||||
this.totalLogCount = parseInt(xHeader, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.historyList = response.body as Array<any>;
|
|
||||||
TagRetentionComponent.calculateDuration(this.historyList);
|
|
||||||
if (this.historyList && this.historyList.length
|
|
||||||
&& this.historyList.some(item => {
|
|
||||||
return item.status === RUNNING || item.status === PENDING;
|
|
||||||
})) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.loadLog();
|
|
||||||
}, TIMEOUT);
|
|
||||||
}
|
|
||||||
}, error => {
|
|
||||||
this.errorHandler.error(error);
|
|
||||||
});
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
openDetail(index, executionId) {
|
|
||||||
if (this.index !== index) {
|
|
||||||
this.index = index;
|
|
||||||
this.historyList = [];
|
|
||||||
this.executionId = executionId;
|
|
||||||
this.isDetailOpened = true;
|
|
||||||
} else {
|
|
||||||
this.index = -1;
|
|
||||||
this.isDetailOpened = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshAfterCreatRetention() {
|
refreshAfterCreatRetention() {
|
||||||
this.tagRetentionService.getProjectInfo(this.projectId).subscribe(
|
this.tagRetentionService.getProjectInfo(this.projectId).subscribe(
|
||||||
response => {
|
response => {
|
||||||
|
@ -33,7 +33,7 @@ ${project_tag_retention_span_daily_xpath} //cron-selection//div//span[contains(
|
|||||||
${project_tag_retention_dry_run_xpath} //*[@id='dry-run']
|
${project_tag_retention_dry_run_xpath} //*[@id='dry-run']
|
||||||
${project_tag_retention_refresh_xpath} //clr-dg-action-bar/button[4]
|
${project_tag_retention_refresh_xpath} //clr-dg-action-bar/button[4]
|
||||||
${project_tag_retention_record_yes_xpath} //clr-datagrid[contains(.,'Yes')]
|
${project_tag_retention_record_yes_xpath} //clr-datagrid[contains(.,'Yes')]
|
||||||
${project_tag_retention_list_expand_icon_xpath} //project-detail/app-tag-feature-integration/tag-retention//clr-datagrid//clr-dg-row//clr-dg-cell[1]/clr-icon[contains(@shape, 'angle')]
|
${project_tag_retention_list_expand_icon_xpath} //project-detail/app-tag-feature-integration/tag-retention//clr-datagrid//clr-dg-row//clr-expandable-animation//clr-icon[@class='datagrid-expandable-caret-icon']
|
||||||
${project_tag_retention_run_now_xpath} //*[@id='run-now']
|
${project_tag_retention_run_now_xpath} //*[@id='run-now']
|
||||||
${project_tag_retention_execute_run_xpath} //*[@id='execute-run']
|
${project_tag_retention_execute_run_xpath} //*[@id='execute-run']
|
||||||
${project_tag_retention_record_no_xpath} //clr-datagrid[contains(.,'No')]
|
${project_tag_retention_record_no_xpath} //clr-datagrid[contains(.,'No')]
|
||||||
|
Loading…
Reference in New Issue
Block a user