add trigger logic and remove project code

Signed-off-by: Meina Zhou <meinaz@vmware.com>
This commit is contained in:
Meina Zhou 2019-03-25 10:20:45 +08:00
parent b0ce3da8bd
commit 2df11a773e
14 changed files with 135 additions and 82 deletions

View File

@ -34,13 +34,16 @@ describe("CreateEditEndpointComponent (inline template)", () => {
url: "https://10.117.4.151" url: "https://10.117.4.151"
}; };
let mockAdapter: [Adapter] = [{ let mockAdapters: Adapter[] = [{
type: "Harbor", type: "Harbor",
description: "test", description: "test",
supported_resource_types: [ supported_resource_filters: [
"repository" {
"type": "Name",
"style": "input"
}
], ],
supported_resource_filters: null supported_triggers: []
}]; }];
let comp: CreateEditEndpointComponent; let comp: CreateEditEndpointComponent;
@ -77,7 +80,7 @@ describe("CreateEditEndpointComponent (inline template)", () => {
endpointService = fixture.debugElement.injector.get(EndpointService); endpointService = fixture.debugElement.injector.get(EndpointService);
spyAdapter = spyOn(endpointService, "getAdapters").and.returnValue( spyAdapter = spyOn(endpointService, "getAdapters").and.returnValue(
of(mockAdapter) of(mockAdapters)
); );
spy = spyOn(endpointService, "getEndpoint").and.returnValue( spy = spyOn(endpointService, "getEndpoint").and.returnValue(

View File

@ -31,7 +31,7 @@
<div class="form-group form-group-override"> <div class="form-group form-group-override">
<label class="form-group-label-override">{{'REPLICATION.REPLI_MODE' | translate}}</label> <label class="form-group-label-override">{{'REPLICATION.REPLI_MODE' | translate}}</label>
<div class="radio" style="display:inherit;"> <div class="radio" style="display:inherit;">
<input type="radio" id="push_base" name="replicationMode" [value]=true [(ngModel)]="isPushMode" [ngModelOptions]="{standalone: true}"> <input type="radio" id="push_base" name="replicationMode" [value]=true [(ngModel)]="isPushMode" (change)="modeChange()" [ngModelOptions]="{standalone: true}">
<label for="push_base">Push-based</label> <label for="push_base">Push-based</label>
<input type="radio" id="pull_base" name="replicationMode" [value]=false [(ngModel)]="isPushMode" [ngModelOptions]="{standalone: true}"> <input type="radio" id="pull_base" name="replicationMode" [value]=false [(ngModel)]="isPushMode" [ngModelOptions]="{standalone: true}">
<label for="pull_base">Pull-based</label> <label for="pull_base">Pull-based</label>
@ -126,15 +126,13 @@
<div formGroupName="trigger"> <div formGroupName="trigger">
<!--on trigger--> <!--on trigger-->
<div class="select floatSetPar"> <div class="select floatSetPar">
<select id="ruleTrigger" formControlName="checkRuleName" (change)="selectTrigger($event)"> <select id="ruleTrigger" formControlName="kind" (change)="selectTrigger($event)">
<option value="Manual">{{'REPLICATION.MANUAL' | translate}}</option> <option *ngFor="let trigger of supportedTriggers" [value]="trigger">{{trigger }}</option>
<option value="Immediate">{{'REPLICATION.IMMEDIATE' | translate}}</option>
<option value="Scheduled">{{'REPLICATION.SCHEDULE' | translate}}</option>
</select> </select>
</div> </div>
<!--on push--> <!--on push-->
<div formGroupName="schedule_param"> <div formGroupName="schedule_param">
<div [hidden]="!isScheduleOpt"> <div [hidden]="isNotSchedule()">
<label>Cron String</label> <label>Cron String</label>
<label for="targetCron" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right"> <label for="targetCron" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right">
<input type="text" name=targetCron id="targetCron" required class="form-control cron-input" formControlName="cron"> <input type="text" name=targetCron id="targetCron" required class="form-control cron-input" formControlName="cron">
@ -145,7 +143,7 @@
</div> </div>
</div> </div>
</div> </div>
<div [hidden]="!isImmediate" class="clr-form-control rule-width"> <div [hidden]="isNotEventBased()" class="clr-form-control rule-width">
<clr-checkbox-wrapper> <clr-checkbox-wrapper>
<input type="checkbox" clrCheckbox [checked]="false" id="ruleDeletion" formControlName="deletion" class="clr-checkbox"> <input type="checkbox" clrCheckbox [checked]="false" id="ruleDeletion" formControlName="deletion" class="clr-checkbox">
<label for="ruleDeletion" class="clr-control-label">{{'REPLICATION.DELETE_REMOTE_IMAGES' | translate}}</label> <label for="ruleDeletion" class="clr-control-label">{{'REPLICATION.DELETE_REMOTE_IMAGES' | translate}}</label>

View File

@ -17,7 +17,8 @@ import {
ReplicationRule, ReplicationRule,
ReplicationJob, ReplicationJob,
Endpoint, Endpoint,
ReplicationJobItem ReplicationJobItem,
Adapter
} from "../service/interface"; } from "../service/interface";
import { ErrorHandler } from "../error-handler/error-handler"; import { ErrorHandler } from "../error-handler/error-handler";
@ -160,6 +161,38 @@ describe("CreateEditRuleComponent (inline template)", () => {
deletion: false deletion: false
}; };
let mockAdapter: Adapter = {
"type": "harbor",
"description": "",
"supported_resource_filters": [
{
"type": "Name",
"style": "input"
},
{
"type": "Version",
"style": "input"
},
{
"type": "Label",
"style": "input"
},
{
"type": "Resource",
"style": "radio",
"values": [
"repository",
"chart"
]
}
],
"supported_triggers": [
"Manual",
"Scheduled",
"EventBased"
]
};
let fixture: ComponentFixture<ReplicationComponent>; let fixture: ComponentFixture<ReplicationComponent>;
let fixtureCreate: ComponentFixture<CreateEditRuleComponent>; let fixtureCreate: ComponentFixture<CreateEditRuleComponent>;
@ -173,10 +206,11 @@ describe("CreateEditRuleComponent (inline template)", () => {
let spyOneRule: jasmine.Spy; let spyOneRule: jasmine.Spy;
let spyJobs: jasmine.Spy; let spyJobs: jasmine.Spy;
let spyAdapter: jasmine.Spy;
let spyEndpoint: jasmine.Spy; let spyEndpoint: jasmine.Spy;
let config: IServiceConfig = { let config: IServiceConfig = {
replicationBaseEndpoint: "/api/replication/executions/testing", replicationBaseEndpoint: "/api/replication/testing",
targetBaseEndpoint: "/api/registries/testing" targetBaseEndpoint: "/api/registries/testing"
}; };
@ -230,6 +264,8 @@ describe("CreateEditRuleComponent (inline template)", () => {
spyJobs = spyOn(replicationService, "getExecutions").and.returnValues( spyJobs = spyOn(replicationService, "getExecutions").and.returnValues(
of(mockJob)); of(mockJob));
spyAdapter = spyOn(replicationService, "getReplicationAdapter").and.returnValues(
of(mockAdapter));
spyEndpoint = spyOn(endpointService, "getEndpoints").and.returnValues( spyEndpoint = spyOn(endpointService, "getEndpoints").and.returnValues(
of(mockEndpoints) of(mockEndpoints)
); );
@ -239,6 +275,7 @@ describe("CreateEditRuleComponent (inline template)", () => {
it("Should open creation modal and load endpoints", async(() => { it("Should open creation modal and load endpoints", async(() => {
fixture.detectChanges(); fixture.detectChanges();
compCreate.initAdapter("harbor");
compCreate.openCreateEditRule(); compCreate.openCreateEditRule();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
@ -254,6 +291,7 @@ describe("CreateEditRuleComponent (inline template)", () => {
it("Should open modal to edit replication rule", async(() => { it("Should open modal to edit replication rule", async(() => {
fixture.detectChanges(); fixture.detectChanges();
compCreate.initAdapter("harbor");
compCreate.openCreateEditRule(mockRule.id); compCreate.openCreateEditRule(mockRule.id);
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();

View File

@ -21,7 +21,7 @@ import {
EventEmitter, EventEmitter,
Output Output
} from "@angular/core"; } from "@angular/core";
import { Filter, ReplicationRule, Endpoint, Label } from "../service/interface"; import { Filter, ReplicationRule, Endpoint, Label, Adapter } from "../service/interface";
import { Subject, Subscription } from "rxjs"; import { Subject, Subscription } from "rxjs";
import { debounceTime, distinctUntilChanged } from "rxjs/operators"; import { debounceTime, distinctUntilChanged } from "rxjs/operators";
import { FormArray, FormBuilder, FormGroup, Validators, FormControl } from "@angular/forms"; import { FormArray, FormBuilder, FormGroup, Validators, FormControl } from "@angular/forms";
@ -51,8 +51,6 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
selectedProjectList: Project[] = []; selectedProjectList: Project[] = [];
isFilterHide = false; isFilterHide = false;
weeklySchedule: boolean; weeklySchedule: boolean;
isScheduleOpt: boolean;
isImmediate = false;
noProjectInfo = ""; noProjectInfo = "";
noEndpointInfo = ""; noEndpointInfo = "";
isPushMode = true; isPushMode = true;
@ -60,7 +58,11 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
noSelectedEndpoint = true; noSelectedEndpoint = true;
filterCount = 0; filterCount = 0;
alertClosed = false; alertClosed = false;
triggerNames: string[] = ["Manual", "Immediate", "Scheduled"]; TRIGGER_TYPES = {
MANUAL: "Manual",
SCHEDULED: "Scheduled",
EVENT_BASED: "EventBased"
};
filterSelect: string[] = ["type", "repository", "tag", "label"]; filterSelect: string[] = ["type", "repository", "tag", "label"];
ruleNameTooltip = "REPLICATION.NAME_TOOLTIP"; ruleNameTooltip = "REPLICATION.NAME_TOOLTIP";
headerTitle = "REPLICATION.ADD_POLICY"; headerTitle = "REPLICATION.ADD_POLICY";
@ -82,6 +84,8 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
formArrayLabel: FormArray; formArrayLabel: FormArray;
copyUpdateForm: ReplicationRule; copyUpdateForm: ReplicationRule;
cronString: string; cronString: string;
supportedTriggers: string[];
supportedFilters: Filter[];
@Input() projectId: number; @Input() projectId: number;
@Input() projectName: string; @Input() projectName: string;
@ -113,6 +117,13 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
}; };
} }
initAdapter(type: string): void {
this.repService.getReplicationAdapter(type).subscribe(adapter => {
this.supportedFilters = adapter.supported_resource_filters;
this.supportedTriggers = adapter.supported_triggers;
});
}
ngOnInit(): void { ngOnInit(): void {
this.endpointService.getEndpoints().subscribe(endPoints => { this.endpointService.getEndpoints().subscribe(endPoints => {
this.targetList = endPoints || []; this.targetList = endPoints || [];
@ -120,8 +131,7 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
}, error => { }, error => {
this.errorHandler.error(error); this.errorHandler.error(error);
}); });
this.initAdapter("harbor");
this.nameChecker this.nameChecker
.pipe(debounceTime(300)) .pipe(debounceTime(300))
.pipe(distinctUntilChanged()) .pipe(distinctUntilChanged())
@ -148,18 +158,21 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
}); });
} }
modeChange(): void {
if (this.isPushMode) {
this.initAdapter("harbor");
}
}
sourceChange($event): void { sourceChange($event): void {
if ($event && $event.target) { this.noSelectedEndpoint = false;
if ($event.target["value"] === "-1") { let selectId = this.ruleForm.get('src_registry_id').value;
this.noSelectedEndpoint = true; let selecedTarget: Endpoint = this.sourceList.find(
return; source => source.id === selectId
} );
let selecedTarget: Endpoint = this.sourceList.find(
source => source.id === +$event.target["value"] this.initAdapter(selecedTarget.type);
);
this.noSelectedEndpoint = false;
}
} }
@ -191,7 +204,7 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
dest_registry_id: new FormControl(), dest_registry_id: new FormControl(),
dest_namespace: "", dest_namespace: "",
trigger: this.fb.group({ trigger: this.fb.group({
kind: this.triggerNames[0], kind: '',
schedule_param: this.fb.group({ schedule_param: this.fb.group({
cron: "" cron: ""
}) })
@ -201,12 +214,24 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
}); });
} }
selectTrigger($event: any): void {
}
isNotSchedule(): boolean {
return this.ruleForm.get("trigger").get("kind").value !== this.TRIGGER_TYPES.SCHEDULED;
}
isNotEventBased(): boolean {
return this.ruleForm.get("trigger").get("kind").value !== this.TRIGGER_TYPES.EVENT_BASED;
}
initForm(): void { initForm(): void {
this.ruleForm.reset({ this.ruleForm.reset({
name: "", name: "",
description: "", description: "",
trigger: { trigger: {
kind: this.triggerNames[0], kind: this.supportedTriggers[0],
schedule_param: { schedule_param: {
cron: "" cron: ""
} }
@ -214,7 +239,6 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
deletion: false deletion: false
}); });
this.setFilter([]); this.setFilter([]);
this.copyUpdateForm = clone(this.ruleForm.value); this.copyUpdateForm = clone(this.ruleForm.value);
} }
@ -439,26 +463,9 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
} }
} }
selectTrigger($event: any): void {
if ($event && $event.target && $event.target["value"]) {
let val: string = $event.target["value"];
if (val === this.triggerNames[2]) {
this.isScheduleOpt = true;
this.isImmediate = false;
}
if (val === this.triggerNames[1]) {
this.isScheduleOpt = false;
this.isImmediate = true;
}
if (val === this.triggerNames[0]) {
this.isScheduleOpt = false;
this.isImmediate = false;
}
}
}
// Replication Schedule select value exchange // Replication Schedule select value exchange
selectSchedule($event: any): void { selectSchedule($event: any): void {
} }
checkRuleName(): void { checkRuleName(): void {
@ -532,7 +539,6 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
// add new Replication rule // add new Replication rule
this.inProgress = true; this.inProgress = true;
let copyRuleForm: ReplicationRule = this.ruleForm.value; let copyRuleForm: ReplicationRule = this.ruleForm.value;
copyRuleForm.trigger = null;
if (this.isPushMode) { if (this.isPushMode) {
copyRuleForm.src_registry_id = null; copyRuleForm.src_registry_id = null;
} else { } else {
@ -586,8 +592,6 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
this.deletedLabelCount = 0; this.deletedLabelCount = 0;
this.weeklySchedule = false; this.weeklySchedule = false;
this.isScheduleOpt = false;
this.isImmediate = false;
this.policyId = -1; this.policyId = -1;
this.createEditRuleOpened = true; this.createEditRuleOpened = true;
this.filterLabelInfo = []; this.filterLabelInfo = [];

View File

@ -93,13 +93,16 @@ describe("EndpointComponent (inline template)", () => {
} }
]; ];
let mockAdapter: [Adapter] = [{ let mockAdapters: Adapter[] = [{
type: "Harbor", type: "Harbor",
description: "test", description: "test",
supported_resource_types: [ supported_resource_filters: [
"repository" {
"type": "Name",
"style": "input"
}
], ],
supported_resource_filters: null supported_triggers: []
}]; }];
let comp: EndpointComponent; let comp: EndpointComponent;
@ -143,7 +146,7 @@ describe("EndpointComponent (inline template)", () => {
); );
spyAdapter = spyOn(endpointService, "getAdapters").and.returnValue( spyAdapter = spyOn(endpointService, "getAdapters").and.returnValue(
of(mockAdapter) of(mockAdapters)
); );
spyOnRules = spyOn( spyOnRules = spyOn(

View File

@ -81,9 +81,8 @@ export const DefaultServiceConfig: IServiceConfig = {
repositoryBaseEndpoint: "/api/repositories", repositoryBaseEndpoint: "/api/repositories",
logBaseEndpoint: "/api/logs", logBaseEndpoint: "/api/logs",
targetBaseEndpoint: "/api/registries", targetBaseEndpoint: "/api/registries",
replicationBaseEndpoint: "/api/replication/executions", replicationBaseEndpoint: "/api/replication",
replicationRuleEndpoint: "/api/replication/policies", replicationRuleEndpoint: "/api/replication/policies",
adapterEndpoint: "api/replication/adapters",
vulnerabilityScanningBaseEndpoint: "/api/repositories", vulnerabilityScanningBaseEndpoint: "/api/repositories",
projectPolicyEndpoint: "/api/projects/configs", projectPolicyEndpoint: "/api/projects/configs",
projectBaseEndpoint: "/api/projects", projectBaseEndpoint: "/api/projects",

View File

@ -138,7 +138,7 @@ describe('Replication Component (inline template)', () => {
let config: IServiceConfig = { let config: IServiceConfig = {
replicationRuleEndpoint: '/api/policies/replication/testing', replicationRuleEndpoint: '/api/policies/replication/testing',
replicationBaseEndpoint: '/api/replication/executions/testing' replicationBaseEndpoint: '/api/replication/testing'
}; };
beforeEach(async(() => { beforeEach(async(() => {

View File

@ -54,11 +54,6 @@ export interface IServiceConfig {
*/ */
replicationBaseEndpoint?: string; replicationBaseEndpoint?: string;
/**
* The base endpoint of the service used to handle the adapters.
*/
adapterEndpoint?: string;
/** /**
* The base endpoint of the service used to handle the replication rules. * The base endpoint of the service used to handle the replication rules.
* Replication rule related endpoints will be built based on this endpoint. * Replication rule related endpoints will be built based on this endpoint.

View File

@ -87,15 +87,17 @@ export interface Endpoint extends Base {
url: string; url: string;
} }
export interface Filter {
type: string;
style: string;
values ?: string[];
}
export interface Adapter extends Base { export interface Adapter extends Base {
type: string; type: string;
description: string; description: string;
supported_resource_types: [ supported_triggers: string [];
string supported_resource_filters: Filter [];
];
supported_resource_filters: [
string
];
} }
/** /**

View File

@ -6,7 +6,7 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
describe('JobLogService', () => { describe('JobLogService', () => {
const mockConfig: IServiceConfig = { const mockConfig: IServiceConfig = {
replicationBaseEndpoint: "/api/replication/executions/testing", replicationBaseEndpoint: "/api/replication/testing",
scanJobEndpoint: "/api/jobs/scan/testing" scanJobEndpoint: "/api/jobs/scan/testing"
}; };
@ -33,7 +33,7 @@ describe('JobLogService', () => {
it('should be initialized', inject([JobLogDefaultService], (service: JobLogService) => { it('should be initialized', inject([JobLogDefaultService], (service: JobLogService) => {
expect(service).toBeTruthy(); expect(service).toBeTruthy();
expect(config.replicationBaseEndpoint).toEqual("/api/replication/executions/testing"); expect(config.replicationBaseEndpoint).toEqual("/api/replication/testing");
expect(config.scanJobEndpoint).toEqual("/api/jobs/scan/testing"); expect(config.scanJobEndpoint).toEqual("/api/jobs/scan/testing");
})); }));
}); });

View File

@ -49,7 +49,7 @@ export class JobLogDefaultService extends JobLogService {
super(); super();
this._replicationJobBaseUrl = config.replicationBaseEndpoint this._replicationJobBaseUrl = config.replicationBaseEndpoint
? config.replicationBaseEndpoint ? config.replicationBaseEndpoint
: "/api/replication/executions"; : "/api/replication";
this._scanningJobBaseUrl = config.scanJobEndpoint this._scanningJobBaseUrl = config.scanJobEndpoint
? config.scanJobEndpoint ? config.scanJobEndpoint
: "/api/jobs/scan"; : "/api/jobs/scan";

View File

@ -7,7 +7,7 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
describe('ReplicationService', () => { describe('ReplicationService', () => {
const mockConfig: IServiceConfig = { const mockConfig: IServiceConfig = {
replicationRuleEndpoint: "/api/policies/replication/testing", replicationRuleEndpoint: "/api/policies/replication/testing",
replicationBaseEndpoint: "/api/replication/executions/testing" replicationBaseEndpoint: "/api/replication/testing"
}; };
let config: IServiceConfig; let config: IServiceConfig;
@ -38,6 +38,6 @@ describe('ReplicationService', () => {
it('should inject the right config', () => { it('should inject the right config', () => {
expect(config).toBeTruthy(); expect(config).toBeTruthy();
expect(config.replicationRuleEndpoint).toEqual("/api/policies/replication/testing"); expect(config.replicationRuleEndpoint).toEqual("/api/policies/replication/testing");
expect(config.replicationBaseEndpoint).toEqual("/api/replication/executions/testing"); expect(config.replicationBaseEndpoint).toEqual("/api/replication/testing");
}); });
}); });

View File

@ -10,7 +10,8 @@ import {
ReplicationJob, ReplicationJob,
ReplicationRule, ReplicationRule,
ReplicationJobItem, ReplicationJobItem,
ReplicationTasks ReplicationTasks,
Adapter
} from "./interface"; } from "./interface";
import { RequestQueryParams } from "./RequestQueryParams"; import { RequestQueryParams } from "./RequestQueryParams";
import { map, catchError } from "rxjs/operators"; import { map, catchError } from "rxjs/operators";
@ -140,6 +141,9 @@ export abstract class ReplicationService {
ruleId: number | string ruleId: number | string
): Observable<any>; ): Observable<any>;
abstract getReplicationAdapter(type: string): Observable<Adapter>;
/** /**
* Get the jobs for the specified replication rule. * Get the jobs for the specified replication rule.
* Set query parameters through 'queryParams', support: * Set query parameters through 'queryParams', support:
@ -202,7 +206,7 @@ export class ReplicationDefaultService extends ReplicationService {
: "/api/replication/policies"; : "/api/replication/policies";
this._replicateUrl = config.replicationBaseEndpoint this._replicateUrl = config.replicationBaseEndpoint
? config.replicationBaseEndpoint ? config.replicationBaseEndpoint
: "/api/replication/executions"; : "/api/replication";
} }
// Private methods // Private methods
@ -218,6 +222,14 @@ export class ReplicationDefaultService extends ReplicationService {
); );
} }
public getReplicationAdapter(type): Observable<Adapter> {
let requestUrl: string = `${this._replicateUrl}/adapters/${type}`;
return this.http
.get(requestUrl)
.pipe(map(response => response.json() as Adapter)
, catchError(error => observableThrowError(error)));
}
public getJobBaseUrl() { public getJobBaseUrl() {
return this._replicateUrl; return this._replicateUrl;
} }

View File

@ -63,9 +63,8 @@ const uiLibConfig: IServiceConfig = {
repositoryBaseEndpoint: "/api/repositories", repositoryBaseEndpoint: "/api/repositories",
logBaseEndpoint: "/api/logs", logBaseEndpoint: "/api/logs",
targetBaseEndpoint: "/api/registries", targetBaseEndpoint: "/api/registries",
replicationBaseEndpoint: "/api/replication/executions", replicationBaseEndpoint: "/api/replication",
replicationRuleEndpoint: "/api/replication/policies", replicationRuleEndpoint: "/api/replication/policies",
adapterEndpoint: "api/replication/adapters",
vulnerabilityScanningBaseEndpoint: "/api/repositories", vulnerabilityScanningBaseEndpoint: "/api/repositories",
projectPolicyEndpoint: "/api/projects/configs", projectPolicyEndpoint: "/api/projects/configs",
projectBaseEndpoint: "/api/projects", projectBaseEndpoint: "/api/projects",