mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-27 02:58:05 +01:00
Add ruote resue strategy (#14524)
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
fbf2409c78
commit
acba15210b
@ -16,9 +16,10 @@
|
||||
<global-message [isAppLevel]="true"></global-message>
|
||||
<navigator (showAccountSettingsModal)="openModal($event)" (showDialogModalAction)="openModal($event)"></navigator>
|
||||
<div class="content-container">
|
||||
<div class="content-area" [class.container-override]="showSearch"
|
||||
<div #scrollDiv class="content-area" [class.container-override]="showSearch"
|
||||
[class.content-area-override]="!shouldOverrideContent"
|
||||
[class.start-content-padding]="shouldOverrideContent">
|
||||
[class.start-content-padding]="shouldOverrideContent"
|
||||
(scroll)="publishScrollEvent()">
|
||||
<global-message [isAppLevel]="false"></global-message>
|
||||
<!-- Only appear when searching -->
|
||||
<search-result></search-result>
|
||||
|
@ -11,7 +11,7 @@
|
||||
// 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, OnDestroy } from '@angular/core';
|
||||
import { Component, OnInit, ViewChild, OnDestroy, ElementRef, ChangeDetectorRef } from '@angular/core';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from "rxjs";
|
||||
import { AppConfigService } from '../../services/app-config.service';
|
||||
@ -28,6 +28,7 @@ import { THEME_ARRAY, ThemeInterface } from "../../services/theme";
|
||||
import { clone } from "../../shared/units/utils";
|
||||
import { ThemeService } from "../../services/theme.service";
|
||||
import { AccountSettingsModalComponent } from "../account-settings/account-settings-modal.component";
|
||||
import { EventService, HarborEvent } from "../../services/event-service/event.service";
|
||||
|
||||
const HAS_SHOWED_SCANNER_INFO: string = 'hasShowScannerInfo';
|
||||
const YES: string = 'yes';
|
||||
@ -67,6 +68,8 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
||||
themeArray: ThemeInterface[] = clone(THEME_ARRAY);
|
||||
|
||||
styleMode = this.themeArray[0].showStyle;
|
||||
@ViewChild('scrollDiv') scrollDiv: ElementRef;
|
||||
scrollToPositionSub: Subscription;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
@ -75,9 +78,19 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
||||
private appConfigService: AppConfigService,
|
||||
private scannerService: ConfigScannerService,
|
||||
public theme: ThemeService,
|
||||
private event: EventService,
|
||||
private cd: ChangeDetectorRef
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.scrollToPositionSub) {
|
||||
this.scrollToPositionSub = this.event.subscribe( HarborEvent.SCROLL_TO_POSITION, scrollTop => {
|
||||
if (this.scrollDiv && this.scrollDiv.nativeElement) {
|
||||
this.cd.detectChanges();
|
||||
this.scrollDiv.nativeElement.scrollTop = scrollTop;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.appConfigService.isLdapMode()) {
|
||||
this.isLdapMode = true;
|
||||
} else if (this.appConfigService.isHttpAuthMode()) {
|
||||
@ -102,6 +115,14 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
||||
this.styleMode = localStorage.getItem(HAS_STYLE_MODE);
|
||||
}
|
||||
}
|
||||
publishScrollEvent() {
|
||||
if (this.scrollDiv && this.scrollDiv.nativeElement) {
|
||||
this.event.publish(HarborEvent.SCROLL, {
|
||||
url: this.router.url,
|
||||
scrollTop: this.scrollDiv.nativeElement.scrollTop
|
||||
});
|
||||
}
|
||||
}
|
||||
closeInfo() {
|
||||
if (localStorage) {
|
||||
localStorage.setItem(HAS_SHOWED_SCANNER_INFO, YES);
|
||||
@ -125,6 +146,10 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
||||
if (this.searchCloseSub) {
|
||||
this.searchCloseSub.unsubscribe();
|
||||
}
|
||||
if (this.scrollToPositionSub) {
|
||||
this.scrollToPositionSub.unsubscribe();
|
||||
this.scrollToPositionSub = null;
|
||||
}
|
||||
}
|
||||
|
||||
public get shouldOverrideContent(): boolean {
|
||||
|
@ -22,10 +22,15 @@ import { ReplicationTasksRoutingResolverService } from "../../../services/routin
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
import { ListReplicationRuleComponent } from "./replication/list-replication-rule/list-replication-rule.component";
|
||||
import { CreateEditRuleComponent } from "./replication/create-edit-rule/create-edit-rule.component";
|
||||
import { RouteConfigId } from "../../../route-reuse-strategy/harbor-route-reuse-strategy";
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: TotalReplicationPageComponent
|
||||
component: TotalReplicationPageComponent,
|
||||
data : {
|
||||
reuse: true,
|
||||
routeConfigId: RouteConfigId.REPLICATION_PAGE
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':id/tasks',
|
||||
@ -33,6 +38,9 @@ const routes: Routes = [
|
||||
resolve: {
|
||||
replicationTasksRoutingResolver: ReplicationTasksRoutingResolverService
|
||||
},
|
||||
data : {
|
||||
routeConfigId: RouteConfigId.REPLICATION_TASKS_PAGE
|
||||
}
|
||||
}
|
||||
];
|
||||
@NgModule({
|
||||
|
@ -1,15 +1,11 @@
|
||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { TotalReplicationPageComponent } from './total-replication-page.component';
|
||||
import {Router, ActivatedRoute} from "@angular/router";
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ClarityModule } from '@clr/angular';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import {SessionService} from "../../../shared/services/session.service";
|
||||
import {AppConfigService} from "../../../services/app-config.service";
|
||||
import { SharedTestingModule } from "../../../shared/shared.module";
|
||||
|
||||
describe('TotalReplicationPageComponent', () => {
|
||||
let component: TotalReplicationPageComponent;
|
||||
@ -26,7 +22,10 @@ describe('TotalReplicationPageComponent', () => {
|
||||
}
|
||||
};
|
||||
const mockRouter = {
|
||||
navigate: () => { }
|
||||
navigate: () => {},
|
||||
events: {
|
||||
subscribe: () => {}
|
||||
}
|
||||
};
|
||||
const mockActivatedRoute = null;
|
||||
beforeEach(waitForAsync(() => {
|
||||
@ -35,17 +34,10 @@ describe('TotalReplicationPageComponent', () => {
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
],
|
||||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
ClarityModule,
|
||||
TranslateModule.forRoot(),
|
||||
FormsModule,
|
||||
RouterTestingModule,
|
||||
NoopAnimationsModule,
|
||||
HttpClientTestingModule
|
||||
SharedTestingModule
|
||||
],
|
||||
declarations: [TotalReplicationPageComponent],
|
||||
providers: [
|
||||
TranslateService,
|
||||
{ provide: SessionService, useValue: mockSessionService },
|
||||
{ provide: AppConfigService, useValue: mockAppConfigService },
|
||||
{ provide: Router, useValue: mockRouter },
|
||||
|
@ -11,23 +11,59 @@
|
||||
// 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 } from '@angular/core';
|
||||
import {Router, ActivatedRoute} from "@angular/router";
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Router, ActivatedRoute, NavigationEnd } from "@angular/router";
|
||||
import {SessionService} from "../../../shared/services/session.service";
|
||||
import {AppConfigService} from "../../../services/app-config.service";
|
||||
import { ReplicationRule } from "../../../shared/services";
|
||||
|
||||
import { Subscription } from "rxjs";
|
||||
import { EventService, HarborEvent } from "../../../services/event-service/event.service";
|
||||
// The route path which will display this component
|
||||
const URL_TO_DISPLAY: string = '/harbor/replications';
|
||||
@Component({
|
||||
selector: 'total-replication',
|
||||
templateUrl: 'total-replication-page.component.html',
|
||||
styleUrls: [ './total-replication-page.component.scss' ]
|
||||
})
|
||||
export class TotalReplicationPageComponent {
|
||||
|
||||
export class TotalReplicationPageComponent implements OnInit, OnDestroy {
|
||||
routerSub: Subscription;
|
||||
scrollSub: Subscription;
|
||||
scrollTop: number;
|
||||
constructor(private router: Router,
|
||||
private session: SessionService,
|
||||
private appConfigService: AppConfigService,
|
||||
private activeRoute: ActivatedRoute) {}
|
||||
private activeRoute: ActivatedRoute,
|
||||
private event: EventService) {}
|
||||
ngOnInit(): void {
|
||||
if (!this.scrollSub) {
|
||||
this.scrollSub = this.event.subscribe(HarborEvent.SCROLL, v => {
|
||||
if (v && URL_TO_DISPLAY === v.url) {
|
||||
this.scrollTop = v.scrollTop;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!this.routerSub) {
|
||||
this.routerSub = this.router.events.subscribe(e => {
|
||||
if (e instanceof NavigationEnd) {
|
||||
if (e && URL_TO_DISPLAY === e.url) { // Into view
|
||||
this.event.publish(HarborEvent.SCROLL_TO_POSITION, this.scrollTop);
|
||||
} else {
|
||||
this.event.publish(HarborEvent.SCROLL_TO_POSITION, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
ngOnDestroy(): void {
|
||||
if (this.routerSub) {
|
||||
this.routerSub.unsubscribe();
|
||||
this.routerSub = null;
|
||||
}
|
||||
if (this.scrollSub) {
|
||||
this.scrollSub.unsubscribe();
|
||||
this.scrollSub = null;
|
||||
}
|
||||
}
|
||||
customRedirect(rule: ReplicationRule): void {
|
||||
if (rule) {
|
||||
this.router.navigate(['../projects', rule.projects[0].project_id, 'replications'], { relativeTo: this.activeRoute });
|
||||
|
@ -5,16 +5,24 @@ import { TaskListComponent } from "./task-list/task-list.component";
|
||||
import { PolicyComponent } from "./policy/policy.component";
|
||||
import { AddP2pPolicyComponent } from "./add-p2p-policy/add-p2p-policy.component";
|
||||
import { P2pProviderService } from "./p2p-provider.service";
|
||||
import { RouteConfigId } from "../../../route-reuse-strategy/harbor-route-reuse-strategy";
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'policies',
|
||||
component: PolicyComponent
|
||||
component: PolicyComponent,
|
||||
data : {
|
||||
reuse: true,
|
||||
routeConfigId: RouteConfigId.P2P_POLICIES_PAGE
|
||||
}
|
||||
},
|
||||
{
|
||||
path: ':preheatPolicyName/executions/:executionId/tasks',
|
||||
component: TaskListComponent
|
||||
component: TaskListComponent,
|
||||
data : {
|
||||
routeConfigId: RouteConfigId.P2P_TASKS_PAGE
|
||||
}
|
||||
},
|
||||
{ path: '', redirectTo: 'policies', pathMatch: 'full' }
|
||||
];
|
||||
|
@ -14,7 +14,7 @@
|
||||
import { debounceTime, finalize, switchMap } from 'rxjs/operators';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||
import { Project } from '../../project';
|
||||
import { clone, CustomComparator, DEFAULT_PAGE_SIZE } from '../../../../shared/units/utils';
|
||||
@ -24,7 +24,7 @@ import {
|
||||
UserPermissionService,
|
||||
USERSTATICPERMISSION
|
||||
} from '../../../../shared/services';
|
||||
import {ClrDatagridStateInterface, ClrLoadingState} from '@clr/angular';
|
||||
import { ClrDatagridStateInterface, ClrLoadingState } from '@clr/angular';
|
||||
import {
|
||||
EXECUTION_STATUS,
|
||||
FILTER_TYPE,
|
||||
@ -43,7 +43,9 @@ import { ProviderUnderProject } from '../../../../../../ng-swagger-gen/models/pr
|
||||
import { ConfirmationDialogComponent } from "../../../../shared/components/confirmation-dialog";
|
||||
import { ConfirmationButtons, ConfirmationState, ConfirmationTargets } from "../../../../shared/entities/shared.const";
|
||||
import { ConfirmationMessage } from "../../../global-confirmation-dialog/confirmation-message";
|
||||
|
||||
import { EventService, HarborEvent } from "../../../../services/event-service/event.service";
|
||||
// The route path which will display this component
|
||||
const URL_TO_DISPLAY: RegExp = /\/harbor\/projects\/(\d+)\/p2p-provider\/policies/;
|
||||
@Component({
|
||||
templateUrl: './policy.component.html',
|
||||
styleUrls: ['./policy.component.scss']
|
||||
@ -82,6 +84,9 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
severity_map: any = PROJECT_SEVERITY_LEVEL_TO_TEXT_MAP;
|
||||
timeout: any;
|
||||
hasAddModalInit: boolean = false;
|
||||
routerSub: Subscription;
|
||||
scrollSub: Subscription;
|
||||
scrollTop: number;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
@ -89,9 +94,29 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
private p2pProviderService: P2pProviderService,
|
||||
private messageHandlerService: MessageHandlerService,
|
||||
private userPermissionService: UserPermissionService,
|
||||
private preheatService: PreheatService) { }
|
||||
private preheatService: PreheatService,
|
||||
private event: EventService) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.scrollSub) {
|
||||
this.scrollSub = this.event.subscribe(HarborEvent.SCROLL, v => {
|
||||
if (v && URL_TO_DISPLAY.test(v.url)) {
|
||||
this.scrollTop = v.scrollTop;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!this.routerSub) {
|
||||
this.routerSub = this.router.events.subscribe(e => {
|
||||
if (e instanceof NavigationEnd) {
|
||||
if (e && URL_TO_DISPLAY.test(e.url)) { // Into view
|
||||
this.event.publish(HarborEvent.SCROLL_TO_POSITION, this.scrollTop);
|
||||
} else {
|
||||
this.event.publish(HarborEvent.SCROLL_TO_POSITION, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this.subscribeSearch();
|
||||
this.projectId = +this.route.snapshot.parent.parent.params['id'];
|
||||
const resolverData = this.route.snapshot.parent.parent.data;
|
||||
@ -102,16 +127,26 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
this.getPermissions();
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this._searchSubscription) {
|
||||
this._searchSubscription.unsubscribe();
|
||||
this._searchSubscription = null;
|
||||
}
|
||||
this.clearLoop();
|
||||
if (this.routerSub) {
|
||||
this.routerSub.unsubscribe();
|
||||
this.routerSub = null;
|
||||
}
|
||||
if (this.scrollSub) {
|
||||
this.scrollSub.unsubscribe();
|
||||
this.scrollSub = null;
|
||||
}
|
||||
if (this._searchSubscription) {
|
||||
this._searchSubscription.unsubscribe();
|
||||
this._searchSubscription = null;
|
||||
}
|
||||
this.clearLoop();
|
||||
}
|
||||
addModalInit() {
|
||||
this.hasAddModalInit = true;
|
||||
this.hasAddModalInit = true;
|
||||
}
|
||||
|
||||
getPermissions() {
|
||||
const permissionsList: Observable<boolean>[] = [];
|
||||
permissionsList.push(this.userPermissionService.getPermission(this.projectId,
|
||||
@ -132,6 +167,7 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
this.addBtnState = ClrLoadingState.ERROR;
|
||||
});
|
||||
}
|
||||
|
||||
getProviders() {
|
||||
this.preheatService.ListProvidersUnderProject({projectName: this.projectName})
|
||||
.subscribe(res => {
|
||||
@ -142,10 +178,12 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.selectedRow = null;
|
||||
this.getPolicies();
|
||||
}
|
||||
|
||||
getPolicies() {
|
||||
this.loading = true;
|
||||
this.preheatService.ListPolicies({projectName: this.projectName})
|
||||
@ -159,7 +197,8 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
);
|
||||
}
|
||||
switchStatus() {
|
||||
|
||||
switchStatus() {
|
||||
let content = '';
|
||||
this.translate.get(
|
||||
!this.selectedRow.enabled
|
||||
@ -178,8 +217,9 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
this.confirmationDialogComponent.open(message);
|
||||
});
|
||||
}
|
||||
|
||||
confirmSwitch(message) {
|
||||
if (message && message.source === ConfirmationTargets.P2P_PROVIDER_STOP &&
|
||||
if (message && message.source === ConfirmationTargets.P2P_PROVIDER_STOP &&
|
||||
message.state === ConfirmationState.CONFIRMED) {
|
||||
this.stopLoading = true;
|
||||
const execution: Execution = clone(this.selectedExecutionRow);
|
||||
@ -196,7 +236,7 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
this.messageHandlerService.error(error);
|
||||
});
|
||||
}
|
||||
if (message && message.source === ConfirmationTargets.P2P_PROVIDER_EXECUTE &&
|
||||
if (message && message.source === ConfirmationTargets.P2P_PROVIDER_EXECUTE &&
|
||||
message.state === ConfirmationState.CONFIRMED) {
|
||||
this.executing = true;
|
||||
this.preheatService.ManualPreheat({
|
||||
@ -220,16 +260,16 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
this.preheatService.UpdatePolicy({
|
||||
projectName: this.projectName,
|
||||
preheatPolicyName: this.selectedRow.name,
|
||||
policy: Object.assign({}, this.selectedRow, { enabled: !this.selectedRow.enabled })
|
||||
policy: Object.assign({}, this.selectedRow, {enabled: !this.selectedRow.enabled})
|
||||
}).subscribe(
|
||||
response => {
|
||||
this.messageHandlerService.showSuccess('P2P_PROVIDER.UPDATED_SUCCESSFULLY');
|
||||
this.refresh();
|
||||
},
|
||||
error => {
|
||||
this.messageHandlerService.handleError(error);
|
||||
}
|
||||
);
|
||||
response => {
|
||||
this.messageHandlerService.showSuccess('P2P_PROVIDER.UPDATED_SUCCESSFULLY');
|
||||
this.refresh();
|
||||
},
|
||||
error => {
|
||||
this.messageHandlerService.handleError(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
if (message &&
|
||||
@ -251,11 +291,13 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
newPolicy() {
|
||||
this.addP2pPolicyComponent.isOpen = true;
|
||||
this.addP2pPolicyComponent.isEdit = false;
|
||||
this.addP2pPolicyComponent.resetForAdd();
|
||||
}
|
||||
|
||||
editPolicy() {
|
||||
if (this.selectedRow) {
|
||||
this.addP2pPolicyComponent.repos = null;
|
||||
@ -269,25 +311,25 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
if (filter && filter.length) {
|
||||
filter.forEach(item => {
|
||||
if (item.type === FILTER_TYPE.REPOS && item.value) {
|
||||
let str: string = item.value;
|
||||
if (/^{\S+}$/.test(str)) {
|
||||
return str.slice(1, str.length - 1);
|
||||
}
|
||||
this.addP2pPolicyComponent.repos = str;
|
||||
let str: string = item.value;
|
||||
if (/^{\S+}$/.test(str)) {
|
||||
return str.slice(1, str.length - 1);
|
||||
}
|
||||
this.addP2pPolicyComponent.repos = str;
|
||||
}
|
||||
if (item.type === FILTER_TYPE.TAG && item.value) {
|
||||
let str: string = item.value;
|
||||
if (/^{\S+}$/.test(str)) {
|
||||
return str.slice(1, str.length - 1);
|
||||
}
|
||||
this.addP2pPolicyComponent.tags = str;
|
||||
let str: string = item.value;
|
||||
if (/^{\S+}$/.test(str)) {
|
||||
return str.slice(1, str.length - 1);
|
||||
}
|
||||
this.addP2pPolicyComponent.tags = str;
|
||||
}
|
||||
if (item.type === FILTER_TYPE.LABEL && item.value) {
|
||||
let str: string = item.value;
|
||||
if (/^{\S+}$/.test(str)) {
|
||||
return str.slice(1, str.length - 1);
|
||||
}
|
||||
this.addP2pPolicyComponent.labels = str;
|
||||
let str: string = item.value;
|
||||
if (/^{\S+}$/.test(str)) {
|
||||
return str.slice(1, str.length - 1);
|
||||
}
|
||||
this.addP2pPolicyComponent.labels = str;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -317,13 +359,14 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
this.addP2pPolicyComponent.originCronForEdit = this.addP2pPolicyComponent.cron;
|
||||
}
|
||||
}
|
||||
|
||||
deletePolicy() {
|
||||
const names: string[] = [];
|
||||
names.push(this.selectedRow.name);
|
||||
let content = '';
|
||||
this.translate.get(
|
||||
'P2P_PROVIDER.DELETE_POLICY_SUMMARY'
|
||||
, {names: names.join(',')}).subscribe((res) => content = res);
|
||||
'P2P_PROVIDER.DELETE_POLICY_SUMMARY'
|
||||
, {names: names.join(',')}).subscribe((res) => content = res);
|
||||
const msg: ConfirmationMessage = new ConfirmationMessage(
|
||||
"SCANNER.CONFIRM_DELETION",
|
||||
content,
|
||||
@ -334,8 +377,9 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
this.confirmationDialogComponent.open(msg);
|
||||
}
|
||||
|
||||
executePolicy() {
|
||||
if (this.selectedRow && this.selectedRow.enabled) {
|
||||
if (this.selectedRow && this.selectedRow.enabled) {
|
||||
const message = new ConfirmationMessage(
|
||||
"P2P_PROVIDER.EXECUTE_TITLE",
|
||||
"P2P_PROVIDER.EXECUTE_SUMMARY",
|
||||
@ -347,6 +391,7 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
this.confirmationDialogComponent.open(message);
|
||||
}
|
||||
}
|
||||
|
||||
success(isAdd: boolean) {
|
||||
let message: string;
|
||||
if (isAdd) {
|
||||
@ -357,10 +402,11 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
this.messageHandlerService.showSuccess(message);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
clrLoadJobs(chosenPolicy: PreheatPolicy, withLoading: boolean, state?: ClrDatagridStateInterface) {
|
||||
if (this.selectedRow) {
|
||||
if (state && state.page) {
|
||||
this.pageSize = state.page.size;
|
||||
this.pageSize = state.page.size;
|
||||
}
|
||||
if (withLoading) {
|
||||
// if datagrid is under control of *ngIf, should add timeout in case of ng changes checking error
|
||||
@ -370,7 +416,7 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
let params: string;
|
||||
if (this.searchString) {
|
||||
params = encodeURIComponent(`${this.filterKey}=~${this.searchString}`);
|
||||
params = encodeURIComponent(`${this.filterKey}=~${this.searchString}`);
|
||||
}
|
||||
this.preheatService.ListExecutionsResponse({
|
||||
projectName: this.projectName,
|
||||
@ -393,7 +439,8 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
}
|
||||
refreshJobs (chosenPolicy?: PreheatPolicy) {
|
||||
|
||||
refreshJobs(chosenPolicy?: PreheatPolicy) {
|
||||
this.executionList = [];
|
||||
this.currentExecutionPage = 1;
|
||||
this.totalExecutionCount = 0;
|
||||
@ -401,6 +448,7 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
this.searchString = null;
|
||||
this.clrLoadJobs(chosenPolicy, true);
|
||||
}
|
||||
|
||||
openStopExecutionsDialog() {
|
||||
if (this.selectedExecutionRow) {
|
||||
const stopExecutionsMessage = new ConfirmationMessage(
|
||||
@ -414,59 +462,70 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
this.confirmationDialogComponent.open(stopExecutionsMessage);
|
||||
}
|
||||
}
|
||||
|
||||
goToLink(executionId: number) {
|
||||
const linkUrl = ["harbor",
|
||||
"projects", `${this.projectId}`, "p2p-provider", `${this.selectedRow.name}`, "executions", `${executionId}`, "tasks"];
|
||||
this.router.navigate(linkUrl);
|
||||
}
|
||||
|
||||
getTriggerTypeI18n(trigger: string): string {
|
||||
if (JSON.parse(trigger).type) {
|
||||
return TRIGGER_I18N_MAP[JSON.parse(trigger).type];
|
||||
}
|
||||
return TRIGGER_I18N_MAP[TRIGGER.MANUAL];
|
||||
}
|
||||
|
||||
getTriggerTypeI18nForExecution(trigger: string) {
|
||||
if (trigger && TRIGGER_I18N_MAP[trigger]) {
|
||||
return TRIGGER_I18N_MAP[trigger];
|
||||
}
|
||||
return trigger;
|
||||
}
|
||||
|
||||
isScheduled(trigger: string): boolean {
|
||||
return JSON.parse(trigger).type === TRIGGER.SCHEDULED;
|
||||
}
|
||||
|
||||
isEventBased(trigger: string): boolean {
|
||||
return JSON.parse(trigger).type === TRIGGER.EVENT_BASED;
|
||||
}
|
||||
|
||||
getScheduledCron(trigger: string): string {
|
||||
return JSON.parse(trigger).trigger_setting.cron;
|
||||
}
|
||||
|
||||
getDuration(e: Execution): string {
|
||||
return this.p2pProviderService.getDuration(e.start_time, e.end_time);
|
||||
}
|
||||
|
||||
getValue(filter: string, type: string): string {
|
||||
const arr: any[] = JSON.parse(filter);
|
||||
if (arr && arr.length) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].type === type && arr[i].value) {
|
||||
let str: string = arr[i].value;
|
||||
if (/^{\S+}$/.test(str)) {
|
||||
return str.slice(1, str.length - 1);
|
||||
}
|
||||
return str;
|
||||
let str: string = arr[i].value;
|
||||
if (/^{\S+}$/.test(str)) {
|
||||
return str.slice(1, str.length - 1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
getSuccessRate(m: Metrics): number {
|
||||
if (m && m.task_count && m.success_task_count) {
|
||||
return m.success_task_count / m.task_count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
selectFilterKey($event: any): void {
|
||||
this.filterKey = $event['target'].value;
|
||||
}
|
||||
|
||||
openFilter(isOpen: boolean): void {
|
||||
this.isOpenFilterTag = isOpen;
|
||||
}
|
||||
@ -479,6 +538,7 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
this.clrLoadJobs(null, true);
|
||||
}
|
||||
}
|
||||
|
||||
subscribeSearch() {
|
||||
if (!this._searchSubscription) {
|
||||
this._searchSubscription = this._searchSubject.pipe(
|
||||
@ -497,26 +557,29 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
q: params
|
||||
}).pipe(finalize(() => this.jobsLoading = false));
|
||||
})).subscribe(response => {
|
||||
if (response.headers) {
|
||||
let xHeader: string = response.headers.get('x-total-count');
|
||||
if (xHeader) {
|
||||
this.totalExecutionCount = parseInt(xHeader, 0);
|
||||
}
|
||||
}
|
||||
this.executionList = response.body;
|
||||
this.setLoop();
|
||||
if (response.headers) {
|
||||
let xHeader: string = response.headers.get('x-total-count');
|
||||
if (xHeader) {
|
||||
this.totalExecutionCount = parseInt(xHeader, 0);
|
||||
}
|
||||
}
|
||||
this.executionList = response.body;
|
||||
this.setLoop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
canStop(): boolean {
|
||||
return this.selectedExecutionRow && this.p2pProviderService.willChangStatus(this.selectedExecutionRow.status);
|
||||
}
|
||||
|
||||
clearLoop() {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
setLoop() {
|
||||
this.clearLoop();
|
||||
if (this.executionList && this.executionList.length) {
|
||||
|
@ -12,10 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { NgModule } from '@angular/core';
|
||||
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
|
||||
import {PreloadAllModules, RouteReuseStrategy, RouterModule, Routes} from '@angular/router';
|
||||
import { AuthCheckGuard } from './shared/router-guard/auth-user-activate.service';
|
||||
import { SignInGuard } from './shared/router-guard/sign-in-guard-activate.service';
|
||||
import { OidcGuard } from './shared/router-guard/oidc-guard-active.service';
|
||||
import {HarborRouteReuseStrategy} from "./route-reuse-strategy/harbor-route-reuse-strategy";
|
||||
|
||||
const harborRoutes: Routes = [
|
||||
{ path: '', redirectTo: 'harbor', pathMatch: 'full' },
|
||||
@ -48,6 +49,9 @@ const harborRoutes: Routes = [
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
{ provide: RouteReuseStrategy, useClass: HarborRouteReuseStrategy }
|
||||
],
|
||||
imports: [
|
||||
RouterModule.forRoot(harborRoutes, {
|
||||
onSameUrlNavigation: 'reload',
|
||||
|
@ -0,0 +1,17 @@
|
||||
import { HarborRouteReuseStrategy } from "./harbor-route-reuse-strategy";
|
||||
import { ActivatedRouteSnapshot } from "@angular/router";
|
||||
|
||||
describe('HarborRouteReuseStrategy', () => {
|
||||
let harborRouteReuseStrategy: HarborRouteReuseStrategy;
|
||||
beforeEach(() => {
|
||||
harborRouteReuseStrategy = new HarborRouteReuseStrategy();
|
||||
});
|
||||
it('should be created', () => {
|
||||
expect(harborRouteReuseStrategy).toBeTruthy();
|
||||
});
|
||||
it('shouldReuseRoute', () => {
|
||||
const future: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
const curr: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
|
||||
expect(harborRouteReuseStrategy.shouldReuseRoute(future, curr)).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,110 @@
|
||||
import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
|
||||
|
||||
/**
|
||||
* if want to reuse a route, add reuse: true to its routeConfig data as below:
|
||||
* data : {
|
||||
* reuse: true,
|
||||
* routeConfigId: 'one unique id'
|
||||
* }
|
||||
*/
|
||||
|
||||
export enum RouteConfigId {
|
||||
REPLICATION_PAGE = 'TotalReplicationPageComponent',
|
||||
REPLICATION_TASKS_PAGE = 'ReplicationTasksComponent',
|
||||
P2P_POLICIES_PAGE = 'PolicyComponent',
|
||||
P2P_TASKS_PAGE = 'P2pTaskListComponent'
|
||||
}
|
||||
|
||||
export class HarborRouteReuseStrategy implements RouteReuseStrategy {
|
||||
|
||||
/**
|
||||
* 1.for each routing action, cache will be removed by default
|
||||
* 2.add the routing actions here that should keep cache
|
||||
* 3.you need to add routeConfigId: 'one unique id' to the related router configs like below:
|
||||
* data : {
|
||||
* reuse: true,
|
||||
* routeConfigId: 'one unique id'
|
||||
* }
|
||||
* @param future
|
||||
* @param curr
|
||||
* @private
|
||||
*/
|
||||
private shouldKeepCache(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) {
|
||||
if (future.routeConfig && curr.routeConfig && future.routeConfig.data && curr.routeConfig.data) {
|
||||
// action 1: from replication tasks list page to TotalReplicationPageComponent page
|
||||
if (future.routeConfig.data.routeConfigId === RouteConfigId.REPLICATION_TASKS_PAGE
|
||||
&& curr.routeConfig.data.routeConfigId === RouteConfigId.REPLICATION_PAGE) {
|
||||
this.shouldDeleteCache = false;
|
||||
}
|
||||
// action 2: from preheat tasks list page to PolicyComponent page
|
||||
if (future.routeConfig.data.routeConfigId === RouteConfigId.P2P_TASKS_PAGE
|
||||
&& curr.routeConfig.data.routeConfigId === RouteConfigId.P2P_POLICIES_PAGE) {
|
||||
this.shouldDeleteCache = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private _cache: { [key: string]: DetachedRouteHandle } = {};
|
||||
|
||||
// cache will be removed by default
|
||||
private shouldDeleteCache: boolean = true;
|
||||
|
||||
|
||||
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
|
||||
this.shouldKeepCache(future, curr);
|
||||
return future.routeConfig === curr.routeConfig;
|
||||
}
|
||||
|
||||
shouldAttach(route: ActivatedRouteSnapshot): boolean {
|
||||
if (this.isReuseRoute(route)) {
|
||||
if (this.shouldDeleteCache) {
|
||||
this.clearAllCache();
|
||||
}
|
||||
}
|
||||
this.shouldDeleteCache = true;
|
||||
return this._cache[this.getFullUrl(route)] && this.isReuseRoute(route);
|
||||
}
|
||||
|
||||
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
|
||||
if (this._cache[this.getFullUrl(route)] && this.isReuseRoute(route)) {
|
||||
return this._cache[this.getFullUrl(route)];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
shouldDetach(route: ActivatedRouteSnapshot): boolean {
|
||||
return this.isReuseRoute(route);
|
||||
}
|
||||
|
||||
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
|
||||
// use the full urls as cache keys
|
||||
this._cache[this.getFullUrl(route)] = handle;
|
||||
}
|
||||
|
||||
|
||||
// full url, equals to window.location.pathName
|
||||
private getFullUrl(route: ActivatedRouteSnapshot) {
|
||||
return route['_routerState'].url;
|
||||
}
|
||||
|
||||
// if this route should be reused
|
||||
private isReuseRoute(route: ActivatedRouteSnapshot): boolean {
|
||||
return route && route.routeConfig && route.routeConfig.data && route.routeConfig.data.reuse;
|
||||
}
|
||||
|
||||
|
||||
// clear cache
|
||||
private clearAllCache() {
|
||||
for (let name in this._cache) {
|
||||
if (this._cache.hasOwnProperty(name)) {
|
||||
if (this._cache[name]) {
|
||||
if ((this._cache[name] as any).componentRef) {
|
||||
(this._cache[name] as any).componentRef.destroy(); // manually call destroy(), to destroy component
|
||||
}
|
||||
}
|
||||
delete this._cache[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { EventService } from "./event.service";
|
||||
|
||||
|
||||
describe('EventServiceService', () => {
|
||||
let service: EventService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(EventService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
60
src/portal/src/app/services/event-service/event.service.ts
Normal file
60
src/portal/src/app/services/event-service/event.service.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { from, Subject } from "rxjs";
|
||||
|
||||
@Injectable({
|
||||
providedIn: "root"
|
||||
})
|
||||
export class EventService {
|
||||
|
||||
private listeners = {};
|
||||
private eventsSubject = new Subject();
|
||||
private events = from(this.eventsSubject);
|
||||
|
||||
constructor() {
|
||||
this.events.subscribe(
|
||||
({name, args}) => {
|
||||
if (this.listeners[name]) {
|
||||
for (let listener of this.listeners[name]) {
|
||||
listener(...args);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
subscribe(name: string, listener): any {
|
||||
if (!this.listeners[name]) {
|
||||
this.listeners[name] = [];
|
||||
}
|
||||
this.listeners[name].push(listener);
|
||||
return {
|
||||
unsubscribe: () => {
|
||||
this.doUnsubscribe(name, listener);
|
||||
}
|
||||
};
|
||||
}
|
||||
doUnsubscribe(name, listener) {
|
||||
this.listeners[name] = this.listeners[name].filter((v) => {
|
||||
return v !== listener;
|
||||
});
|
||||
}
|
||||
unsubscribe(name, listener?) {
|
||||
if (this.listeners[name]) {
|
||||
if (!listener) {
|
||||
this.listeners[name] = [];
|
||||
} else {
|
||||
this.doUnsubscribe(name, listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
publish(name, ...args) {
|
||||
this.eventsSubject.next({
|
||||
name,
|
||||
args
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export enum HarborEvent {
|
||||
SCROLL = 'scroll',
|
||||
SCROLL_TO_POSITION = 'scrollToPosition'
|
||||
}
|
Loading…
Reference in New Issue
Block a user