mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-24 17:47:46 +01:00
Merge pull request #2347 from wknet123/master-replication
Add shareable replication components
This commit is contained in:
commit
12660e0ea6
@ -62,7 +62,6 @@ export class CreateEditEndpointComponent implements AfterViewChecked {
|
|||||||
currentForm: NgForm;
|
currentForm: NgForm;
|
||||||
|
|
||||||
hasChanged: boolean;
|
hasChanged: boolean;
|
||||||
|
|
||||||
endpointHasChanged: boolean;
|
endpointHasChanged: boolean;
|
||||||
targetNameHasChanged: boolean;
|
targetNameHasChanged: boolean;
|
||||||
|
|
||||||
@ -263,7 +262,7 @@ export class CreateEditEndpointComponent implements AfterViewChecked {
|
|||||||
ngAfterViewChecked(): void {
|
ngAfterViewChecked(): void {
|
||||||
this.targetForm = this.currentForm;
|
this.targetForm = this.currentForm;
|
||||||
if(this.targetForm) {
|
if(this.targetForm) {
|
||||||
let comparison: {[key: string]: string} = {
|
let comparison: {[key: string]: any} = {
|
||||||
targetName: this.initVal.name,
|
targetName: this.initVal.name,
|
||||||
endpointUrl: this.initVal.endpoint,
|
endpointUrl: this.initVal.endpoint,
|
||||||
username: this.initVal.username,
|
username: this.initVal.username,
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
export const CREATE_EDIT_RULE_STYLE: string = `
|
||||||
|
.form-group-label-override {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
}`;
|
@ -0,0 +1,88 @@
|
|||||||
|
export const CREATE_EDIT_RULE_TEMPLATE: string = `
|
||||||
|
<clr-modal [(clrModalOpen)]="createEditRuleOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
||||||
|
<h3 class="modal-title">{{modalTitle}}</h3>
|
||||||
|
<inline-alert class="modal-title" (confirmEvt)="confirmCancel($event)"></inline-alert>
|
||||||
|
<div class="modal-body" style="max-height: 85vh;">
|
||||||
|
<form #ruleForm="ngForm">
|
||||||
|
<section class="form-block">
|
||||||
|
<div class="alert alert-warning" *ngIf="!editable">
|
||||||
|
<div class="alert-item">
|
||||||
|
<span class="alert-text">
|
||||||
|
{{'REPLICATION.CANNOT_EDIT' | translate}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="policy_name" class="col-md-4 form-group-label-override">{{'REPLICATION.NAME' | translate}}<span style="color: red">*</span></label>
|
||||||
|
<label for="policy_name" class="col-md-8" aria-haspopup="true" role="tooltip" [class.invalid]="name.errors && (name.dirty || name.touched)" class="tooltip tooltip-validation tooltip-sm tooltip-bottom-left">
|
||||||
|
<input type="text" id="policy_name" [(ngModel)]="createEditRule.name" name="name" size="20" #name="ngModel" required [readonly]="readonly">
|
||||||
|
<span class="tooltip-content" *ngIf="name.errors && name.errors.required && (name.dirty || name.touched)">
|
||||||
|
{{'REPLICATION.NAME_IS_REQUIRED' | translate}}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="policy_description" class="col-md-4 form-group-label-override">{{'REPLICATION.DESCRIPTION' | translate}}</label>
|
||||||
|
<textarea class="col-md-8" id="policy_description" row="3" [(ngModel)]="createEditRule.description" name="description" size="20" #description="ngModel" [readonly]="readonly"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-4 form-group-label-override">{{'REPLICATION.ENABLE' | translate}}</label>
|
||||||
|
<div class="checkbox-inline">
|
||||||
|
<input type="checkbox" id="policy_enable" [(ngModel)]="createEditRule.enable" name="enable" #enable="ngModel" [disabled]="untoggleable">
|
||||||
|
<label for="policy_enable"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="destination_name" class="col-md-4 form-group-label-override">{{'REPLICATION.DESTINATION_NAME' | translate}}<span style="color: red">*</span></label>
|
||||||
|
<div class="select" *ngIf="!isCreateEndpoint">
|
||||||
|
<select id="destination_name" [(ngModel)]="createEditRule.endpointId" name="endpointId" (change)="selectEndpoint()" [disabled]="testOngoing || readonly">
|
||||||
|
<option *ngFor="let t of endpoints" [value]="t.id" [selected]="t.id == createEditRule.endpointId">{{t.name}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label class="col-md-8" *ngIf="isCreateEndpoint" for="destination_name" aria-haspopup="true" role="tooltip" [class.invalid]="endpointName.errors && (endpointName.dirty || endpointName.touched)"
|
||||||
|
class="tooltip tooltip-validation tooltip-sm tooltip-bottom-left">
|
||||||
|
<input type="text" id="destination_name" [(ngModel)]="createEditRule.endpointName" name="endpointName" size="8" #endpointName="ngModel" value="" required>
|
||||||
|
<span class="tooltip-content" *ngIf="endpointName.errors && endpointName.errors.required && (endpointName.dirty || endpointName.touched)">
|
||||||
|
{{'REPLICATION.DESTINATION_NAME_IS_REQUIRED' | translate}}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div class="checkbox-inline" *ngIf="showNewDestination">
|
||||||
|
<input type="checkbox" id="check_new" (click)="newEndpoint(checkedAddNew.checked)" #checkedAddNew [checked]="isCreateEndpoint" [disabled]="testOngoing || readonly">
|
||||||
|
<label for="check_new">{{'REPLICATION.NEW_DESTINATION' | translate}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="destination_url" class="col-md-4 form-group-label-override">{{'REPLICATION.DESTINATION_URL' | translate}}<span style="color: red">*</span></label>
|
||||||
|
<label for="destination_url" class="col-md-8" aria-haspopup="true" role="tooltip" [class.invalid]="endpointUrl.errors && (endpointUrl.dirty || endpointUrl.touched)"
|
||||||
|
class="tooltip tooltip-validation tooltip-sm tooltip-bottom-left">
|
||||||
|
<input type="text" id="destination_url" [disabled]="testOngoing" [readonly]="readonly || !isCreateEndpoint"
|
||||||
|
[(ngModel)]="createEditRule.endpointUrl" size="20" name="endpointUrl" required #endpointUrl="ngModel">
|
||||||
|
<span class="tooltip-content" *ngIf="endpointUrl.errors && endpointUrl.errors.required && (endpointUrl.dirty || endpointUrl.touched)">
|
||||||
|
{{'REPLICATION.DESTINATION_URL_IS_REQUIRED' | translate}}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="destination_username" class="col-md-4 form-group-label-override">{{'REPLICATION.DESTINATION_USERNAME' | translate}}</label>
|
||||||
|
<input type="text" class="col-md-8" id="destination_username" [disabled]="testOngoing" [readonly]="readonly || !isCreateEndpoint"
|
||||||
|
[(ngModel)]="createEditRule.username" size="20" name="username" #username="ngModel">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="destination_password" class="col-md-4 form-group-label-override">{{'REPLICATION.DESTINATION_PASSWORD' | translate}}</label>
|
||||||
|
<input type="password" class="col-md-8" id="destination_password" [disabled]="testOngoing" [readonly]="readonly || !isCreateEndpoint"
|
||||||
|
[(ngModel)]="createEditRule.password" size="20" name="password" #password="ngModel">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="spin" class="col-md-4"></label>
|
||||||
|
<span class="col-md-8 spinner spinner-inline" [hidden]="!testOngoing"></span>
|
||||||
|
<span [style.color]="!pingStatus ? 'red': ''" class="form-group-label-override">{{ pingTestMessage }}</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline" (click)="testConnection()" [disabled]="testOngoing">{{'REPLICATION.TEST_CONNECTION' | translate}}</button>
|
||||||
|
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate }}</button>
|
||||||
|
<button type="submit" class="btn btn-primary" [disabled]="!ruleForm.form.valid || testOngoing || !editable" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</clr-modal>`;
|
@ -0,0 +1,243 @@
|
|||||||
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
|
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||||
|
import { ReplicationComponent } from '../replication/replication.component';
|
||||||
|
|
||||||
|
import { ListReplicationRuleComponent } from '../list-replication-rule/list-replication-rule.component';
|
||||||
|
|
||||||
|
import { CreateEditRuleComponent } from './create-edit-rule.component';
|
||||||
|
import { DatePickerComponent } from '../datetime-picker/datetime-picker.component';
|
||||||
|
import { DateValidatorDirective } from '../datetime-picker/date-validator.directive';
|
||||||
|
import { FilterComponent } from '../filter/filter.component';
|
||||||
|
import { InlineAlertComponent } from '../inline-alert/inline-alert.component';
|
||||||
|
import { ReplicationRule, ReplicationJob, Endpoint } from '../service/interface';
|
||||||
|
|
||||||
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
|
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||||
|
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
|
||||||
|
import { EndpointService, EndpointDefaultService } from '../service/endpoint.service';
|
||||||
|
|
||||||
|
describe('CreateEditRuleComponent (inline template)', ()=>{
|
||||||
|
|
||||||
|
let mockRules: ReplicationRule[] = [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"project_id": 1,
|
||||||
|
"project_name": "library",
|
||||||
|
"target_id": 1,
|
||||||
|
"target_name": "target_01",
|
||||||
|
"name": "sync_01",
|
||||||
|
"enabled": 0,
|
||||||
|
"description": "",
|
||||||
|
"cron_str": "",
|
||||||
|
"error_job_count": 2,
|
||||||
|
"deleted": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"project_id": 1,
|
||||||
|
"project_name": "library",
|
||||||
|
"target_id": 3,
|
||||||
|
"target_name": "target_02",
|
||||||
|
"name": "sync_02",
|
||||||
|
"enabled": 1,
|
||||||
|
"description": "",
|
||||||
|
"cron_str": "",
|
||||||
|
"error_job_count": 1,
|
||||||
|
"deleted": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"project_id": 1,
|
||||||
|
"project_name": "library",
|
||||||
|
"target_id": 2,
|
||||||
|
"target_name": "target_03",
|
||||||
|
"name": "sync_03",
|
||||||
|
"enabled": 0,
|
||||||
|
"description": "",
|
||||||
|
"cron_str": "",
|
||||||
|
"error_job_count": 0,
|
||||||
|
"deleted": 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let mockJobs: ReplicationJob[] = [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"status": "stopped",
|
||||||
|
"repository": "library/busybox",
|
||||||
|
"policy_id": 1,
|
||||||
|
"operation": "transfer",
|
||||||
|
"tags": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"status": "stopped",
|
||||||
|
"repository": "library/busybox",
|
||||||
|
"policy_id": 1,
|
||||||
|
"operation": "transfer",
|
||||||
|
"tags": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"status": "stopped",
|
||||||
|
"repository": "library/busybox",
|
||||||
|
"policy_id": 2,
|
||||||
|
"operation": "transfer",
|
||||||
|
"tags": null
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let mockEndpoints: Endpoint[] = [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"endpoint": "https://10.117.4.151",
|
||||||
|
"name": "target_01",
|
||||||
|
"username": "admin",
|
||||||
|
"password": "",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"endpoint": "https://10.117.5.142",
|
||||||
|
"name": "target_02",
|
||||||
|
"username": "AAA",
|
||||||
|
"password": "",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"endpoint": "https://101.1.11.111",
|
||||||
|
"name": "target_03",
|
||||||
|
"username": "admin",
|
||||||
|
"password": "",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"endpoint": "http://4.4.4.4",
|
||||||
|
"name": "target_04",
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"type": 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let mockRule: ReplicationRule = {
|
||||||
|
"id": 1,
|
||||||
|
"project_id": 1,
|
||||||
|
"project_name": "library",
|
||||||
|
"target_id": 1,
|
||||||
|
"target_name": "target_01",
|
||||||
|
"name": "sync_01",
|
||||||
|
"enabled": 0,
|
||||||
|
"description": "",
|
||||||
|
"cron_str": "",
|
||||||
|
"error_job_count": 2,
|
||||||
|
"deleted": 0
|
||||||
|
};
|
||||||
|
|
||||||
|
let fixture: ComponentFixture<ReplicationComponent>;
|
||||||
|
let fixtureCreate: ComponentFixture<CreateEditRuleComponent>;
|
||||||
|
|
||||||
|
let comp: ReplicationComponent;
|
||||||
|
let compCreate: CreateEditRuleComponent;
|
||||||
|
|
||||||
|
let replicationService: ReplicationService;
|
||||||
|
let endpointService: EndpointService;
|
||||||
|
|
||||||
|
let spyRules: jasmine.Spy;
|
||||||
|
let spyOneRule: jasmine.Spy;
|
||||||
|
|
||||||
|
let spyJobs: jasmine.Spy;
|
||||||
|
let spyEndpoint: jasmine.Spy;
|
||||||
|
|
||||||
|
let config: IServiceConfig = {
|
||||||
|
replicationRuleEndpoint: '/api/policies/replication/testing',
|
||||||
|
replicationJobEndpoint: '/api/jobs/replication/testing',
|
||||||
|
targetBaseEndpoint: '/api/targets/testing'
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async(()=>{
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
SharedModule,
|
||||||
|
NoopAnimationsModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ReplicationComponent,
|
||||||
|
ListReplicationRuleComponent,
|
||||||
|
CreateEditRuleComponent,
|
||||||
|
ConfirmationDialogComponent,
|
||||||
|
DatePickerComponent,
|
||||||
|
FilterComponent,
|
||||||
|
InlineAlertComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
ErrorHandler,
|
||||||
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
|
{ provide: ReplicationService, useClass: ReplicationDefaultService },
|
||||||
|
{ provide: EndpointService, useClass: EndpointDefaultService }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(()=>{
|
||||||
|
fixture = TestBed.createComponent(ReplicationComponent);
|
||||||
|
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
comp.projectId = 1;
|
||||||
|
comp.search.ruleId = 1;
|
||||||
|
|
||||||
|
replicationService = fixture.debugElement.injector.get(ReplicationService);
|
||||||
|
|
||||||
|
spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(Promise.resolve(mockRules));
|
||||||
|
spyOneRule = spyOn(replicationService, 'getReplicationRule').and.returnValue(Promise.resolve(mockRule));
|
||||||
|
spyJobs = spyOn(replicationService, 'getJobs').and.returnValues(Promise.resolve(mockJobs));
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(()=>{
|
||||||
|
fixtureCreate = TestBed.createComponent(CreateEditRuleComponent);
|
||||||
|
|
||||||
|
compCreate = fixtureCreate.componentInstance;
|
||||||
|
compCreate.projectId = 1;
|
||||||
|
|
||||||
|
endpointService = fixtureCreate.debugElement.injector.get(EndpointService);
|
||||||
|
spyEndpoint = spyOn(endpointService, 'getEndpoints').and.returnValues(Promise.resolve(mockEndpoints));
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should open creation modal and load endpoints', async(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
comp.openModal();
|
||||||
|
fixture.whenStable().then(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
let de: DebugElement = fixture.debugElement.query(By.css('input'));
|
||||||
|
expect(de).toBeTruthy();
|
||||||
|
let deSelect: DebugElement = fixture.debugElement.query(By.css('select'));
|
||||||
|
expect(deSelect).toBeTruthy();
|
||||||
|
let elSelect: HTMLElement = de.nativeElement;
|
||||||
|
expect(elSelect).toBeTruthy();
|
||||||
|
expect(elSelect.childNodes.item(0).textContent).toEqual('target_01');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('Should open modal to edit replication rule', async(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
comp.openEditRule(mockRule);
|
||||||
|
fixture.whenStable().then(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
let de: DebugElement = fixture.debugElement.query(By.css('input'));
|
||||||
|
expect(de).toBeTruthy();
|
||||||
|
fixture.detectChanges();
|
||||||
|
let el: HTMLElement = de.nativeElement;
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
expect(el.textContent.trim()).toEqual('sync_01');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
419
src/ui_ng/lib/src/create-edit-rule/create-edit-rule.component.ts
Normal file
419
src/ui_ng/lib/src/create-edit-rule/create-edit-rule.component.ts
Normal file
@ -0,0 +1,419 @@
|
|||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// 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, Input, Output, EventEmitter, ViewChild, AfterViewChecked } from '@angular/core';
|
||||||
|
|
||||||
|
import { NgForm } from '@angular/forms';
|
||||||
|
|
||||||
|
import { ReplicationService } from '../service/replication.service';
|
||||||
|
import { EndpointService } from '../service/endpoint.service';
|
||||||
|
|
||||||
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
|
import { ActionType } from '../shared/shared.const';
|
||||||
|
|
||||||
|
import { InlineAlertComponent } from '../inline-alert/inline-alert.component';
|
||||||
|
|
||||||
|
import { ReplicationRule } from '../service/interface';
|
||||||
|
import { Endpoint } from '../service/interface';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { CREATE_EDIT_RULE_STYLE } from './create-edit-rule.component.css';
|
||||||
|
import { CREATE_EDIT_RULE_TEMPLATE } from './create-edit-rule.component.html';
|
||||||
|
|
||||||
|
import { toPromise } from '../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rule form model.
|
||||||
|
*/
|
||||||
|
export interface CreateEditRule {
|
||||||
|
ruleId?: number | string;
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
enable?: boolean;
|
||||||
|
endpointId?: number | string;
|
||||||
|
endpointName?: string;
|
||||||
|
endpointUrl?: string;
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FAKE_PASSWORD: string = 'ywJZnDTM';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'create-edit-rule',
|
||||||
|
template: CREATE_EDIT_RULE_TEMPLATE,
|
||||||
|
styles: [ CREATE_EDIT_RULE_STYLE ]
|
||||||
|
})
|
||||||
|
export class CreateEditRuleComponent implements AfterViewChecked {
|
||||||
|
|
||||||
|
modalTitle: string;
|
||||||
|
createEditRuleOpened: boolean;
|
||||||
|
createEditRule: CreateEditRule = this.initCreateEditRule;
|
||||||
|
initVal: CreateEditRule = this.initCreateEditRule;
|
||||||
|
|
||||||
|
actionType: ActionType;
|
||||||
|
|
||||||
|
isCreateEndpoint: boolean;
|
||||||
|
@Input() projectId: number;
|
||||||
|
|
||||||
|
@Output() reload = new EventEmitter();
|
||||||
|
|
||||||
|
endpoints: Endpoint[];
|
||||||
|
|
||||||
|
pingTestMessage: string;
|
||||||
|
testOngoing: boolean;
|
||||||
|
pingStatus: boolean;
|
||||||
|
|
||||||
|
ruleForm: NgForm;
|
||||||
|
|
||||||
|
staticBackdrop: boolean = true;
|
||||||
|
closable: boolean = false;
|
||||||
|
|
||||||
|
@ViewChild('ruleForm')
|
||||||
|
currentForm: NgForm;
|
||||||
|
|
||||||
|
hasChanged: boolean;
|
||||||
|
|
||||||
|
editable: boolean;
|
||||||
|
|
||||||
|
get initCreateEditRule(): CreateEditRule {
|
||||||
|
return {
|
||||||
|
endpointId: '',
|
||||||
|
name: '',
|
||||||
|
enable: false,
|
||||||
|
description: '',
|
||||||
|
endpointName: '',
|
||||||
|
endpointUrl: '',
|
||||||
|
username: '',
|
||||||
|
password: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get initReplicationRule(): ReplicationRule {
|
||||||
|
return {
|
||||||
|
project_id: '',
|
||||||
|
project_name: '',
|
||||||
|
target_id: '',
|
||||||
|
target_name: '',
|
||||||
|
enabled: 0,
|
||||||
|
description: '',
|
||||||
|
cron_str: '',
|
||||||
|
error_job_count: 0,
|
||||||
|
deleted: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get initEndpoint(): Endpoint {
|
||||||
|
return {
|
||||||
|
endpoint: '',
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
type: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewChild(InlineAlertComponent)
|
||||||
|
inlineAlert: InlineAlertComponent;
|
||||||
|
|
||||||
|
get readonly(): boolean {
|
||||||
|
return this.actionType === ActionType.EDIT && (this.createEditRule.enable || false);
|
||||||
|
}
|
||||||
|
|
||||||
|
get untoggleable(): boolean {
|
||||||
|
return this.actionType === ActionType.EDIT && (this.initVal.enable || false);
|
||||||
|
}
|
||||||
|
|
||||||
|
get showNewDestination(): boolean {
|
||||||
|
return this.actionType === ActionType.ADD_NEW || (!this.createEditRule.enable || false);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private replicationService: ReplicationService,
|
||||||
|
private endpointService: EndpointService,
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
|
private translateService: TranslateService) {}
|
||||||
|
|
||||||
|
prepareTargets(endpointId?: number | string) {
|
||||||
|
toPromise<Endpoint[]>(this.endpointService
|
||||||
|
.getEndpoints())
|
||||||
|
.then(endpoints=>{
|
||||||
|
this.endpoints = endpoints;
|
||||||
|
if(this.endpoints && this.endpoints.length > 0) {
|
||||||
|
let initialEndpoint: Endpoint | undefined;
|
||||||
|
(endpointId) ? initialEndpoint = this.endpoints.find(t=>t.id===endpointId) : initialEndpoint = this.endpoints[0];
|
||||||
|
if(!initialEndpoint) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.createEditRule.endpointId = initialEndpoint.id;
|
||||||
|
this.createEditRule.endpointName = initialEndpoint.name;
|
||||||
|
this.createEditRule.endpointUrl = initialEndpoint.endpoint;
|
||||||
|
this.createEditRule.username = initialEndpoint.username;
|
||||||
|
this.createEditRule.password = FAKE_PASSWORD;
|
||||||
|
|
||||||
|
this.initVal.endpointId = this.createEditRule.endpointId;
|
||||||
|
this.initVal.endpointUrl = this.createEditRule.endpointUrl;
|
||||||
|
this.initVal.username = this.createEditRule.username;
|
||||||
|
this.initVal.password = this.createEditRule.password;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error=>{
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
this.createEditRuleOpened = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openCreateEditRule(editable: boolean, ruleId?: number | string): void {
|
||||||
|
|
||||||
|
this.createEditRule = this.initCreateEditRule;
|
||||||
|
this.editable = editable;
|
||||||
|
|
||||||
|
this.isCreateEndpoint = false;
|
||||||
|
this.hasChanged = false;
|
||||||
|
|
||||||
|
this.pingTestMessage = '';
|
||||||
|
this.pingStatus = true;
|
||||||
|
this.testOngoing = false;
|
||||||
|
|
||||||
|
if(ruleId) {
|
||||||
|
this.actionType = ActionType.EDIT;
|
||||||
|
this.translateService.get('REPLICATION.EDIT_POLICY_TITLE').subscribe(res=>this.modalTitle=res);
|
||||||
|
toPromise<ReplicationRule>(this.replicationService
|
||||||
|
.getReplicationRule(ruleId))
|
||||||
|
.then(rule=>{
|
||||||
|
if(rule) {
|
||||||
|
this.createEditRule.ruleId = ruleId;
|
||||||
|
this.createEditRule.name = rule.name;
|
||||||
|
this.createEditRule.description = rule.description;
|
||||||
|
this.createEditRule.enable = rule.enabled === 1? true : false;
|
||||||
|
this.prepareTargets(rule.target_id);
|
||||||
|
|
||||||
|
this.initVal.name = this.createEditRule.name;
|
||||||
|
this.initVal.description = this.createEditRule.description;
|
||||||
|
this.initVal.enable = this.createEditRule.enable;
|
||||||
|
|
||||||
|
this.createEditRuleOpened = true;
|
||||||
|
}
|
||||||
|
}).catch(err=>this.errorHandler.error(err));
|
||||||
|
} else {
|
||||||
|
if(!this.projectId) {
|
||||||
|
this.errorHandler.error('Project ID cannot be unset');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.actionType = ActionType.ADD_NEW;
|
||||||
|
this.translateService.get('REPLICATION.ADD_POLICY').subscribe(res=>this.modalTitle=res);
|
||||||
|
this.prepareTargets();
|
||||||
|
this.createEditRuleOpened = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newEndpoint(checkedAddNew: boolean): void {
|
||||||
|
this.isCreateEndpoint = checkedAddNew;
|
||||||
|
if(this.isCreateEndpoint) {
|
||||||
|
this.createEditRule.endpointName = '';
|
||||||
|
this.createEditRule.endpointUrl = '';
|
||||||
|
this.createEditRule.username = '';
|
||||||
|
this.createEditRule.password = '';
|
||||||
|
} else {
|
||||||
|
this.prepareTargets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectEndpoint(): void {
|
||||||
|
let result: Endpoint | undefined = this.endpoints.find(target=>target.id == this.createEditRule.endpointId);
|
||||||
|
if(result) {
|
||||||
|
this.createEditRule.endpointId = result.id;
|
||||||
|
this.createEditRule.endpointUrl = result.endpoint;
|
||||||
|
this.createEditRule.username = result.username;
|
||||||
|
this.createEditRule.password = FAKE_PASSWORD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getRuleByForm(): ReplicationRule {
|
||||||
|
let rule: ReplicationRule = this.initReplicationRule;
|
||||||
|
rule.project_id = this.projectId;
|
||||||
|
rule.id = this.createEditRule.ruleId;
|
||||||
|
rule.name = this.createEditRule.name;
|
||||||
|
rule.description = this.createEditRule.description;
|
||||||
|
rule.enabled = this.createEditRule.enable ? 1 : 0;
|
||||||
|
rule.target_id = this.createEditRule.endpointId || '';
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEndpointByForm(): Endpoint {
|
||||||
|
let endpoint: Endpoint = this.initEndpoint;
|
||||||
|
endpoint.id = this.createEditRule.ruleId;
|
||||||
|
endpoint.name = this.createEditRule.endpointName || '';
|
||||||
|
endpoint.endpoint = this.createEditRule.endpointUrl || '';
|
||||||
|
endpoint.username = this.createEditRule.username;
|
||||||
|
endpoint.password = this.createEditRule.password;
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
createReplicationRule(): void {
|
||||||
|
toPromise<ReplicationRule>(this.replicationService
|
||||||
|
.createReplicationRule(this.getRuleByForm()))
|
||||||
|
.then(response=>{
|
||||||
|
this.translateService.get('REPLICATION.CREATED_SUCCESS')
|
||||||
|
.subscribe(res=>this.errorHandler.info(res));
|
||||||
|
this.createEditRuleOpened = false;
|
||||||
|
this.reload.emit(true);
|
||||||
|
})
|
||||||
|
.catch(error=>{
|
||||||
|
if (error.status === 409) {
|
||||||
|
this.inlineAlert.showInlineError('REPLICATION.POLICY_ALREADY_EXISTS');
|
||||||
|
} else {
|
||||||
|
this.inlineAlert.showInlineError(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateReplicationRule(): void {
|
||||||
|
toPromise<ReplicationRule>(this.replicationService
|
||||||
|
.updateReplicationRule(this.getRuleByForm()))
|
||||||
|
.then(()=>{
|
||||||
|
this.translateService.get('REPLICATION.UPDATED_SUCCESS')
|
||||||
|
.subscribe(res=>this.errorHandler.info(res));
|
||||||
|
this.createEditRuleOpened = false;
|
||||||
|
this.reload.emit(true);
|
||||||
|
})
|
||||||
|
.catch(error=>{
|
||||||
|
if (error.status === 409) {
|
||||||
|
this.inlineAlert.showInlineError('REPLICATION.POLICY_ALREADY_EXISTS');
|
||||||
|
} else {
|
||||||
|
this.inlineAlert.showInlineError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createWithEndpoint(actionType: ActionType): void {
|
||||||
|
toPromise<Endpoint>(this.endpointService
|
||||||
|
.createEndpoint(this.getEndpointByForm()))
|
||||||
|
.then(()=>{
|
||||||
|
toPromise<Endpoint[]>(this.endpointService
|
||||||
|
.getEndpoints(this.createEditRule.endpointName))
|
||||||
|
.then(endpoints=>{
|
||||||
|
if(endpoints && endpoints.length > 0) {
|
||||||
|
let addedEndpoint: Endpoint = endpoints[0];
|
||||||
|
this.createEditRule.endpointId = addedEndpoint.id;
|
||||||
|
switch(actionType) {
|
||||||
|
case ActionType.ADD_NEW:
|
||||||
|
this.createReplicationRule();
|
||||||
|
break;
|
||||||
|
case ActionType.EDIT:
|
||||||
|
this.updateReplicationRule();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error=>{
|
||||||
|
this.inlineAlert.showInlineError(error);
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error=>{
|
||||||
|
this.inlineAlert.showInlineError(error);
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
if(this.isCreateEndpoint) {
|
||||||
|
this.createWithEndpoint(this.actionType);
|
||||||
|
} else {
|
||||||
|
switch(this.actionType) {
|
||||||
|
case ActionType.ADD_NEW:
|
||||||
|
this.createReplicationRule();
|
||||||
|
break;
|
||||||
|
case ActionType.EDIT:
|
||||||
|
this.updateReplicationRule();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel() {
|
||||||
|
if(this.hasChanged) {
|
||||||
|
this.inlineAlert.showInlineConfirmation({message: 'ALERT.FORM_CHANGE_CONFIRMATION'});
|
||||||
|
} else {
|
||||||
|
this.createEditRuleOpened = false;
|
||||||
|
this.ruleForm.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmCancel(confirmed: boolean) {
|
||||||
|
this.createEditRuleOpened = false;
|
||||||
|
this.inlineAlert.close();
|
||||||
|
this.ruleForm.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewChecked(): void {
|
||||||
|
this.ruleForm = this.currentForm;
|
||||||
|
if(this.ruleForm) {
|
||||||
|
let comparison: {[key: string]: any} = {
|
||||||
|
name: this.initVal.name,
|
||||||
|
description: this.initVal.description,
|
||||||
|
enable: this.initVal.enable,
|
||||||
|
endpointId: this.initVal.endpointId,
|
||||||
|
targetName: this.initVal.name,
|
||||||
|
endpointUrl: this.initVal.endpointUrl,
|
||||||
|
username: this.initVal.username,
|
||||||
|
password: this.initVal.password
|
||||||
|
};
|
||||||
|
let self: CreateEditRuleComponent | any = this;
|
||||||
|
if(self) {
|
||||||
|
self.ruleForm.valueChanges.subscribe((data: any)=>{
|
||||||
|
for(let key in data) {
|
||||||
|
let current = data[key];
|
||||||
|
let origin: string = comparison[key];
|
||||||
|
if(((self.actionType === ActionType.EDIT && !self.readonly && !current ) || current) && current !== origin) {
|
||||||
|
self.hasChanged = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
self.hasChanged = false;
|
||||||
|
self.inlineAlert.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testConnection() {
|
||||||
|
this.pingStatus = true;
|
||||||
|
this.translateService.get('REPLICATION.TESTING_CONNECTION').subscribe(res=>this.pingTestMessage=res);
|
||||||
|
this.testOngoing = !this.testOngoing;
|
||||||
|
let pingTarget: Endpoint = this.initEndpoint;
|
||||||
|
if(this.isCreateEndpoint) {
|
||||||
|
pingTarget.endpoint = this.createEditRule.endpointUrl || '';
|
||||||
|
pingTarget.username = this.createEditRule.username;
|
||||||
|
pingTarget.password = this.createEditRule.password;
|
||||||
|
} else {
|
||||||
|
pingTarget.id = this.createEditRule.endpointId;
|
||||||
|
}
|
||||||
|
toPromise<Endpoint>(this.endpointService
|
||||||
|
.pingEndpoint(pingTarget))
|
||||||
|
.then(()=>{
|
||||||
|
this.testOngoing = !this.testOngoing;
|
||||||
|
this.translateService.get('REPLICATION.TEST_CONNECTION_SUCCESS').subscribe(res=>this.pingTestMessage=res);
|
||||||
|
this.pingStatus = true;
|
||||||
|
})
|
||||||
|
.catch(error=>{
|
||||||
|
this.testOngoing = !this.testOngoing;
|
||||||
|
this.translateService.get('REPLICATION.TEST_CONNECTION_FAILURE').subscribe(res=>this.pingTestMessage=res);
|
||||||
|
this.pingStatus = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
7
src/ui_ng/lib/src/create-edit-rule/index.ts
Normal file
7
src/ui_ng/lib/src/create-edit-rule/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Type } from '@angular/core';
|
||||||
|
|
||||||
|
import { CreateEditRuleComponent } from './create-edit-rule.component';
|
||||||
|
|
||||||
|
export const CREATE_EDIT_RULE_DIRECTIVES: Type<any>[] = [
|
||||||
|
CreateEditRuleComponent
|
||||||
|
];
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// 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 { Directive, OnChanges, Input, SimpleChanges } from '@angular/core';
|
||||||
|
import { NG_VALIDATORS, Validator, Validators, ValidatorFn, AbstractControl } from '@angular/forms';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[dateValidator]',
|
||||||
|
providers: [{provide: NG_VALIDATORS, useExisting: DateValidatorDirective, multi: true}]
|
||||||
|
})
|
||||||
|
export class DateValidatorDirective implements Validator, OnChanges {
|
||||||
|
@Input() dateValidator: string;
|
||||||
|
private valFn = Validators.nullValidator;
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
const change = changes['dateValidator'];
|
||||||
|
if (change) {
|
||||||
|
this.valFn = dateValidator();
|
||||||
|
} else {
|
||||||
|
this.valFn = Validators.nullValidator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
validate(control: AbstractControl): {[key: string]: any} {
|
||||||
|
return this.valFn(control) || Validators.nullValidator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dateValidator(): ValidatorFn {
|
||||||
|
return (control: AbstractControl): {[key: string]: any} => {
|
||||||
|
let controlValue = control.value;
|
||||||
|
let valid = true;
|
||||||
|
if(controlValue) {
|
||||||
|
const regYMD=/^(19|20)\d\d([- /.])(0[1-9]|1[012])\2(0[1-9]|[12][0-9]|3[01])$/g;
|
||||||
|
const regDMY=/^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d$/g;
|
||||||
|
valid = (regYMD.test(controlValue) || regDMY.test(controlValue));
|
||||||
|
}
|
||||||
|
return valid ? Validators.nullValidator : {'dateValidator': { value: controlValue }};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
export const DATETIME_PICKER_TEMPLATE: string = `
|
||||||
|
<clr-icon shape="date"></clr-icon>
|
||||||
|
<label aria-haspopup="true" role="tooltip" [class.invalid]="dateInvalid" class="tooltip tooltip-validation tooltip-sm">
|
||||||
|
<input type="date" #searchTime="ngModel" [(ngModel)]="dateInput" name="searchTime" placeholder="dd/mm/yyyy" dateValidator (change)="doSearch()">
|
||||||
|
<span *ngIf="dateInvalid" class="tooltip-content">
|
||||||
|
{{'AUDIT_LOG.INVALID_DATE' | translate }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
`;
|
@ -0,0 +1,43 @@
|
|||||||
|
import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
|
||||||
|
import { NgModel } from '@angular/forms';
|
||||||
|
|
||||||
|
import { DATETIME_PICKER_TEMPLATE } from './datetime-picker.component.html';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'hbr-datetime',
|
||||||
|
template: DATETIME_PICKER_TEMPLATE
|
||||||
|
})
|
||||||
|
export class DatePickerComponent {
|
||||||
|
|
||||||
|
@Input() dateInput: string;
|
||||||
|
@Input() oneDayOffset: boolean;
|
||||||
|
|
||||||
|
@ViewChild('searchTime')
|
||||||
|
searchTime: NgModel;
|
||||||
|
|
||||||
|
@Output() search = new EventEmitter<string>();
|
||||||
|
|
||||||
|
get dateInvalid(): boolean {
|
||||||
|
return (this.searchTime.errors && this.searchTime.errors.dateValidator && (this.searchTime.dirty || this.searchTime.touched)) || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
convertDate(strDate: string): string {
|
||||||
|
if(/^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d$/.test(strDate)) {
|
||||||
|
let parts = strDate.split(/[-\/]/);
|
||||||
|
strDate = parts[2] /*Year*/ + '-' +parts[1] /*Month*/ + '-' + parts[0] /*Date*/;
|
||||||
|
}
|
||||||
|
return strDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
doSearch() {
|
||||||
|
let searchTerm: string = '';
|
||||||
|
if(this.searchTime.valid && this.dateInput) {
|
||||||
|
let timestamp: number = new Date(this.convertDate(this.searchTime.value)).getTime() / 1000;
|
||||||
|
if(this.oneDayOffset) {
|
||||||
|
timestamp += 3600 * 24;
|
||||||
|
}
|
||||||
|
searchTerm = timestamp.toString();
|
||||||
|
}
|
||||||
|
this.search.emit(searchTerm);
|
||||||
|
}
|
||||||
|
}
|
8
src/ui_ng/lib/src/datetime-picker/index.ts
Normal file
8
src/ui_ng/lib/src/datetime-picker/index.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Type } from '@angular/core';
|
||||||
|
|
||||||
|
import { DatePickerComponent } from './datetime-picker.component';
|
||||||
|
import { DateValidatorDirective } from './date-validator.directive';
|
||||||
|
export const DATETIME_PICKER_DIRECTIVES: Type<any>[] = [
|
||||||
|
DatePickerComponent,
|
||||||
|
DateValidatorDirective
|
||||||
|
];
|
@ -163,13 +163,12 @@ export class EndpointComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deleteTarget(target: Endpoint) {
|
deleteTarget(target: Endpoint) {
|
||||||
console.log('Endpoint:' + JSON.stringify(target));
|
|
||||||
if (target) {
|
if (target) {
|
||||||
let targetId = target.id;
|
let targetId = target.id;
|
||||||
let deletionMessage = new ConfirmationMessage(
|
let deletionMessage = new ConfirmationMessage(
|
||||||
'REPLICATION.DELETION_TITLE_TARGET',
|
'REPLICATION.DELETION_TITLE_TARGET',
|
||||||
'REPLICATION.DELETION_SUMMARY_TARGET',
|
'REPLICATION.DELETION_SUMMARY_TARGET',
|
||||||
target.name,
|
target.name || '',
|
||||||
target.id,
|
target.id,
|
||||||
ConfirmationTargets.TARGET,
|
ConfirmationTargets.TARGET,
|
||||||
ConfirmationButtons.DELETE_CANCEL);
|
ConfirmationButtons.DELETE_CANCEL);
|
||||||
|
@ -7,12 +7,17 @@ import { REPOSITORY_DIRECTIVES } from './repository/index';
|
|||||||
import { LIST_REPOSITORY_DIRECTIVES } from './list-repository/index';
|
import { LIST_REPOSITORY_DIRECTIVES } from './list-repository/index';
|
||||||
import { TAG_DIRECTIVES } from './tag/index';
|
import { TAG_DIRECTIVES } from './tag/index';
|
||||||
|
|
||||||
|
import { REPLICATION_DIRECTIVES } from './replication/index';
|
||||||
|
import { CREATE_EDIT_RULE_DIRECTIVES } from './create-edit-rule/index';
|
||||||
|
import { LIST_REPLICATION_RULE_DIRECTIVES } from './list-replication-rule/index';
|
||||||
|
|
||||||
import { CREATE_EDIT_ENDPOINT_DIRECTIVES } from './create-edit-endpoint/index';
|
import { CREATE_EDIT_ENDPOINT_DIRECTIVES } from './create-edit-endpoint/index';
|
||||||
|
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from './service.config';
|
import { SERVICE_CONFIG, IServiceConfig } from './service.config';
|
||||||
|
|
||||||
import { CONFIRMATION_DIALOG_DIRECTIVES } from './confirmation-dialog/index';
|
import { CONFIRMATION_DIALOG_DIRECTIVES } from './confirmation-dialog/index';
|
||||||
import { INLINE_ALERT_DIRECTIVES } from './inline-alert/index';
|
import { INLINE_ALERT_DIRECTIVES } from './inline-alert/index';
|
||||||
|
import { DATETIME_PICKER_DIRECTIVES } from './datetime-picker/index';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AccessLogService,
|
AccessLogService,
|
||||||
@ -41,11 +46,11 @@ import { CookieService } from 'ngx-cookie';
|
|||||||
*/
|
*/
|
||||||
export const DefaultServiceConfig: IServiceConfig = {
|
export const DefaultServiceConfig: IServiceConfig = {
|
||||||
systemInfoEndpoint: "/api/system",
|
systemInfoEndpoint: "/api/system",
|
||||||
repositoryBaseEndpoint: "",
|
repositoryBaseEndpoint: "/api/repositories",
|
||||||
logBaseEndpoint: "/api/logs",
|
logBaseEndpoint: "/api/logs",
|
||||||
targetBaseEndpoint: "",
|
targetBaseEndpoint: "/api/targets",
|
||||||
replicationRuleEndpoint: "",
|
replicationRuleEndpoint: "/api/policies/replication",
|
||||||
replicationJobEndpoint: "",
|
replicationJobEndpoint: "/api/jobs/replication",
|
||||||
langCookieKey: DEFAULT_LANG_COOKIE_KEY,
|
langCookieKey: DEFAULT_LANG_COOKIE_KEY,
|
||||||
supportedLangs: DEFAULT_SUPPORTING_LANGS,
|
supportedLangs: DEFAULT_SUPPORTING_LANGS,
|
||||||
enablei18Support: false
|
enablei18Support: false
|
||||||
@ -128,7 +133,11 @@ export function initConfig(translateService: TranslateService, config: IServiceC
|
|||||||
TAG_DIRECTIVES,
|
TAG_DIRECTIVES,
|
||||||
CREATE_EDIT_ENDPOINT_DIRECTIVES,
|
CREATE_EDIT_ENDPOINT_DIRECTIVES,
|
||||||
CONFIRMATION_DIALOG_DIRECTIVES,
|
CONFIRMATION_DIALOG_DIRECTIVES,
|
||||||
INLINE_ALERT_DIRECTIVES
|
INLINE_ALERT_DIRECTIVES,
|
||||||
|
REPLICATION_DIRECTIVES,
|
||||||
|
LIST_REPLICATION_RULE_DIRECTIVES,
|
||||||
|
CREATE_EDIT_RULE_DIRECTIVES,
|
||||||
|
DATETIME_PICKER_DIRECTIVES
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
LOG_DIRECTIVES,
|
LOG_DIRECTIVES,
|
||||||
@ -139,7 +148,11 @@ export function initConfig(translateService: TranslateService, config: IServiceC
|
|||||||
TAG_DIRECTIVES,
|
TAG_DIRECTIVES,
|
||||||
CREATE_EDIT_ENDPOINT_DIRECTIVES,
|
CREATE_EDIT_ENDPOINT_DIRECTIVES,
|
||||||
CONFIRMATION_DIALOG_DIRECTIVES,
|
CONFIRMATION_DIALOG_DIRECTIVES,
|
||||||
INLINE_ALERT_DIRECTIVES
|
INLINE_ALERT_DIRECTIVES,
|
||||||
|
REPLICATION_DIRECTIVES,
|
||||||
|
LIST_REPLICATION_RULE_DIRECTIVES,
|
||||||
|
CREATE_EDIT_RULE_DIRECTIVES,
|
||||||
|
DATETIME_PICKER_DIRECTIVES
|
||||||
],
|
],
|
||||||
providers: []
|
providers: []
|
||||||
})
|
})
|
||||||
|
@ -253,6 +253,7 @@ export const EN_US_LANG: any = {
|
|||||||
"CREATION_TIME": "Start Time",
|
"CREATION_TIME": "Start Time",
|
||||||
"END_TIME": "End Time",
|
"END_TIME": "End Time",
|
||||||
"LOGS": "Logs",
|
"LOGS": "Logs",
|
||||||
|
"OF": "of",
|
||||||
"ITEMS": "item(s)",
|
"ITEMS": "item(s)",
|
||||||
"TOGGLE_ENABLE_TITLE": "Enable Rule",
|
"TOGGLE_ENABLE_TITLE": "Enable Rule",
|
||||||
"CONFIRM_TOGGLE_ENABLE_POLICY": "After enabling the replication rule, all repositories under the project will be replicated to the destination registry. \nPlease confirm to continue.",
|
"CONFIRM_TOGGLE_ENABLE_POLICY": "After enabling the replication rule, all repositories under the project will be replicated to the destination registry. \nPlease confirm to continue.",
|
||||||
|
@ -253,6 +253,7 @@ export const ZH_CN_LANG: any = {
|
|||||||
"CREATION_TIME": "创建时间",
|
"CREATION_TIME": "创建时间",
|
||||||
"END_TIME": "结束时间",
|
"END_TIME": "结束时间",
|
||||||
"LOGS": "日志",
|
"LOGS": "日志",
|
||||||
|
"OF": "共计",
|
||||||
"ITEMS": "条记录",
|
"ITEMS": "条记录",
|
||||||
"TOGGLE_ENABLE_TITLE": "启用规则",
|
"TOGGLE_ENABLE_TITLE": "启用规则",
|
||||||
"CONFIRM_TOGGLE_ENABLE_POLICY": "启用规则后,该项目下的所有镜像仓库将复制到目标实例。\n请确认继续。",
|
"CONFIRM_TOGGLE_ENABLE_POLICY": "启用规则后,该项目下的所有镜像仓库将复制到目标实例。\n请确认继续。",
|
||||||
|
@ -5,4 +5,7 @@ export * from './error-handler/index';
|
|||||||
//export * from './utils';
|
//export * from './utils';
|
||||||
export * from './log/index';
|
export * from './log/index';
|
||||||
export * from './filter/index';
|
export * from './filter/index';
|
||||||
export * from './endpoint/index';
|
export * from './endpoint/index';
|
||||||
|
export * from './repository/index';
|
||||||
|
export * from './tag/index';
|
||||||
|
export * from './replication/index';
|
7
src/ui_ng/lib/src/list-replication-rule/index.ts
Normal file
7
src/ui_ng/lib/src/list-replication-rule/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Type } from '@angular/core';
|
||||||
|
|
||||||
|
import { ListReplicationRuleComponent } from './list-replication-rule.component';
|
||||||
|
|
||||||
|
export const LIST_REPLICATION_RULE_DIRECTIVES: Type<any>[] = [
|
||||||
|
ListReplicationRuleComponent
|
||||||
|
];
|
@ -0,0 +1,40 @@
|
|||||||
|
export const LIST_REPLICATION_RULE_TEMPLATE: string = `
|
||||||
|
<confirmation-dialog #toggleConfirmDialog (confirmAction)="toggleConfirm($event)"></confirmation-dialog>
|
||||||
|
<confirmation-dialog #deletionConfirmDialog (confirmAction)="deletionConfirm($event)"></confirmation-dialog>
|
||||||
|
<clr-datagrid>
|
||||||
|
<clr-dg-column>{{'REPLICATION.NAME' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column *ngIf="projectless">{{'REPLICATION.PROJECT' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'REPLICATION.DESCRIPTION' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'REPLICATION.DESTINATION_NAME' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'REPLICATION.LAST_START_TIME' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'REPLICATION.ACTIVATION' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-row *clrDgItems="let p of rules" [clrDgItem]="p" (click)="selectRule(p)" [style.backgroundColor]="(!projectless && selectedId === p.id) ? '#eee' : ''">
|
||||||
|
<clr-dg-action-overflow>
|
||||||
|
<button class="action-item" (click)="editRule(p)">{{'REPLICATION.EDIT_POLICY' | translate}}</button>
|
||||||
|
<button class="action-item" (click)="toggleRule(p)">{{ (p.enabled === 0 ? 'REPLICATION.ENABLE' : 'REPLICATION.DISABLE') | translate}}</button>
|
||||||
|
<button class="action-item" (click)="deleteRule(p)">{{'REPLICATION.DELETE_POLICY' | translate}}</button>
|
||||||
|
</clr-dg-action-overflow>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<ng-template [ngIf]="projectless">
|
||||||
|
<a href="javascript:void(0)">{{p.name}}</a>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template [ngIf]="!projectless">
|
||||||
|
{{p.name}}
|
||||||
|
</ng-template>
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell *ngIf="projectless">{{p.project_name}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{p.description ? p.description : '-'}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{p.target_name}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<ng-template [ngIf]="p.start_time === nullTime">-</ng-template>
|
||||||
|
<ng-template [ngIf]="p.start_time !== nullTime">{{p.start_time}}</ng-template>
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
{{ (p.enabled === 1 ? 'REPLICATION.ENABLED' : 'REPLICATION.DISABLED') | translate}}
|
||||||
|
</clr-dg-cell>
|
||||||
|
</clr-dg-row>
|
||||||
|
<clr-dg-footer>
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPLICATION.OF' | translate}} {{pagination.totalItems }} {{'REPLICATION.ITEMS' | translate}}
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>`;
|
@ -0,0 +1,127 @@
|
|||||||
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
|
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||||
|
|
||||||
|
import { ListReplicationRuleComponent } from '../list-replication-rule/list-replication-rule.component';
|
||||||
|
import { ReplicationRule } from '../service/interface';
|
||||||
|
|
||||||
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
|
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||||
|
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
|
||||||
|
|
||||||
|
|
||||||
|
describe('ListReplicationRuleComponent (inline template)', ()=>{
|
||||||
|
|
||||||
|
let mockRules: ReplicationRule[] = [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"project_id": 1,
|
||||||
|
"project_name": "library",
|
||||||
|
"target_id": 1,
|
||||||
|
"target_name": "target_01",
|
||||||
|
"name": "sync_01",
|
||||||
|
"enabled": 0,
|
||||||
|
"description": "",
|
||||||
|
"cron_str": "",
|
||||||
|
"error_job_count": 2,
|
||||||
|
"deleted": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"project_id": 1,
|
||||||
|
"project_name": "library",
|
||||||
|
"target_id": 3,
|
||||||
|
"target_name": "target_02",
|
||||||
|
"name": "sync_02",
|
||||||
|
"enabled": 1,
|
||||||
|
"description": "",
|
||||||
|
"cron_str": "",
|
||||||
|
"error_job_count": 1,
|
||||||
|
"deleted": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"project_id": 1,
|
||||||
|
"project_name": "library",
|
||||||
|
"target_id": 2,
|
||||||
|
"target_name": "target_03",
|
||||||
|
"name": "sync_03",
|
||||||
|
"enabled": 0,
|
||||||
|
"description": "",
|
||||||
|
"cron_str": "",
|
||||||
|
"error_job_count": 0,
|
||||||
|
"deleted": 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
let mockRule: ReplicationRule = {
|
||||||
|
"id": 1,
|
||||||
|
"project_id": 1,
|
||||||
|
"project_name": "library",
|
||||||
|
"target_id": 1,
|
||||||
|
"target_name": "target_01",
|
||||||
|
"name": "sync_01",
|
||||||
|
"enabled": 0,
|
||||||
|
"description": "",
|
||||||
|
"cron_str": "",
|
||||||
|
"error_job_count": 2,
|
||||||
|
"deleted": 0
|
||||||
|
};
|
||||||
|
|
||||||
|
let fixture: ComponentFixture<ListReplicationRuleComponent>;
|
||||||
|
|
||||||
|
let comp: ListReplicationRuleComponent;
|
||||||
|
|
||||||
|
let replicationService: ReplicationService;
|
||||||
|
|
||||||
|
let spyRules: jasmine.Spy;
|
||||||
|
|
||||||
|
let config: IServiceConfig = {
|
||||||
|
replicationRuleEndpoint: '/api/policies/replication/testing'
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async(()=>{
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
SharedModule,
|
||||||
|
NoopAnimationsModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ListReplicationRuleComponent,
|
||||||
|
ConfirmationDialogComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
ErrorHandler,
|
||||||
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
|
{ provide: ReplicationService, useClass: ReplicationDefaultService }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(()=>{
|
||||||
|
fixture = TestBed.createComponent(ListReplicationRuleComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
replicationService = fixture.debugElement.injector.get(ReplicationService);
|
||||||
|
spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(Promise.resolve(mockRules));
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should load and render data', async(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
let de: DebugElement = fixture.debugElement.query(By.css('datagrid-cell'));
|
||||||
|
expect(de).toBeTruthy();
|
||||||
|
fixture.detectChanges();
|
||||||
|
let el: HTMLElement = de.nativeElement;
|
||||||
|
expect(el.textContent.trim()).toEqual('sync_01');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,136 @@
|
|||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// 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, Input, Output, EventEmitter, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||||
|
|
||||||
|
import { ReplicationService } from '../service/replication.service';
|
||||||
|
import { ReplicationRule } from '../service/interface';
|
||||||
|
|
||||||
|
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||||
|
import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
|
||||||
|
import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
|
||||||
|
|
||||||
|
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
|
import { toPromise } from '../utils';
|
||||||
|
|
||||||
|
import { State } from 'clarity-angular';
|
||||||
|
|
||||||
|
import { LIST_REPLICATION_RULE_TEMPLATE } from './list-replication-rule.component.html';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'list-replication-rule',
|
||||||
|
template: LIST_REPLICATION_RULE_TEMPLATE,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ListReplicationRuleComponent {
|
||||||
|
|
||||||
|
nullTime: string = '0001-01-01T00:00:00Z';
|
||||||
|
|
||||||
|
@Input() rules: ReplicationRule[];
|
||||||
|
@Input() projectless: boolean;
|
||||||
|
@Input() selectedId: number | string;
|
||||||
|
|
||||||
|
@Output() reload = new EventEmitter<boolean>();
|
||||||
|
@Output() selectOne = new EventEmitter<ReplicationRule>();
|
||||||
|
@Output() editOne = new EventEmitter<ReplicationRule>();
|
||||||
|
@Output() toggleOne = new EventEmitter<ReplicationRule>();
|
||||||
|
|
||||||
|
@ViewChild('toggleConfirmDialog')
|
||||||
|
toggleConfirmDialog: ConfirmationDialogComponent;
|
||||||
|
|
||||||
|
@ViewChild('deletionConfirmDialog')
|
||||||
|
deletionConfirmDialog: ConfirmationDialogComponent;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private replicationService: ReplicationService,
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
|
private ref: ChangeDetectorRef) {
|
||||||
|
setInterval(()=>ref.markForCheck(), 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleConfirm(message: ConfirmationAcknowledgement) {
|
||||||
|
if(message &&
|
||||||
|
message.source === ConfirmationTargets.TOGGLE_CONFIRM &&
|
||||||
|
message.state === ConfirmationState.CONFIRMED) {
|
||||||
|
let rule: ReplicationRule = message.data;
|
||||||
|
if(rule) {
|
||||||
|
rule.enabled = rule.enabled === 0 ? 1 : 0;
|
||||||
|
toPromise<any>(this.replicationService
|
||||||
|
.enableReplicationRule(rule.id || '', rule.enabled))
|
||||||
|
.then(() =>
|
||||||
|
this.translateService.get('REPLICATION.TOGGLED_SUCCESS')
|
||||||
|
.subscribe(res=>this.errorHandler.info(res)))
|
||||||
|
.catch(error => this.errorHandler.error(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deletionConfirm(message: ConfirmationAcknowledgement) {
|
||||||
|
if (message &&
|
||||||
|
message.source === ConfirmationTargets.POLICY &&
|
||||||
|
message.state === ConfirmationState.CONFIRMED) {
|
||||||
|
toPromise<any>(this.replicationService
|
||||||
|
.deleteReplicationRule(message.data))
|
||||||
|
.then(() => {
|
||||||
|
this.translateService.get('REPLICATION.DELETED_SUCCESS')
|
||||||
|
.subscribe(res=>this.errorHandler.info(res));
|
||||||
|
this.reload.emit(true);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
if(error && error.status === 412) {
|
||||||
|
this.translateService.get('REPLICATION.FAILED_TO_DELETE_POLICY_ENABLED')
|
||||||
|
.subscribe(res=>this.errorHandler.error(res));
|
||||||
|
} else {
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectRule(rule: ReplicationRule): void {
|
||||||
|
this.selectedId = rule.id || '';
|
||||||
|
this.selectOne.emit(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
editRule(rule: ReplicationRule) {
|
||||||
|
this.editOne.emit(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleRule(rule: ReplicationRule) {
|
||||||
|
let toggleConfirmMessage: ConfirmationMessage = new ConfirmationMessage(
|
||||||
|
rule.enabled === 1 ? 'REPLICATION.TOGGLE_DISABLE_TITLE' : 'REPLICATION.TOGGLE_ENABLE_TITLE',
|
||||||
|
rule.enabled === 1 ? 'REPLICATION.CONFIRM_TOGGLE_DISABLE_POLICY': 'REPLICATION.CONFIRM_TOGGLE_ENABLE_POLICY',
|
||||||
|
rule.name || '',
|
||||||
|
rule,
|
||||||
|
ConfirmationTargets.TOGGLE_CONFIRM
|
||||||
|
);
|
||||||
|
this.toggleConfirmDialog.open(toggleConfirmMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteRule(rule: ReplicationRule) {
|
||||||
|
let deletionMessage: ConfirmationMessage = new ConfirmationMessage(
|
||||||
|
'REPLICATION.DELETION_TITLE',
|
||||||
|
'REPLICATION.DELETION_SUMMARY',
|
||||||
|
rule.name || '',
|
||||||
|
rule.id,
|
||||||
|
ConfirmationTargets.POLICY,
|
||||||
|
ConfirmationButtons.DELETE_CANCEL);
|
||||||
|
this.deletionConfirmDialog.open(deletionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -54,6 +54,9 @@ describe('RecentLogComponent', () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(()=>{
|
||||||
fixture = TestBed.createComponent(RecentLogComponent);
|
fixture = TestBed.createComponent(RecentLogComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
serviceConfig = TestBed.get(SERVICE_CONFIG);
|
serviceConfig = TestBed.get(SERVICE_CONFIG);
|
||||||
@ -63,8 +66,7 @@ describe('RecentLogComponent', () => {
|
|||||||
.and.returnValue(Promise.resolve(mockData));
|
.and.returnValue(Promise.resolve(mockData));
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
@ -122,7 +124,7 @@ describe('RecentLogComponent', () => {
|
|||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(component.recentLogs.length).toEqual(2);
|
expect(component.recentLogs.length).toEqual(1);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
6
src/ui_ng/lib/src/replication/index.ts
Normal file
6
src/ui_ng/lib/src/replication/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Type } from '@angular/core';
|
||||||
|
import { ReplicationComponent } from './replication.component';
|
||||||
|
|
||||||
|
export const REPLICATION_DIRECTIVES: Type<any>[] = [
|
||||||
|
ReplicationComponent
|
||||||
|
];
|
18
src/ui_ng/lib/src/replication/replication.component.css.ts
Normal file
18
src/ui_ng/lib/src/replication/replication.component.css.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export const REPLICATION_STYLE: string = `
|
||||||
|
.option-left {
|
||||||
|
padding-left: 16px;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
.option-right {
|
||||||
|
padding-right: 16px;
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-left-down {
|
||||||
|
margin-top: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-right-down {
|
||||||
|
padding-right: 16px;
|
||||||
|
margin-top: 24px;
|
||||||
|
}`;
|
75
src/ui_ng/lib/src/replication/replication.component.html.ts
Normal file
75
src/ui_ng/lib/src/replication/replication.component.html.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
export const REPLICATION_TEMPLATE: string = `
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
|
<div class="row flex-items-xs-between">
|
||||||
|
<div class="flex-xs-middle option-left">
|
||||||
|
<button class="btn btn-link" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'REPLICATION.REPLICATION_RULE' | translate}}</button>
|
||||||
|
<create-edit-rule [projectId]="projectId" (reload)="reloadRules($event)"></create-edit-rule>
|
||||||
|
</div>
|
||||||
|
<div class="flex-xs-middle option-right">
|
||||||
|
<div class="select" style="float: left;">
|
||||||
|
<select (change)="doFilterRuleStatus($event)">
|
||||||
|
<option *ngFor="let r of ruleStatus" value="{{r.key}}">{{r.description | translate}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<hbr-filter filterPlaceholder='{{"REPLICATION.FILTER_POLICIES_PLACEHOLDER" | translate}}' (filter)="doSearchRules($event)" [currentValue]="search.ruleName"></hbr-filter>
|
||||||
|
<a href="javascript:void(0)" (click)="refreshRules()">
|
||||||
|
<clr-icon shape="refresh"></clr-icon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
|
<list-replication-rule [rules]="changedRules" [projectless]="false" [selectedId]="initSelectedId" (selectOne)="selectOneRule($event)" (editOne)="openEditRule($event)" (reload)="reloadRules($event)"></list-replication-rule>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
|
<div class="row flex-items-xs-between">
|
||||||
|
<h5 class="flex-items-xs-bottom option-left-down" style="margin-left: 14px;">{{'REPLICATION.REPLICATION_JOBS' | translate}}</h5>
|
||||||
|
<div class="flex-items-xs-bottom option-right-down">
|
||||||
|
<button class="btn btn-link" (click)="toggleSearchJobOptionalName(currentJobSearchOption)">{{toggleJobSearchOption[currentJobSearchOption] | translate}}</button>
|
||||||
|
<hbr-filter filterPlaceholder='{{"REPLICATION.FILTER_JOBS_PLACEHOLDER" | translate}}' (filter)="doSearchJobs($event)" [currentValue]="search.repoName" ></hbr-filter>
|
||||||
|
<a href="javascript:void(0)" (click)="refreshJobs()">
|
||||||
|
<clr-icon shape="refresh"></clr-icon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row flex-items-xs-right option-right" [hidden]="currentJobSearchOption === 0">
|
||||||
|
<div class="select" style="float: left;">
|
||||||
|
<select (change)="doFilterJobStatus($event)">
|
||||||
|
<option *ngFor="let j of jobStatus" value="{{j.key}}" [selected]="currentJobStatus.key === j.key">{{j.description | translate}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex-items-xs-middle">
|
||||||
|
<hbr-datetime [dateInput]="search.startTime" (search)="doJobSearchByStartTime($event)"></hbr-datetime>
|
||||||
|
<hbr-datetime [dateInput]="search.endTime" [oneDayOffset]="true" (search)="doJobSearchByEndTime($event)"></hbr-datetime>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
|
<clr-datagrid>
|
||||||
|
<clr-dg-column>{{'REPLICATION.NAME' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'REPLICATION.STATUS' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'REPLICATION.OPERATION' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'REPLICATION.CREATION_TIME' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'REPLICATION.END_TIME' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'REPLICATION.LOGS' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-row *clrDgItems="let j of jobs" [clrDgItem]='j'>
|
||||||
|
<clr-dg-cell>{{j.repository}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{j.status}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{j.operation}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{j.creation_time}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{j.update_time}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<a href="/api/jobs/replication/{{j.id}}/log" target="_BLANK">
|
||||||
|
<clr-icon shape="clipboard"></clr-icon>
|
||||||
|
</a>
|
||||||
|
</clr-dg-cell>
|
||||||
|
</clr-dg-row>
|
||||||
|
<clr-dg-footer>
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPLICATION.OF' | translate}}
|
||||||
|
{{pagination.totalItems}} {{'REPLICATION.ITEMS' | translate}}
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
300
src/ui_ng/lib/src/replication/replication.component.spec.ts
Normal file
300
src/ui_ng/lib/src/replication/replication.component.spec.ts
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
import { ComponentFixture, TestBed, async, inject } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
|
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
|
||||||
|
import { ReplicationComponent } from './replication.component';
|
||||||
|
import { ListReplicationRuleComponent } from '../list-replication-rule/list-replication-rule.component';
|
||||||
|
import { CreateEditRuleComponent } from '../create-edit-rule/create-edit-rule.component';
|
||||||
|
import { DatePickerComponent } from '../datetime-picker/datetime-picker.component';
|
||||||
|
import { DateValidatorDirective } from '../datetime-picker/date-validator.directive';
|
||||||
|
import { FilterComponent } from '../filter/filter.component';
|
||||||
|
import { InlineAlertComponent } from '../inline-alert/inline-alert.component';
|
||||||
|
import { ReplicationRule, ReplicationJob, Endpoint } from '../service/interface';
|
||||||
|
|
||||||
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
|
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||||
|
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
|
||||||
|
import { EndpointService, EndpointDefaultService } from '../service/endpoint.service';
|
||||||
|
|
||||||
|
describe('Replication Component (inline template)', ()=>{
|
||||||
|
|
||||||
|
let mockRules: ReplicationRule[] = [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"project_id": 1,
|
||||||
|
"project_name": "library",
|
||||||
|
"target_id": 1,
|
||||||
|
"target_name": "target_01",
|
||||||
|
"name": "sync_01",
|
||||||
|
"enabled": 0,
|
||||||
|
"description": "",
|
||||||
|
"cron_str": "",
|
||||||
|
"error_job_count": 2,
|
||||||
|
"deleted": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"project_id": 1,
|
||||||
|
"project_name": "library",
|
||||||
|
"target_id": 3,
|
||||||
|
"target_name": "target_02",
|
||||||
|
"name": "sync_02",
|
||||||
|
"enabled": 1,
|
||||||
|
"description": "",
|
||||||
|
"cron_str": "",
|
||||||
|
"error_job_count": 1,
|
||||||
|
"deleted": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"project_id": 1,
|
||||||
|
"project_name": "library",
|
||||||
|
"target_id": 2,
|
||||||
|
"target_name": "target_03",
|
||||||
|
"name": "sync_03",
|
||||||
|
"enabled": 0,
|
||||||
|
"description": "",
|
||||||
|
"cron_str": "",
|
||||||
|
"error_job_count": 0,
|
||||||
|
"deleted": 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let mockJobs: ReplicationJob[] = [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"status": "error",
|
||||||
|
"repository": "library/nginx",
|
||||||
|
"policy_id": 1,
|
||||||
|
"operation": "transfer",
|
||||||
|
"update_time": new Date("2017-05-23 12:20:33"),
|
||||||
|
"tags": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"status": "finished",
|
||||||
|
"repository": "library/mysql",
|
||||||
|
"policy_id": 1,
|
||||||
|
"operation": "transfer",
|
||||||
|
"update_time": new Date("2017-05-27 12:20:33"),
|
||||||
|
"tags": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"status": "stopped",
|
||||||
|
"repository": "library/busybox",
|
||||||
|
"policy_id": 2,
|
||||||
|
"operation": "transfer",
|
||||||
|
"update_time": new Date("2017-04-23 12:20:33"),
|
||||||
|
"tags": null
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let mockEndpoints: Endpoint[] = [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"endpoint": "https://10.117.4.151",
|
||||||
|
"name": "target_01",
|
||||||
|
"username": "admin",
|
||||||
|
"password": "",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"endpoint": "https://10.117.5.142",
|
||||||
|
"name": "target_02",
|
||||||
|
"username": "AAA",
|
||||||
|
"password": "",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"endpoint": "https://101.1.11.111",
|
||||||
|
"name": "target_03",
|
||||||
|
"username": "admin",
|
||||||
|
"password": "",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"endpoint": "http://4.4.4.4",
|
||||||
|
"name": "target_04",
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"type": 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let mockRule: ReplicationRule = {
|
||||||
|
"id": 1,
|
||||||
|
"project_id": 1,
|
||||||
|
"project_name": "library",
|
||||||
|
"target_id": 1,
|
||||||
|
"target_name": "target_01",
|
||||||
|
"name": "sync_01",
|
||||||
|
"enabled": 0,
|
||||||
|
"description": "",
|
||||||
|
"cron_str": "",
|
||||||
|
"error_job_count": 2,
|
||||||
|
"deleted": 0
|
||||||
|
};
|
||||||
|
|
||||||
|
let fixture: ComponentFixture<ReplicationComponent>;
|
||||||
|
let comp: ReplicationComponent;
|
||||||
|
|
||||||
|
let replicationService: ReplicationService;
|
||||||
|
|
||||||
|
let spyRules: jasmine.Spy;
|
||||||
|
let spyJobs: jasmine.Spy;
|
||||||
|
|
||||||
|
let deGrids: DebugElement[];
|
||||||
|
let deRules: DebugElement;
|
||||||
|
let deJobs: DebugElement;
|
||||||
|
|
||||||
|
let elRule: HTMLElement;
|
||||||
|
let elJob: HTMLElement;
|
||||||
|
|
||||||
|
let config: IServiceConfig = {
|
||||||
|
replicationRuleEndpoint: '/api/policies/replication/testing',
|
||||||
|
replicationJobEndpoint: '/api/jobs/replication/testing'
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async(()=>{
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
SharedModule,
|
||||||
|
NoopAnimationsModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ReplicationComponent,
|
||||||
|
ListReplicationRuleComponent,
|
||||||
|
CreateEditRuleComponent,
|
||||||
|
ConfirmationDialogComponent,
|
||||||
|
DatePickerComponent,
|
||||||
|
FilterComponent,
|
||||||
|
InlineAlertComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
ErrorHandler,
|
||||||
|
{ provide: SERVICE_CONFIG, useValue: config },
|
||||||
|
{ provide: ReplicationService, useClass: ReplicationDefaultService },
|
||||||
|
{ provide: EndpointService, useClass: EndpointDefaultService }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(()=>{
|
||||||
|
fixture = TestBed.createComponent(ReplicationComponent);
|
||||||
|
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
comp.projectId = 1;
|
||||||
|
comp.search.ruleId = 1;
|
||||||
|
|
||||||
|
replicationService = fixture.debugElement.injector.get(ReplicationService);
|
||||||
|
|
||||||
|
spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(Promise.resolve(mockRules));
|
||||||
|
spyJobs = spyOn(replicationService, 'getJobs').and.returnValues(Promise.resolve(mockJobs));
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
deGrids = fixture.debugElement.queryAll(del=>del.classes['datagrid']);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(deGrids).toBeTruthy();
|
||||||
|
expect(deGrids.length).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should load replication rules', async(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
deRules = deGrids[0].query(By.css('datagrid-cell'));
|
||||||
|
expect(deRules).toBeTruthy();
|
||||||
|
fixture.detectChanges();
|
||||||
|
elRule = deRules.nativeElement;
|
||||||
|
expect(elRule).toBeTruthy();
|
||||||
|
expect(elRule.textContent).toEqual('sync_01');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('Should load replication jobs', async(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
deJobs = deGrids[1].query(By.css('datagrid-cell'));
|
||||||
|
expect(deJobs).toBeTruthy();
|
||||||
|
fixture.detectChanges();
|
||||||
|
elJob = deJobs.nativeElement;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(elJob).toBeTruthy();
|
||||||
|
expect(elJob.textContent).toEqual('library/nginx');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('Should filter replication rules by keywords', async(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
comp.doSearchRules('sync_01');
|
||||||
|
fixture.detectChanges();
|
||||||
|
let el: HTMLElement = deRules.nativeElement;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(el.textContent.trim()).toEqual('sync_01');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('Should filter replication rules by status', async(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
comp.doFilterRuleStatus('1' /*Enabled*/);
|
||||||
|
fixture.detectChanges();
|
||||||
|
let el: HTMLElement = deRules.nativeElement;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
expect(el.textContent.trim()).toEqual('sync_02');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('Should filter replication jobs by keywords', async(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
comp.doSearchJobs('nginx');
|
||||||
|
fixture.detectChanges();
|
||||||
|
let el: HTMLElement = deJobs.nativeElement;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
expect(el.textContent.trim()).toEqual('library/nginx');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('Should filter replication jobs by status', async(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
comp.doFilterJobStatus('finished');
|
||||||
|
let el: HTMLElement = deJobs.nativeElement;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
expect(el.textContent.trim()).toEqual('library/mysql');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('Should filter replication jobs by date range', async(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(()=>{
|
||||||
|
fixture.detectChanges();
|
||||||
|
comp.doJobSearchByStartTime('2017-05-01');
|
||||||
|
comp.doJobSearchByEndTime('2015-05-25');
|
||||||
|
let el: HTMLElement = deJobs.nativeElement;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(el).toBeTruthy();
|
||||||
|
expect(el.textContent.trim()).toEqual('library/nginx');
|
||||||
|
});
|
||||||
|
}))
|
||||||
|
});
|
232
src/ui_ng/lib/src/replication/replication.component.ts
Normal file
232
src/ui_ng/lib/src/replication/replication.component.ts
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// 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, Input } from '@angular/core';
|
||||||
|
import { ResponseOptions, RequestOptions } from '@angular/http';
|
||||||
|
import { NgModel } from '@angular/forms';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { CreateEditRuleComponent } from '../create-edit-rule/create-edit-rule.component';
|
||||||
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
|
|
||||||
|
import { ReplicationService } from '../service/replication.service';
|
||||||
|
import { RequestQueryParams } from '../service/RequestQueryParams';
|
||||||
|
import { ReplicationRule, ReplicationJob, Endpoint } from '../service/interface';
|
||||||
|
|
||||||
|
import { toPromise } from '../utils';
|
||||||
|
|
||||||
|
import { REPLICATION_TEMPLATE } from './replication.component.html';
|
||||||
|
import { REPLICATION_STYLE } from './replication.component.css';
|
||||||
|
|
||||||
|
const ruleStatus: {[key: string]: any} = [
|
||||||
|
{ 'key': 'all', 'description': 'REPLICATION.ALL_STATUS'},
|
||||||
|
{ 'key': '1', 'description': 'REPLICATION.ENABLED'},
|
||||||
|
{ 'key': '0', 'description': 'REPLICATION.DISABLED'}
|
||||||
|
];
|
||||||
|
|
||||||
|
const jobStatus: {[key: string]: any} = [
|
||||||
|
{ 'key': 'all', 'description': 'REPLICATION.ALL' },
|
||||||
|
{ 'key': 'pending', 'description': 'REPLICATION.PENDING' },
|
||||||
|
{ 'key': 'running', 'description': 'REPLICATION.RUNNING' },
|
||||||
|
{ 'key': 'error', 'description': 'REPLICATION.ERROR' },
|
||||||
|
{ 'key': 'retrying', 'description': 'REPLICATION.RETRYING' },
|
||||||
|
{ 'key': 'stopped' , 'description': 'REPLICATION.STOPPED' },
|
||||||
|
{ 'key': 'finished', 'description': 'REPLICATION.FINISHED' },
|
||||||
|
{ 'key': 'canceled', 'description': 'REPLICATION.CANCELED' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const optionalSearch: {} = {0: 'REPLICATION.ADVANCED', 1: 'REPLICATION.SIMPLE'};
|
||||||
|
|
||||||
|
export class SearchOption {
|
||||||
|
ruleId: number | string;
|
||||||
|
ruleName: string = '';
|
||||||
|
repoName: string = '';
|
||||||
|
status: string = '';
|
||||||
|
startTime: string = '';
|
||||||
|
startTimestamp: string = '';
|
||||||
|
endTime: string = '';
|
||||||
|
endTimestamp: string = '';
|
||||||
|
page: number = 1;
|
||||||
|
pageSize: number = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'hbr-replication',
|
||||||
|
template: REPLICATION_TEMPLATE,
|
||||||
|
styles: [ REPLICATION_STYLE ]
|
||||||
|
})
|
||||||
|
export class ReplicationComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() projectId: number | string;
|
||||||
|
|
||||||
|
search: SearchOption = new SearchOption();
|
||||||
|
|
||||||
|
ruleStatus = ruleStatus;
|
||||||
|
currentRuleStatus: {key: string, description: string};
|
||||||
|
|
||||||
|
jobStatus = jobStatus;
|
||||||
|
currentJobStatus: {key: string, description: string};
|
||||||
|
|
||||||
|
changedRules: ReplicationRule[];
|
||||||
|
initSelectedId: number | string;
|
||||||
|
|
||||||
|
rules: ReplicationRule[];
|
||||||
|
jobs: ReplicationJob[];
|
||||||
|
|
||||||
|
jobsTotalRecordCount: number;
|
||||||
|
jobsTotalPage: number;
|
||||||
|
|
||||||
|
toggleJobSearchOption = optionalSearch;
|
||||||
|
currentJobSearchOption: number;
|
||||||
|
|
||||||
|
@ViewChild(CreateEditRuleComponent)
|
||||||
|
createEditPolicyComponent: CreateEditRuleComponent;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
|
private replicationService: ReplicationService,
|
||||||
|
private translateService: TranslateService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if(!this.projectId) {
|
||||||
|
this.errorHandler.warning('Project ID is unset.');
|
||||||
|
}
|
||||||
|
this.currentRuleStatus = this.ruleStatus[0];
|
||||||
|
this.currentJobStatus = this.jobStatus[0];
|
||||||
|
this.currentJobSearchOption = 0;
|
||||||
|
this.retrieveRules();
|
||||||
|
}
|
||||||
|
|
||||||
|
retrieveRules(): void {
|
||||||
|
toPromise<ReplicationRule[]>(this.replicationService
|
||||||
|
.getReplicationRules(this.projectId, this.search.ruleName))
|
||||||
|
.then(response=>{
|
||||||
|
this.changedRules = response || [];
|
||||||
|
if(this.changedRules && this.changedRules.length > 0) {
|
||||||
|
this.initSelectedId = this.changedRules[0].id || '';
|
||||||
|
}
|
||||||
|
this.rules = this.changedRules;
|
||||||
|
if(this.changedRules && this.changedRules.length > 0) {
|
||||||
|
this.search.ruleId = this.changedRules[0].id || '';
|
||||||
|
this.fetchReplicationJobs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).catch(error=>this.errorHandler.error(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
openModal(): void {
|
||||||
|
this.createEditPolicyComponent.openCreateEditRule(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
openEditRule(rule: ReplicationRule) {
|
||||||
|
if(rule) {
|
||||||
|
let editable = true;
|
||||||
|
if(rule.enabled === 1) {
|
||||||
|
editable = false;
|
||||||
|
}
|
||||||
|
this.createEditPolicyComponent.openCreateEditRule(editable, rule.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchReplicationJobs() {
|
||||||
|
|
||||||
|
let params: RequestQueryParams = new RequestQueryParams();
|
||||||
|
params.set('status', this.search.status);
|
||||||
|
params.set('repository', this.search.repoName);
|
||||||
|
params.set('start_time', this.search.startTimestamp);
|
||||||
|
params.set('end_time', this.search.endTimestamp);
|
||||||
|
|
||||||
|
toPromise<ReplicationJob[]>(this.replicationService
|
||||||
|
.getJobs(this.search.ruleId, params))
|
||||||
|
.then(
|
||||||
|
response=>{
|
||||||
|
this.jobs = response;
|
||||||
|
}).catch(error=>this.errorHandler.error(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
selectOneRule(rule: ReplicationRule) {
|
||||||
|
if (rule) {
|
||||||
|
this.search.ruleId = rule.id || '';
|
||||||
|
this.search.repoName = '';
|
||||||
|
this.search.status = '';
|
||||||
|
this.currentJobSearchOption = 0;
|
||||||
|
this.currentJobStatus = { 'key': 'all', 'description': 'REPLICATION.ALL' };
|
||||||
|
this.fetchReplicationJobs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doSearchRules(ruleName: string) {
|
||||||
|
this.search.ruleName = ruleName;
|
||||||
|
this.retrieveRules();
|
||||||
|
}
|
||||||
|
|
||||||
|
doFilterRuleStatus($event: any) {
|
||||||
|
if ($event && $event.target && $event.target["value"]) {
|
||||||
|
let status = $event.target["value"];
|
||||||
|
this.currentRuleStatus = this.ruleStatus.find((r: any)=>r.key === status);
|
||||||
|
if(this.currentRuleStatus.key === 'all') {
|
||||||
|
this.changedRules = this.rules;
|
||||||
|
} else {
|
||||||
|
this.changedRules = this.rules.filter(policy=>policy.enabled === +this.currentRuleStatus.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doFilterJobStatus($event: any) {
|
||||||
|
if ($event && $event.target && $event.target["value"]) {
|
||||||
|
let status = $event.target["value"];
|
||||||
|
this.currentJobStatus = this.jobStatus.find((r: any)=>r.key === status);
|
||||||
|
if(this.currentJobStatus.key === 'all') {
|
||||||
|
status = '';
|
||||||
|
}
|
||||||
|
this.search.status = status;
|
||||||
|
this.doSearchJobs(this.search.repoName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doSearchJobs(repoName: string) {
|
||||||
|
this.search.repoName = repoName;
|
||||||
|
this.fetchReplicationJobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadRules(isReady: boolean) {
|
||||||
|
if(isReady) {
|
||||||
|
this.search.ruleName = '';
|
||||||
|
this.retrieveRules();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshRules() {
|
||||||
|
this.retrieveRules();
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshJobs() {
|
||||||
|
this.fetchReplicationJobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleSearchJobOptionalName(option: number) {
|
||||||
|
(option === 1) ? this.currentJobSearchOption = 0 : this.currentJobSearchOption = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
doJobSearchByStartTime(fromTimestamp: string) {
|
||||||
|
this.search.startTimestamp = fromTimestamp;
|
||||||
|
this.fetchReplicationJobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
doJobSearchByEndTime(toTimestamp: string) {
|
||||||
|
this.search.endTimestamp = toTimestamp;
|
||||||
|
this.fetchReplicationJobs();
|
||||||
|
}
|
||||||
|
}
|
@ -75,8 +75,8 @@ export interface Tag extends Base {
|
|||||||
export interface Endpoint extends Base {
|
export interface Endpoint extends Base {
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
name: string;
|
name: string;
|
||||||
username: string;
|
username?: string;
|
||||||
password: string;
|
password?: string;
|
||||||
type: number;
|
type: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,9 +87,9 @@ export interface Endpoint extends Base {
|
|||||||
* @interface ReplicationRule
|
* @interface ReplicationRule
|
||||||
*/
|
*/
|
||||||
export interface ReplicationRule extends Base {
|
export interface ReplicationRule extends Base {
|
||||||
project_id: number;
|
project_id: number | string;
|
||||||
project_name: string;
|
project_name: string;
|
||||||
target_id: number;
|
target_id: number | string;
|
||||||
target_name: string;
|
target_name: string;
|
||||||
enabled: number;
|
enabled: number;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
@ -84,7 +84,7 @@ export abstract class ReplicationService {
|
|||||||
*
|
*
|
||||||
* @memberOf ReplicationService
|
* @memberOf ReplicationService
|
||||||
*/
|
*/
|
||||||
abstract enableReplicationRule(ruleId: number | string): Observable<any> | Promise<any> | any;
|
abstract enableReplicationRule(ruleId: number | string, enablement: number): Observable<any> | Promise<any> | any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable the specified replication rule.
|
* Disable the specified replication rule.
|
||||||
@ -130,13 +130,13 @@ export class ReplicationDefaultService extends ReplicationService {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private http: Http,
|
private http: Http,
|
||||||
@Inject(SERVICE_CONFIG) private config: IServiceConfig
|
@Inject(SERVICE_CONFIG) config: IServiceConfig
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._ruleBaseUrl = this.config.replicationRuleEndpoint ?
|
this._ruleBaseUrl = config.replicationRuleEndpoint ?
|
||||||
this.config.replicationRuleEndpoint : '/api/policies/replication';
|
config.replicationRuleEndpoint : '/api/policies/replication';
|
||||||
this._jobBaseUrl = this.config.replicationJobEndpoint ?
|
this._jobBaseUrl = config.replicationJobEndpoint ?
|
||||||
this.config.replicationJobEndpoint : '/api/jobs/replication';
|
config.replicationJobEndpoint : '/api/jobs/replication';
|
||||||
}
|
}
|
||||||
|
|
||||||
//Private methods
|
//Private methods
|
||||||
@ -169,7 +169,7 @@ export class ReplicationDefaultService extends ReplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let url: string = `${this._ruleBaseUrl}/${ruleId}`;
|
let url: string = `${this._ruleBaseUrl}/${ruleId}`;
|
||||||
return this.http.get(url, HTTP_JSON_OPTIONS).toPromise()
|
return this.http.get(url).toPromise()
|
||||||
.then(response => response.json() as ReplicationRule)
|
.then(response => response.json() as ReplicationRule)
|
||||||
.catch(error => Promise.reject(error));
|
.catch(error => Promise.reject(error));
|
||||||
}
|
}
|
||||||
@ -206,13 +206,13 @@ export class ReplicationDefaultService extends ReplicationService {
|
|||||||
.catch(error => Promise.reject(error));
|
.catch(error => Promise.reject(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
public enableReplicationRule(ruleId: number | string): Observable<any> | Promise<any> | any {
|
public enableReplicationRule(ruleId: number | string, enablement: number): Observable<any> | Promise<any> | any {
|
||||||
if (!ruleId || ruleId <= 0) {
|
if (!ruleId || ruleId <= 0) {
|
||||||
return Promise.reject('Bad argument');
|
return Promise.reject('Bad argument');
|
||||||
}
|
}
|
||||||
|
|
||||||
let url: string = `${this._ruleBaseUrl}/${ruleId}/enablement`;
|
let url: string = `${this._ruleBaseUrl}/${ruleId}/enablement`;
|
||||||
return this.http.put(url, { enabled: 1 }, HTTP_JSON_OPTIONS).toPromise()
|
return this.http.put(url, { enabled: enablement }, HTTP_JSON_OPTIONS).toPromise()
|
||||||
.then(response => response)
|
.then(response => response)
|
||||||
.catch(error => Promise.reject(error));
|
.catch(error => Promise.reject(error));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user