Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature json schema #3050

Merged
merged 36 commits into from
Jan 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f2bac91
ADD angular2-json-schema-form
hamzahamidi Jul 11, 2018
d5caf5e
ADD json schema form generator to service binding & instance
hamzahamidi Jul 11, 2018
7d746b0
Fix codeclimate issues
hamzahamidi Jul 11, 2018
ebdfcf2
Fix Error subscribe of undefined onEnter
hamzahamidi Jul 11, 2018
2603bdc
ADD mixin for json-schema-component
hamzahamidi Jul 11, 2018
0879bd0
Update library Angular 2 JSON Schema Form builder
hamzahamidi Jul 19, 2018
5e6f35a
Delete unused types
hamzahamidi Jul 19, 2018
5b592bb
Add JSON schema support for update schemas
hamzahamidi Jul 19, 2018
e7c63bf
Refractoring
hamzahamidi Jul 19, 2018
a448786
Fix AOT build
hamzahamidi Jul 25, 2018
e4f032c
ADD schemaplan types
hamzahamidi Aug 9, 2018
048bfd9
optimize prettyValidationErrors
hamzahamidi Aug 9, 2018
f2075b4
fix Onformchange
hamzahamidi Aug 9, 2018
5c6f849
Merge remote-tracking branch 'upstream/v2-master' into feature-json-s…
richard-cox Sep 7, 2018
49c3539
Update schema form
richard-cox Sep 14, 2018
40a4b91
Refactor, some expression changed fixes & spamming fix
richard-cox Sep 14, 2018
0e27c78
WIP
richard-cox Sep 17, 2018
e61a158
Fixes for non-schema world
richard-cox Sep 17, 2018
c7dd5a7
Use stratos-angular6-json-schema-form & don't loadExternalAssets
richard-cox Sep 19, 2018
3d789cf
Various fixes
richard-cox Sep 19, 2018
ecba7cc
Fix unit tests
richard-cox Sep 19, 2018
4323333
Remove todos
richard-cox Sep 19, 2018
43548b2
Fix imports
richard-cox Sep 19, 2018
424a768
Changes following review
richard-cox Sep 20, 2018
561c3a3
Removed todo
richard-cox Sep 20, 2018
8728318
Merge remote-tracking branch 'upstream/v2-master' into feature-json-s…
richard-cox Sep 21, 2018
4c52565
Merge pull request #2997 from richard-cox/feature-json-schema
richard-cox Sep 21, 2018
2fca2d3
Fix valid state of json only state in schema form
richard-cox Sep 24, 2018
bf7d428
Merge remote-tracking branch 'origin/v2-master' into feature-json-schema
richard-cox Nov 5, 2018
4dce2a9
Merge remote-tracking branch 'origin/v2-master' into feature-json-schema
richard-cox Nov 21, 2018
1e3ef2e
Merge fixes
richard-cox Nov 21, 2018
0f99693
Merge remote-tracking branch 'origin/v2-master' into feature-json-schema
richard-cox Dec 6, 2018
fa3d89c
Merge remote-tracking branch 'origin/v2-master' into feature-json-schema
richard-cox Dec 18, 2018
6bedf2d
Merge fix
richard-cox Dec 18, 2018
fb40fad
Merge remote-tracking branch 'origin/v2-master' into feature-json-schema
richard-cox Dec 20, 2018
d7cdbee
Show JSON style service param option when the schema is empty (instea…
richard-cox Dec 21, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@angular/compiler": "^6.1.1",
"@angular/core": "^6.1.1",
"@angular/forms": "^6.1.1",
"@angular/flex-layout": "^6.0.0-beta.16",
"@angular/http": "^6.1.1",
"@angular/material": "^6.1.0",
"@angular/material-moment-adapter": "^6.4.7",
Expand All @@ -59,6 +60,7 @@
"@ngrx/store-devtools": "^6.0.1",
"@swimlane/ngx-charts": "^9.0.0",
"angular2-virtual-scroll": "^0.3.1",
"stratos-angular6-json-schema-form": "1.0.3",
"core-js": "^2.5.7",
"hammerjs": "^2.0.8",
"js-yaml": "^3.11.0",
Expand Down
16 changes: 16 additions & 0 deletions src/frontend/app/core/cf-api-svc.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ export interface IServicePlan {
service?: APIResource<IService>;
guid?: string;
cfGuid?: string;
schemas?: ServicePlanSchemas;
}

export interface ServicePlanSchemas {
service_instance: ServicePlanSchema;
service_binding: ServicePlanSchema;
}

export interface ServicePlanSchema {
create?: {
parameters: object
};
update?: {
parameters: object
};
}

export interface IServicePlanExtra {
Expand All @@ -81,6 +96,7 @@ export interface IServicePlanCost {
};
unit: string;
}

export interface IService {
label: string;
description: string;
Expand Down
16 changes: 16 additions & 0 deletions src/frontend/app/core/utils.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,22 @@ export function parseHttpPipeError(res): {} {
return {};
}

export function safeStringToObj(value: string): object {
try {
if (value) {
const jsonObj = JSON.parse(value);
// Check if jsonObj is actually an obj
if (jsonObj.constructor !== {}.constructor) {
throw new Error('not an object');
}
return jsonObj;
}
} catch (e) {
return null;
}
return null;
}

export const safeUnsubscribe = (...subs: Subscription[]) => {
subs.forEach(sub => {
if (sub) {
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/app/features/service-catalog/services-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { getPaginationObservables } from '../../store/reducers/pagination-reduce
import { APIResource } from '../../store/types/api.types';
import { getIdFromRoute } from '../cloud-foundry/cf.helpers';


export const getSvcAvailability = (servicePlan: APIResource<IServicePlan>,
serviceBroker: APIResource<IServiceBroker>,
allServicePlanVisibilities: APIResource<IServicePlanVisibility>[]) => {
Expand Down Expand Up @@ -51,7 +50,6 @@ export const getServiceJsonParams = (params: any): {} => {
return prms;
};


export const isMarketplaceMode = (activatedRoute: ActivatedRoute) => {
const serviceId = getIdFromRoute(activatedRoute, 'serviceId');
const cfId = getIdFromRoute(activatedRoute, 'endpointId');
Expand Down Expand Up @@ -108,3 +106,5 @@ export const getServicePlans = (
}
}));
};


Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
<app-step title="Select Plan" [onNext]="selectPlan.onNext" [blocked]="initialisedService$ | async" [onEnter]="selectPlan.onEnter" [valid]="selectPlan.validate | async" cancelButtonText="Cancel">
<app-select-plan-step #selectPlan></app-select-plan-step>
</app-step>
<app-step [title]="bindAppStepperText" *ngIf="modeService.viewDetail.showBindApp" [skip]="skipApps$ | async" [onNext]="bindApp.submit " [valid]="bindApp.validate | async" cancelButtonText="Cancel" finishButtonText="Bind">
<app-step [title]="bindAppStepperText" *ngIf="modeService.viewDetail.showBindApp" [skip]="skipApps$ | async" [onEnter]="bindApp.onEnter" [onNext]="bindApp.submit " [valid]="bindApp.validate | async" cancelButtonText="Cancel">
<app-bind-apps-step #bindApp [boundAppId]="appId"></app-bind-apps-step>
</app-step>
<app-step title="Service Instance" [onNext]="specifyDetails.onNext" [onEnter]="specifyDetails.onEnter" [blocked]="!!(specifyDetails.serviceInstancesInit$ | async)" [valid]=" specifyDetails.validate | async " cancelButtonText="Cancel " nextButtonText="Create ">
<app-step title="Service Instance" [onNext]="specifyDetails.onNext" [onEnter]="specifyDetails.onEnter" [blocked]="!!(specifyDetails.serviceInstancesInit$ | async)" [valid]="specifyDetails.validate | async " cancelButtonText="Cancel " nextButtonText="Create ">
<app-specify-details-step [appId]="appId" #specifyDetails [showModeSelection]="!!appId "></app-specify-details-step>
</app-step>
</app-steppers>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MaterialDesignFrameworkModule } from 'stratos-angular6-json-schema-form';

import { ServicesService } from '../../../../features/service-catalog/services.service';
import { ServicesServiceMock } from '../../../../features/service-catalog/services.service.mock';
Expand Down Expand Up @@ -28,6 +29,7 @@ import { CfServiceCardComponent } from '../../list/list-types/cf-services/cf-ser
import { MetadataItemComponent } from '../../metadata-item/metadata-item.component';
import { MultilineTitleComponent } from '../../multiline-title/multiline-title.component';
import { PageHeaderModule } from '../../page-header/page-header.module';
import { SchemaFormComponent } from '../../schema-form/schema-form.component';
import { ServiceIconComponent } from '../../service-icon/service-icon.component';
import { SteppersModule } from '../../stepper/steppers.module';
import { BindAppsStepComponent } from '../bind-apps-step/bind-apps-step.component';
Expand Down Expand Up @@ -63,12 +65,14 @@ describe('AddServiceInstanceComponent', () => {
AppChipsComponent,
ApplicationStateIconComponent,
ApplicationStateIconPipe,
SchemaFormComponent,
MultilineTitleComponent,
FocusDirective
],
imports: [
PageHeaderModule,
SteppersModule,
MaterialDesignFrameworkModule,
// CoreModule,
BaseTestModulesNoShared
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export class AddServiceInstanceComponent implements OnDestroy, AfterContentInit
bindAppStepperText = 'Bind App (Optional)';
appId: string;
public inMarketplaceMode: boolean;

constructor(
private cSIHelperServiceFactory: CreateServiceInstanceHelperServiceFactory,
private activatedRoute: ActivatedRoute,
Expand Down Expand Up @@ -130,13 +131,6 @@ export class AddServiceInstanceComponent implements OnDestroy, AfterContentInit
}
}

private getIdsFromRoute() {
const serviceId = getIdFromRoute(this.activatedRoute, 'serviceId');
const cfId = getIdFromRoute(this.activatedRoute, 'endpointId');
const appId = getIdFromRoute(this.activatedRoute, 'id');
return { serviceId, cfId, appId };
}

private setupForAppServiceMode() {

const appId = getIdFromRoute(this.activatedRoute, 'id');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
<mat-option *ngFor="let app of apps$ | async" [value]="app.metadata.guid">{{ app.entity.name }}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<textarea matInput placeholder="JSON parameters" matAutosizeMinRows="2" matAutosizeMaxRows="5" formControlName="params"></textarea>
<mat-error *ngIf="stepperForm.controls.params?.hasError('notValidJson')">
Not valid JSON. Please specify a valid JSON Object
</mat-error>
</mat-form-field>
<div *ngIf="stepperForm.controls.apps.value" class="stepper-form__params">
<div>Binding Parameters</div>
<app-schema-form [config]="schemaFormConfig" (dataChange)="setBindingParams($event)" (validChange)="setParamValid($event)"></app-schema-form>
</div>
</form>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
:host {
flex: 1;
}

.stepper-form__params {
padding-top: 20px;
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MaterialDesignFrameworkModule } from 'stratos-angular6-json-schema-form';

import { BindAppsStepComponent } from './bind-apps-step.component';
import { BaseTestModules, BaseTestModulesNoShared } from '../../../../test-framework/cloud-foundry-endpoint-service.helper';
import { ServicesService } from '../../../../features/service-catalog/services.service';
import { ServicesServiceMock } from '../../../../features/service-catalog/services.service.mock';
import { CsiGuidsService } from '../csi-guids.service';
import { BaseTestModulesNoShared } from '../../../../test-framework/cloud-foundry-endpoint-service.helper';
import { PaginationMonitorFactory } from '../../../monitors/pagination-monitor.factory';
import { SchemaFormComponent } from '../../schema-form/schema-form.component';
import { CsiGuidsService } from '../csi-guids.service';
import { BindAppsStepComponent } from './bind-apps-step.component';

describe('BindAppsStepComponent', () => {
let component: BindAppsStepComponent;
let fixture: ComponentFixture<BindAppsStepComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BindAppsStepComponent],
imports: [BaseTestModulesNoShared],
declarations: [
BindAppsStepComponent,
SchemaFormComponent
],
imports: [
BaseTestModulesNoShared,
MaterialDesignFrameworkModule
],
providers: [
{ provide: ServicesService, useClass: ServicesServiceMock },
CsiGuidsService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { AfterContentInit, Component, Input, OnDestroy } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs';
import { filter, first, map, switchMap, tap } from 'rxjs/operators';
import { filter, switchMap } from 'rxjs/operators';

import { IServicePlan } from '../../../../core/cf-api-svc.types';
import { IApp } from '../../../../core/cf-api.types';
import { appDataSort } from '../../../../features/cloud-foundry/services/cloud-foundry-endpoint.service';
import { pathGet, safeUnsubscribe } from '../../../../core/utils.service';
import { SetCreateServiceInstanceApp } from '../../../../store/actions/create-service-instance.actions';
import { GetAllAppsInSpace } from '../../../../store/actions/space.actions';
import { AppState } from '../../../../store/app-state';
Expand All @@ -15,9 +16,8 @@ import { getPaginationObservables } from '../../../../store/reducers/pagination-
import { selectCreateServiceInstance } from '../../../../store/selectors/create-service-instance.selectors';
import { APIResource } from '../../../../store/types/api.types';
import { PaginationMonitorFactory } from '../../../monitors/pagination-monitor.factory';
import { SchemaFormConfig } from '../../schema-form/schema-form.component';
import { StepOnNextResult } from '../../stepper/step/step.component';
import { CsiGuidsService } from '../csi-guids.service';
import { SpecifyDetailsStepComponent } from '../specify-details-step/specify-details-step.component';

@Component({
selector: 'app-bind-apps-step',
Expand All @@ -30,18 +30,21 @@ export class BindAppsStepComponent implements OnDestroy, AfterContentInit {
boundAppId: string;

validateSubscription: Subscription;
validate = new BehaviorSubject(true);
validate = new BehaviorSubject<boolean>(true);
serviceInstanceGuid: string;
stepperForm: FormGroup;
apps$: Observable<APIResource<IApp>[]>;
guideText = 'Specify the application to bind (Optional)';
selectedServicePlan: APIResource<IServicePlan>;
bindingParams: object = {};
schemaFormConfig: SchemaFormConfig;

constructor(
private store: Store<AppState>,
private paginationMonitorFactory: PaginationMonitorFactory
) {
this.stepperForm = new FormGroup({
apps: new FormControl(''),
params: new FormControl('', SpecifyDetailsStepComponent.isValidJsonValidatorFn()),
});
}

Expand All @@ -54,16 +57,6 @@ export class BindAppsStepComponent implements OnDestroy, AfterContentInit {
}

ngAfterContentInit() {
this.validateSubscription = this.stepperForm.statusChanges.pipe(
map(() => {
if (this.stepperForm.pristine) {
setTimeout(() => this.validate.next(true));
}
setTimeout(() => this.validate.next(this.stepperForm.valid));
})
).subscribe();


this.apps$ = this.store.select(selectCreateServiceInstance).pipe(
filter(p => !!p && !!p.spaceGuid && !!p.cfGuid),
switchMap(createServiceInstance => {
Expand All @@ -80,17 +73,53 @@ export class BindAppsStepComponent implements OnDestroy, AfterContentInit {
this.setBoundApp();
}

submit = (): Observable<StepOnNextResult> => {
this.setApp();
return observableOf({ success: true });
onEnter = (selectedServicePlan: APIResource<IServicePlan>) => {
if (selectedServicePlan) {
// Don't overwrite if it's null (we've returned to this step from the next)
this.selectedServicePlan = selectedServicePlan;
}

// Start
this.validateSubscription = this.stepperForm.controls['apps'].valueChanges.subscribe(app => {
if (!app) {
// If there's no app selected the step will always be valid
this.validate.next(true);
}
});

if (!this.schemaFormConfig) {
// Create new config
this.schemaFormConfig = {
schema: pathGet('entity.schemas.service_binding.create.parameters', this.selectedServicePlan),
};
} else {
// Update existing config (retaining any existing config)
this.schemaFormConfig = {
...this.schemaFormConfig,
schema: pathGet('entity.schemas.service_binding.create.parameters', this.selectedServicePlan)
};
}

}

setBindingParams(data) {
this.bindingParams = data;
}

setParamValid(valid: boolean) {
this.validate.next(valid);
}

setApp = () => this.store.dispatch(
new SetCreateServiceInstanceApp(this.stepperForm.controls.apps.value, this.stepperForm.controls.params.value)
)
submit = (): Observable<StepOnNextResult> => {
this.store.dispatch(new SetCreateServiceInstanceApp(this.stepperForm.controls.apps.value, this.bindingParams));
return observableOf({
success: true,
data: this.selectedServicePlan
});
}

ngOnDestroy(): void {
this.validateSubscription.unsubscribe();
safeUnsubscribe(this.validateSubscription);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,7 @@ import {
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { Store } from '@ngrx/store';
import {
BehaviorSubject,
combineLatest as observableCombineLatest,
Observable,
of as observableOf,
Subscription,
} from 'rxjs';
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
import {
distinctUntilChanged,
filter,
Expand Down Expand Up @@ -167,7 +161,12 @@ export class SelectPlanStepComponent implements OnDestroy {

onNext = (): Observable<StepOnNextResult> => {
this.store.dispatch(new SetCreateServiceInstanceServicePlan(this.stepperForm.controls.servicePlans.value));
return observableOf({ success: true });
return this.selectedPlan$.pipe(
map(selectedServicePlan => ({
success: true,
data: selectedServicePlan.entity
}))
);
}

ngOnDestroy(): void {
Expand Down
Loading