From af12f9aa016c9f167291711b7bd20ab2ac089417 Mon Sep 17 00:00:00 2001 From: Will Sun <30999793+AllForNothing@users.noreply.github.com> Date: Mon, 26 Apr 2021 14:27:44 +0800 Subject: [PATCH] Refactor tag-retention page (#14749) Signed-off-by: AllForNothing --- .../tag-feature-integration.module.ts | 4 +- .../tag-retention/retention.ts | 3 + .../tag-retention-tasks.component.css | 6 + .../tag-retention-tasks.component.html | 29 +++ .../tag-retention-tasks.component.spec.ts | 89 +++++++++ .../tag-retention-tasks.component.ts | 82 ++++++++ .../tag-retention.component.html | 52 +---- .../tag-retention.component.spec.ts | 181 +++++++++++------- .../tag-retention/tag-retention.component.ts | 106 ++++------ .../Project-Tag-Retention_Elements.robot | 2 +- 10 files changed, 370 insertions(+), 184 deletions(-) create mode 100644 src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.css create mode 100644 src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.html create mode 100644 src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.spec.ts create mode 100644 src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.ts diff --git a/src/portal/src/app/base/project/tag-feature-integration/tag-feature-integration.module.ts b/src/portal/src/app/base/project/tag-feature-integration/tag-feature-integration.module.ts index 82d5a7668..020a4ecd2 100644 --- a/src/portal/src/app/base/project/tag-feature-integration/tag-feature-integration.module.ts +++ b/src/portal/src/app/base/project/tag-feature-integration/tag-feature-integration.module.ts @@ -8,6 +8,7 @@ import { TagRetentionService } from './tag-retention/tag-retention.service'; import { ImmutableTagComponent } from "./immutable-tag/immutable-tag.component"; import { ImmutableTagService } from "./immutable-tag/immutable-tag.service"; 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 = [ @@ -37,7 +38,8 @@ const routes: Routes = [ TagRetentionComponent, AddRuleComponent, ImmutableTagComponent, - AddImmutableRuleComponent + AddImmutableRuleComponent, + TagRetentionTasksComponent ], providers: [ TagRetentionService, diff --git a/src/portal/src/app/base/project/tag-feature-integration/tag-retention/retention.ts b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/retention.ts index 1e37475f7..ecb5235cb 100644 --- a/src/portal/src/app/base/project/tag-feature-integration/tag-retention/retention.ts +++ b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/retention.ts @@ -155,3 +155,6 @@ export class RuleMetadate { } } +export const RUNNING: string = "Running"; +export const PENDING: string = "pending"; +export const TIMEOUT: number = 5000; diff --git a/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.css b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.css new file mode 100644 index 000000000..141b1c955 --- /dev/null +++ b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.css @@ -0,0 +1,6 @@ +.hand { + cursor: pointer; +} +.color-79b { + color: #0079b8; +} diff --git a/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.html b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.html new file mode 100644 index 000000000..0be74eee6 --- /dev/null +++ b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.html @@ -0,0 +1,29 @@ + + {{'TAG_RETENTION.REPOSITORY' | translate}} + {{'TAG_RETENTION.STATUS' | translate}} + {{'TAG_RETENTION.RETAINED' | translate}}/{{'TAG_RETENTION.TOTAL' | translate}} + {{'TAG_RETENTION.START_TIME' | translate}} + {{'TAG_RETENTION.DURATION' | translate}} + {{'TAG_RETENTION.LOG' | translate}} + + {{'TAG_RETENTION.NO_HISTORY' | translate}} + + + {{task.repository}} + {{task.status}} + {{task?.retained?task?.retained:0}}/{{task?.total?task?.total:0}} + {{task.start_time| harborDatetime:'medium'}} + {{task.duration}} + + + + + {{innerPagination.firstItem + 1}} + - + {{innerPagination.lastItem + 1 }} {{'ROBOT_ACCOUNT.OF' | + translate}} + {{ total }} {{'ROBOT_ACCOUNT.ITEMS' | translate}} + + + diff --git a/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.spec.ts b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.spec.ts new file mode 100644 index 000000000..080db9612 --- /dev/null +++ b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.spec.ts @@ -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; + 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> = new HttpResponse>({ + headers: new HttpHeaders({'x-total-count': mockedRunningTasks.length.toString()}), + body: mockedRunningTasks + }); + return of(response).pipe(delay(0)); + } else { + this.count += 1; + const response: HttpResponse> = new HttpResponse>({ + 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'); + }); + })); +}); diff --git a/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.ts b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.ts new file mode 100644 index 000000000..405a0da20 --- /dev/null +++ b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention-tasks/tag-retention-tasks/tag-retention-tasks.component.ts @@ -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; + 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; + TagRetentionComponent.calculateDuration(this.tasks); + this.loopGettingTasks(); + }); + }, TIMEOUT); + } + } +} diff --git a/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention.component.html b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention.component.html index 8524b7053..14178d6e3 100644 --- a/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention.component.html +++ b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention.component.html @@ -110,50 +110,16 @@ {{'TAG_RETENTION.NO_EXECUTION' | translate}} - - - - {{execution.id}} + + + {{execution.id}} - {{execution.status}} - {{(execution.dry_run ? 'TAG_RETENTION.YES' : 'TAG_RETENTION.NO') | translate}} - {{execution.trigger}} - {{execution.start_time| harborDatetime:'medium'}} - {{execution.duration}} - - - {{'TAG_RETENTION.REPOSITORY' | translate}} - {{'TAG_RETENTION.STATUS' | translate}} - {{'TAG_RETENTION.RETAINED' | translate}}/{{'TAG_RETENTION.TOTAL' | translate}} - {{'TAG_RETENTION.START_TIME' | translate}} - {{'TAG_RETENTION.DURATION' | translate}} - {{'TAG_RETENTION.LOG' | translate}} - - {{'TAG_RETENTION.NO_HISTORY' | translate}} - - - {{task.repository}} - {{task.status}} - {{task?.retained?task?.retained:0}}/{{task?.total?task?.total:0}} - {{task.start_time| harborDatetime:'medium'}} - {{task.duration}} - {{'TAG_RETENTION.LOG' | translate}} - - - - {{innerPagination.firstItem + 1}} - - - {{innerPagination.lastItem + 1 }} {{'ROBOT_ACCOUNT.OF' | - translate}} - {{ totalLogCount }} {{'ROBOT_ACCOUNT.ITEMS' | translate}} - - - - + {{execution.status}} + {{(execution.dry_run ? 'TAG_RETENTION.YES' : 'TAG_RETENTION.NO') | translate}} + {{(execution?.trigger ? 'SCHEDULE.' + execution?.trigger.toUpperCase() : '') | translate }} + {{execution.start_time| harborDatetime:'medium'}} + {{execution.duration}} + diff --git a/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention.component.spec.ts b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention.component.spec.ts index 5d8c8e73c..91593acaa 100644 --- a/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention.component.spec.ts +++ b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention.component.spec.ts @@ -1,91 +1,124 @@ -import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; -import { TranslateService } from '@ngx-translate/core'; +import { waitForAsync, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { TagRetentionComponent } from './tag-retention.component'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { of } from 'rxjs'; import { ActivatedRoute } from '@angular/router'; import { AddRuleComponent } from "./add-rule/add-rule.component"; import { TagRetentionService } from "./tag-retention.service"; -import { RuleMetadate, Retention } from './retention'; +import { RuleMetadate, Retention, TIMEOUT } from './retention'; import { delay } from 'rxjs/operators'; -import { ErrorHandler } from "../../../../shared/units/error-handler"; import { SharedTestingModule } from "../../../../shared/shared.module"; +import { HttpHeaders, HttpResponse } from "@angular/common/http"; +import { Registry } from "../../../../../../ng-swagger-gen/models/registry"; describe('TagRetentionComponent', () => { - let component: TagRetentionComponent; - let fixture: ComponentFixture; - const mockTagRetentionService = { - createRetention: () => of(null).pipe(delay(0)), - updateRetention: () => of(null).pipe(delay(0)), - runNowTrigger: () => of(null).pipe(delay(0)), - whatIfRunTrigger: () => of(null).pipe(delay(0)), - AbortRun: () => of(null).pipe(delay(0)), - seeLog: () => of(null).pipe(delay(0)), - getExecutionHistory: () => of({ - body: [] - }).pipe(delay(0)), - getRunNowList: () => of({ - body: [] - }).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 mockedRunningExecutions = [{ + "dry_run": true, + "end_time": "2021-04-26T04:32:21Z", + "id": 57, + "policy_id": 1, + "start_time": "2021-04-26T04:32:18.032419Z", + "status": "Running", + "trigger": "MANUAL" + }]; + const mockedSuccessExecutions = [{ + "dry_run": true, + "end_time": "2021-04-26T04:32:21Z", + "id": 57, + "policy_id": 1, + "start_time": "2021-04-26T04:32:18.032419Z", + "status": "Success", + "trigger": "MANUAL" + }]; + let component: TagRetentionComponent; + let fixture: ComponentFixture; + const mockTagRetentionService = { + createRetention: () => of(null).pipe(delay(0)), + updateRetention: () => of(null).pipe(delay(0)), + runNowTrigger: () => of(null).pipe(delay(0)), + whatIfRunTrigger: () => of(null).pipe(delay(0)), + AbortRun: () => of(null).pipe(delay(0)), + seeLog: () => of(null).pipe(delay(0)), + getExecutionHistory: () => of({ + body: [] + }).pipe(delay(0)), + count: 0, + getRunNowList() { + if (this.count === 0) { + this.count += 1; + const response: HttpResponse> = new HttpResponse>({ + headers: new HttpHeaders({'x-total-count': mockedRunningExecutions.length.toString()}), + body: mockedRunningExecutions + }); + return of(response).pipe(delay(0)); + } else { + this.count += 1; + const response: HttpResponse> = new HttpResponse>({ + 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(() => { - TestBed.configureTestingModule({ - schemas: [ - CUSTOM_ELEMENTS_SCHEMA - ], - imports: [ - SharedTestingModule - ], - declarations: [TagRetentionComponent, AddRuleComponent], - providers: [ - TranslateService, - { provide: TagRetentionService, useValue: mockTagRetentionService }, - { provide: ActivatedRoute, useValue: mockActivatedRoute }, - { provide: ErrorHandler, useValue: mockErrorHandler } + } + } + }; + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ], + imports: [ + SharedTestingModule + ], + declarations: [TagRetentionComponent, AddRuleComponent], + providers: [ + {provide: TagRetentionService, useValue: mockTagRetentionService}, + {provide: ActivatedRoute, useValue: mockActivatedRoute}, + ] + }) + .compileComponents(); + })); - ] - }) - .compileComponents(); - })); + beforeEach(() => { + fixture = TestBed.createComponent(TagRetentionComponent); + component = fixture.componentInstance; + component.loadingRule = false; + fixture.detectChanges(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TagRetentionComponent); - component = fixture.componentInstance; - component.loadingHistories = false; - component.loadingRule = false; - component.loadingHistories = false; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should retry getting executions', fakeAsync(() => { + tick(TIMEOUT); + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(component.executionList[0].status).toEqual('Success'); }); + })); }); diff --git a/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention.component.ts b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention.component.ts index 253322ac6..bbb2a86ac 100644 --- a/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention.component.ts +++ b/src/portal/src/app/base/project/tag-feature-integration/tag-retention/tag-retention.component.ts @@ -11,17 +11,17 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // 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 { 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 { Retention, Rule } from "./retention"; +import { PENDING, Retention, Rule, RUNNING, TIMEOUT } from "./retention"; import { finalize } from "rxjs/operators"; import { CronScheduleComponent } from "../../../../shared/components/cron-schedule"; import { ErrorHandler } from "../../../../shared/units/error-handler"; 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 SEC = 1000; @@ -38,15 +38,12 @@ const DECORATION = { MATCHES: "matches", EXCLUDES: "excludes", }; -const RUNNING: string = "Running"; -const PENDING: string = "pending"; -const TIMEOUT: number = 5000; @Component({ selector: 'tag-retention', templateUrl: './tag-retention.component.html', styleUrls: ['./tag-retention.component.scss'] }) -export class TagRetentionComponent implements OnInit { +export class TagRetentionComponent implements OnInit, OnDestroy { serialFilter: ClrDatagridStringFilterInterface = { accepts(item: any, search: string): boolean { return item.id.toString().indexOf(search) !== -1; @@ -70,27 +67,21 @@ export class TagRetentionComponent implements OnInit { cron: string; selectedItem: any = null; ruleIndex: number = -1; - index: number = -1; retentionId: number; retention: Retention = new Retention(); editIndex: number; executionList = []; executionId: number; - historyList = []; loadingExecutions: boolean = true; - loadingHistories: boolean = true; label: string = 'TAG_RETENTION.TRIGGER'; loadingRule: boolean = false; currentPage: number = 1; pageSize: number = DEFAULT_PAGE_SIZE; totalCount: number = 0; - currentLogPage: number = 1; - totalLogCount: number = 0; - logPageSize: number = 5; - isDetailOpened: boolean = false; @ViewChild('cronScheduleComponent') cronScheduleComponent: CronScheduleComponent; @ViewChild('addRule') addRuleComponent: AddRuleComponent; + executionTimeout; constructor( private route: ActivatedRoute, private tagRetentionService: TagRetentionService, @@ -126,6 +117,13 @@ export class TagRetentionComponent implements OnInit { this.refreshAfterCreatRetention(); this.getMetadata(); } + ngOnDestroy() { + if (this.executionTimeout) { + clearTimeout(this.executionTimeout); + this.executionTimeout = null; + } + } + openConfirm(cron: string) { if (cron) { this.isConfirmOpened = true; @@ -274,15 +272,36 @@ export class TagRetentionComponent implements OnInit { 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; + TagRetentionComponent.calculateDuration(this.executionList); + this.loopGettingExecutions(); + }); + }, TIMEOUT); + } + } refreshList(state?: ClrDatagridStateInterface) { - this.index = -1 ; this.selectedItem = null; - this.loadingExecutions = true; if (this.retentionId) { if (state && state.page) { this.pageSize = state.page.size; } + this.loadingExecutions = true; this.tagRetentionService.getRunNowList(this.retentionId, this.currentPage, this.pageSize) .pipe(finalize(() => this.loadingExecutions = false)) .subscribe( @@ -296,13 +315,14 @@ export class TagRetentionComponent implements OnInit { } this.executionList = response.body as Array; TagRetentionComponent.calculateDuration(this.executionList); + this.loopGettingExecutions(); }, error => { this.errorHandler.error(error); }); } else { - setTimeout(() => { - this.loadingExecutions = false; - }); + setTimeout(() => { + this.loadingExecutions = false; + }, 0); } } @@ -352,50 +372,6 @@ export class TagRetentionComponent implements OnInit { 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; - 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() { this.tagRetentionService.getProjectInfo(this.projectId).subscribe( response => { diff --git a/tests/resources/Harbor-Pages/Project-Tag-Retention_Elements.robot b/tests/resources/Harbor-Pages/Project-Tag-Retention_Elements.robot index ebf0f0f13..bdf8597a1 100644 --- a/tests/resources/Harbor-Pages/Project-Tag-Retention_Elements.robot +++ b/tests/resources/Harbor-Pages/Project-Tag-Retention_Elements.robot @@ -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_refresh_xpath} //clr-dg-action-bar/button[4] ${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_execute_run_xpath} //*[@id='execute-run'] ${project_tag_retention_record_no_xpath} //clr-datagrid[contains(.,'No')]