Skip to content

Commit 5b592bb

Browse files
committed
Add JSON schema support for update schemas
Signed-off-by: Hamza Hamidi <[email protected]>
1 parent 5e6f35a commit 5b592bb

File tree

5 files changed

+92
-22
lines changed

5 files changed

+92
-22
lines changed

src/frontend/app/shared/components/add-service-instance/bind-apps-step/bind-apps-step.component.html

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,24 @@
1414
</mat-error>
1515
</mat-form-field>
1616
</form>
17-
<div *ngIf="schemas?.serviceBinding" class="json-schema">
17+
<div *ngIf="!!schema" class="json-schema">
1818
<mat-card class="json-schema-form">
1919
<mat-card-header>
20-
<mat-card-title>Generated Form<button mat-button color="accent" (click)="showJsonSchema=!showJsonSchema">Json schema</button>
20+
<mat-card-title><b>Generated Form</b><button mat-button color="accent" (click)="showJsonSchema=!showJsonSchema">Json schema</button>
2121
</mat-card-title>
2222
</mat-card-header>
2323
<mat-card-content>
24-
<json-schema-form loadExternalAssets="true" [options]="jsonFormOptions" [schema]="schemas.serviceBinding" [framework]="selectedFramework" (onChanges)="onFormChange($event)">
24+
<json-schema-form loadExternalAssets="true" [options]="jsonFormOptions" [schema]="schema" [framework]="selectedFramework" (validationErrors)="validationErrors($event)" (onChanges)="onFormChange($event)">
2525
</json-schema-form>
26+
<div *ngIf="!!prettyValidationErrors" class="data-bad" [innerHTML]="prettyValidationErrors"></div>
2627
</mat-card-content>
2728
</mat-card>
2829
<mat-card *ngIf="showJsonSchema" class="json-schema-data">
2930
<mat-card-header>
30-
<mat-card-title>Json schema</mat-card-title>
31+
<mat-card-title><b>Json schema</b></mat-card-title>
3132
</mat-card-header>
3233
<mat-card-content>
33-
<pre class="display-json">{{schemas.serviceBinding | json}} </pre>
34+
<pre class="display-json">{{schema | json}} </pre>
3435
</mat-card-content>
3536
</mat-card>
3637
</div>

src/frontend/app/shared/components/add-service-instance/bind-apps-step/bind-apps-step.component.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
}
55

66
@mixin json-schema-component() {
7+
.data-bad { background-color: #fcc; }
78
.display-json {
89
white-space: -moz-pre-wrap;
910
white-space: -pre-wrap;

src/frontend/app/shared/components/add-service-instance/bind-apps-step/bind-apps-step.component.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import { PaginationMonitorFactory } from '../../../monitors/pagination-monitor.f
1818
import { StepOnNextResult } from '../../stepper/step/step.component';
1919
import { CsiGuidsService } from '../csi-guids.service';
2020
import { SpecifyDetailsStepComponent } from '../specify-details-step/specify-details-step.component';
21-
import { IServicePlanSchemas } from '../../../../core/cf-api-svc.types';
2221
import { safeUnsubscribe } from '../../../../features/service-catalog/services-helper';
22+
import { JsonPointer } from 'angular2-json-schema-form';
2323

2424
@Component({
2525
selector: 'app-bind-apps-step',
@@ -39,10 +39,12 @@ export class BindAppsStepComponent implements OnDestroy, AfterContentInit {
3939
guideText = 'Specify the application to bind (Optional)';
4040

4141
selectedFramework = 'material-design';
42-
schemas: IServicePlanSchemas;
42+
schema: any;
4343
showJsonSchema: boolean;
4444
jsonFormOptions: any = { addSubmit: false };
4545
selectedServiceSubscription: Subscription;
46+
formValidationErrors: any;
47+
selectedService$: any;
4648
constructor(
4749
private store: Store<AppState>,
4850
private paginationMonitorFactory: PaginationMonitorFactory
@@ -68,6 +70,30 @@ export class BindAppsStepComponent implements OnDestroy, AfterContentInit {
6870
}
6971
}
7072

73+
validationErrors(data: any): void {
74+
this.formValidationErrors = data;
75+
}
76+
77+
get prettyValidationErrors() {
78+
if (!this.formValidationErrors) { return null; }
79+
const errorArray = [];
80+
for (const error of this.formValidationErrors) {
81+
const message = error.message;
82+
const dataPathArray = JsonPointer.parse(error.dataPath);
83+
if (dataPathArray.length) {
84+
let field = dataPathArray[0];
85+
for (let i = 1; i < dataPathArray.length; i++) {
86+
const key = dataPathArray[i];
87+
field += /^\d+$/.test(key) ? `[${key}]` : `.${key}`;
88+
}
89+
errorArray.push(`${field}: ${message}`);
90+
} else {
91+
errorArray.push(message);
92+
}
93+
}
94+
return errorArray.join('<br>');
95+
}
96+
7197
ngAfterContentInit() {
7298
this.validateSubscription = this.stepperForm.statusChanges.pipe(
7399
map(() => {
@@ -96,13 +122,11 @@ export class BindAppsStepComponent implements OnDestroy, AfterContentInit {
96122
}
97123

98124
onEnter = (selectedService$?) => {
125+
this.selectedService$ = selectedService$;
99126
if (selectedService$ instanceof Observable) {
100127
this.selectedServiceSubscription = selectedService$
101128
.subscribe(selectedService => {
102-
this.schemas = {
103-
serviceBinding: this.filterSchema(selectedService.entity.entity.schemas.service_binding.create.parameters),
104-
serviceInstances: this.filterSchema(selectedService.entity.entity.schemas.service_instance.create.parameters),
105-
};
129+
this.schema = this.filterSchema(selectedService.entity.entity.schemas.service_binding.create.parameters);
106130
});
107131
}
108132
}
@@ -116,7 +140,7 @@ export class BindAppsStepComponent implements OnDestroy, AfterContentInit {
116140

117141
submit = (): Observable<StepOnNextResult> => {
118142
this.setApp();
119-
return observableOf({ success: true, data: this.schemas });
143+
return observableOf({ success: true, data: this.selectedService$ });
120144
}
121145

122146
setApp = () => this.store.dispatch(

src/frontend/app/shared/components/add-service-instance/specify-details-step/specify-details-step.component.html

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,24 @@
3838
</mat-select>
3939
</mat-form-field>
4040
</form>
41-
<div *ngIf="schemas?.serviceInstances" class="json-schema">
41+
<div *ngIf="!!schema" class="json-schema">
4242
<mat-card class="json-schema-form">
4343
<mat-card-header>
44-
<mat-card-title>Generated Form<button mat-button color="accent" (click)="showJsonSchema=!showJsonSchema">Json schema</button>
44+
<mat-card-title><b>Generated Form</b><button mat-button color="accent" (click)="showJsonSchema=!showJsonSchema">Json schema</button>
4545
</mat-card-title>
4646
</mat-card-header>
4747
<mat-card-content>
48-
<json-schema-form loadExternalAssets="true" [options]="jsonFormOptions" [schema]="schemas.serviceInstances" [framework]="selectedFramework" (onChanges)="onFormChange($event)">
48+
<json-schema-form loadExternalAssets="true" [options]="jsonFormOptions" [schema]="schema" [framework]="selectedFramework" (validationErrors)="validationErrors($event)" (onChanges)="onFormChange($event)">
4949
</json-schema-form>
50+
<div *ngIf="!!prettyValidationErrors" class="data-bad" [innerHTML]="prettyValidationErrors"></div>
5051
</mat-card-content>
5152
</mat-card>
5253
<mat-card *ngIf="showJsonSchema" class="json-schema-data">
5354
<mat-card-header>
54-
<mat-card-title>Json schema</mat-card-title>
55+
<mat-card-title><b>Json schema</b></mat-card-title>
5556
</mat-card-header>
5657
<mat-card-content>
57-
<pre class="display-json">{{schemas.serviceInstances | json}} </pre>
58+
<pre class="display-json">{{schema | json}} </pre>
5859
</mat-card-content>
5960
</mat-card>
6061
</div>

src/frontend/app/shared/components/add-service-instance/specify-details-step/specify-details-step.component.ts

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import {
1818
take,
1919
tap
2020
} from 'rxjs/operators';
21-
import { IServiceInstance, IServicePlanSchemas } from '../../../../core/cf-api-svc.types';
22-
import { getServiceJsonParams } from '../../../../features/service-catalog/services-helper';
21+
import { IServiceInstance } from '../../../../core/cf-api-svc.types';
22+
import { getServiceJsonParams, safeUnsubscribe } from '../../../../features/service-catalog/services-helper';
2323
import { GetAppEnvVarsAction } from '../../../../store/actions/app-metadata.actions';
2424
import { SetCreateServiceInstanceOrg, SetServiceInstanceGuid } from '../../../../store/actions/create-service-instance.actions';
2525
import { RouterNav } from '../../../../store/actions/router.actions';
@@ -40,6 +40,8 @@ import { CreateServiceInstanceHelperServiceFactory } from '../create-service-ins
4040
import { CreateServiceInstanceHelper } from '../create-service-instance-helper.service';
4141
import { CsiGuidsService } from '../csi-guids.service';
4242
import { CsiModeService } from '../csi-mode.service';
43+
import { constants } from 'os';
44+
import { JsonPointer } from 'angular2-json-schema-form';
4345

4446

4547
const enum FormMode {
@@ -90,11 +92,13 @@ export class SpecifyDetailsStepComponent implements OnDestroy, AfterContentInit
9092
separatorKeysCodes = [ENTER, COMMA, SPACE];
9193
tags = [];
9294
spaceScopeSub: Subscription;
95+
selectedServiceSubscription: Subscription;
9396
bindExistingInstance = false;
9497
subscriptions: Subscription[] = [];
95-
schemas: IServicePlanSchemas;
98+
schema: any;
9699
showJsonSchema: boolean;
97100
jsonFormOptions: any = { addSubmit: false };
101+
formValidationErrors: any;
98102

99103
static isValidJsonValidatorFn = (): ValidatorFn => {
100104
return (formField: AbstractControl): { [key: string]: any } => {
@@ -182,8 +186,15 @@ export class SpecifyDetailsStepComponent implements OnDestroy, AfterContentInit
182186
);
183187
}
184188

185-
onEnter = (schemas) => {
186-
this.schemas = schemas;
189+
onEnter = (selectedService$?) => {
190+
if (selectedService$ instanceof Observable) {
191+
this.selectedServiceSubscription = selectedService$
192+
.subscribe(selectedService => {
193+
if (!!this.modeService.isEditServiceInstanceMode()) {
194+
this.schema = this.filterSchema(selectedService.entity.entity.schemas.service_instance.create.parameters);
195+
} else { this.schema = this.filterSchema(selectedService.entity.entity.schemas.service_instance.update.parameters); }
196+
});
197+
}
187198
this.formMode = FormMode.CreateServiceInstance;
188199
this.allServiceInstances$ = this.cSIHelperService.getServiceInstancesForService(null, null, this.csiGuidsService.cfGuid);
189200
if (this.modeService.isEditServiceInstanceMode()) {
@@ -204,6 +215,13 @@ export class SpecifyDetailsStepComponent implements OnDestroy, AfterContentInit
204215
this.subscriptions.push(this.setupFormValidatorData());
205216
}
206217

218+
private filterSchema = (schema: any): any => {
219+
return Object.keys(schema).reduce((obj, key) => {
220+
if (key !== '$schema') { obj[key] = schema[key]; }
221+
return obj;
222+
}, {});
223+
}
224+
207225
resetForms = (mode: FormMode) => {
208226
this.validate.next(false);
209227
this.createNewInstanceForm.reset();
@@ -251,6 +269,7 @@ export class SpecifyDetailsStepComponent implements OnDestroy, AfterContentInit
251269

252270
ngOnDestroy(): void {
253271
this.subscriptions.forEach(s => s.unsubscribe());
272+
safeUnsubscribe(this.selectedServiceSubscription);
254273
}
255274

256275
ngAfterContentInit() {
@@ -264,6 +283,30 @@ export class SpecifyDetailsStepComponent implements OnDestroy, AfterContentInit
264283
}
265284
}
266285

286+
validationErrors(data: any): void {
287+
this.formValidationErrors = data;
288+
}
289+
290+
get prettyValidationErrors() {
291+
if (!this.formValidationErrors) { return null; }
292+
const errorArray = [];
293+
for (const error of this.formValidationErrors) {
294+
const message = error.message;
295+
const dataPathArray = JsonPointer.parse(error.dataPath);
296+
if (dataPathArray.length) {
297+
let field = dataPathArray[0];
298+
for (let i = 1; i < dataPathArray.length; i++) {
299+
const key = dataPathArray[i];
300+
field += /^\d+$/.test(key) ? `[${key}]` : `.${key}`;
301+
}
302+
errorArray.push(`${field}: ${message}`);
303+
} else {
304+
errorArray.push(message);
305+
}
306+
}
307+
return errorArray.join('<br>');
308+
}
309+
267310
onNext = (): Observable<StepOnNextResult> => {
268311
return this.store.select(selectCreateServiceInstance).pipe(
269312
filter(p => !!p),

0 commit comments

Comments
 (0)