From 2c95d4d6985f93d4b7296c6bb91d9b2563106e4a Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 11 Oct 2018 10:51:10 -0700 Subject: [PATCH] SHIBUI-914 Fixing validation --- ui/package-lock.json | 6 +- ui/package.json | 2 +- .../model/wizards/metadata-source-wizard.ts | 3 +- .../dashboard-resolvers-list.component.ts | 10 ++- .../container/new-resolver.component.ts | 17 +++- .../resolver-wizard-step.component.ts | 7 +- .../container/resolver-wizard.component.html | 4 +- .../container/resolver-wizard.component.ts | 14 +++- .../metadata/resolver/effect/wizard.effect.ts | 6 +- .../widget/button/icon-button.component.ts | 2 +- ui/src/app/wizard/reducer/index.ts | 3 +- .../filebacked-http-common.editor.schema.json | 1 + .../assets/schema/source/metadata-source.json | 81 ++++++++++++++----- 13 files changed, 119 insertions(+), 37 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 4850aa82f..89be49e79 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -7956,9 +7956,9 @@ "dev": true }, "ngx-schema-form": { - "version": "2.0.0-beta.3", - "resolved": "https://registry.npmjs.org/ngx-schema-form/-/ngx-schema-form-2.0.0-beta.3.tgz", - "integrity": "sha512-t545QEm16G4kdHFq4VOfmn0ICVwE6v+WAeQ5Bw1ur21U6U6n5q1srImQtefmGdqx69kl1sNwLJLF7Zm8UhJZCg==", + "version": "2.2.0-beta.1", + "resolved": "https://registry.npmjs.org/ngx-schema-form/-/ngx-schema-form-2.2.0-beta.1.tgz", + "integrity": "sha512-tNJR/rFEU2zHAOKOl47ujqM/7y08x8slPYyozgUgKt6aJa5JATqzf2Ee0WAF0fYrJzN4diF1fpNWHYqoA3qDGg==", "requires": { "tslib": "1.9.3" }, diff --git a/ui/package.json b/ui/package.json index b02a3f394..9bf38918e 100644 --- a/ui/package.json +++ b/ui/package.json @@ -34,7 +34,7 @@ "deep-object-diff": "^1.1.0", "file-saver": "^1.3.3", "font-awesome": "^4.7.0", - "ngx-schema-form": "^2.0.0-beta.3", + "ngx-schema-form": "^2.2.0-beta.1", "rxjs": "^6.1.0", "rxjs-compat": "^6.1.0", "xml-formatter": "^1.0.1", diff --git a/ui/src/app/metadata/domain/model/wizards/metadata-source-wizard.ts b/ui/src/app/metadata/domain/model/wizards/metadata-source-wizard.ts index 7917d7283..82a7a2763 100644 --- a/ui/src/app/metadata/domain/model/wizards/metadata-source-wizard.ts +++ b/ui/src/app/metadata/domain/model/wizards/metadata-source-wizard.ts @@ -140,6 +140,7 @@ export class MetadataSourceWizard implements Wizard { } getValidators(...args: any[]): { [key: string]: any } { - return {}; + const validators = {}; + return validators; } } diff --git a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts index b4b5af807..d32b1742c 100644 --- a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts +++ b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts @@ -71,7 +71,15 @@ export class DashboardResolversListComponent implements OnInit { } edit(entity: MetadataEntity): void { - this.router.navigate(['metadata', 'resolver', entity.getId(), entity.isDraft() ? 'wizard' : 'edit']); + if (entity.isDraft()) { + this.router.navigate(['metadata', 'resolver', 'new', 'blank'], { + queryParams: { + entityId: entity.getId() + } + }); + } else { + this.router.navigate(['metadata', 'resolver', entity.getId(), 'edit']); + } } toggleEntity(entity: MetadataEntity): void { diff --git a/ui/src/app/metadata/resolver/container/new-resolver.component.ts b/ui/src/app/metadata/resolver/container/new-resolver.component.ts index 71eee6e07..ce42ea511 100644 --- a/ui/src/app/metadata/resolver/container/new-resolver.component.ts +++ b/ui/src/app/metadata/resolver/container/new-resolver.component.ts @@ -1,7 +1,10 @@ import { Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Observable } from 'rxjs'; -import { map, withLatestFrom } from 'rxjs/operators'; +import { Observable, Subscription } from 'rxjs'; +import { map, withLatestFrom, distinctUntilChanged } from 'rxjs/operators'; +import { SelectDraft } from '../action/draft.action'; +import { Store } from '@ngrx/store'; +import * as fromCollection from '../reducer'; @Component({ selector: 'new-resolver-page', @@ -10,14 +13,22 @@ import { map, withLatestFrom } from 'rxjs/operators'; }) export class NewResolverComponent { + actionsSubscription: Subscription; + canSetNewType$: Observable; constructor( - private route: ActivatedRoute + private route: ActivatedRoute, + private store: Store ) { this.canSetNewType$ = this.route.queryParams.pipe( withLatestFrom(this.route.url), map(([params, url]) => this.route.snapshot.firstChild.routeConfig.path !== 'blank' || params.index === 'common') ); + + this.actionsSubscription = this.route.queryParams.pipe( + distinctUntilChanged(), + map(params => new SelectDraft(params.entityId)) + ).subscribe(store); } } diff --git a/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.ts b/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.ts index b9be4d33b..c916cb689 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.ts +++ b/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.ts @@ -6,11 +6,9 @@ import { Store } from '@ngrx/store'; import * as fromResolver from '../reducer'; import * as fromWizard from '../../../wizard/reducer'; -import { SetDefinition } from '../../../wizard/action/wizard.action'; import { UpdateStatus, UpdateChanges } from '../action/entity.action'; import { Wizard } from '../../../wizard/model'; import { MetadataResolver } from '../../domain/model'; -import { pick } from '../../../shared/util'; @Component({ selector: 'resolver-wizard-step', @@ -70,10 +68,11 @@ export class ResolverWizardStepComponent implements OnDestroy { this.valueChangeEmitted$.pipe( withLatestFrom(this.definition$), skipWhile(([ changes, definition ]) => !definition || !changes), - map(([ changes, definition ]) => definition.parser(changes.value)) + map(([ changes, definition ]) => definition.parser(changes.value)), + withLatestFrom(this.store.select(fromResolver.getSelectedDraft)), + map(([changes, original]) => ({ ...changes })) ) .subscribe(changes => { - // console.log(changes); this.store.dispatch(new UpdateChanges(changes)); }); diff --git a/ui/src/app/metadata/resolver/container/resolver-wizard.component.html b/ui/src/app/metadata/resolver/container/resolver-wizard.component.html index f5d57a27d..273a327e6 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard.component.html +++ b/ui/src/app/metadata/resolver/container/resolver-wizard.component.html @@ -2,4 +2,6 @@
-
\ No newline at end of file + +
{{ changes$ | async | json }}
+
{{ schema$ | async | json }}
\ No newline at end of file diff --git a/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts b/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts index b2f4f77d3..62d57798f 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts +++ b/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts @@ -10,7 +10,7 @@ import { RouterStateSnapshot } from '@angular/router'; import { Observable, Subject, of } from 'rxjs'; -import { skipWhile } from 'rxjs/operators'; +import { skipWhile, startWith } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; @@ -55,6 +55,9 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat saved$: Observable; saving: boolean; + valid$: Observable; + schema$: Observable; + constructor( private store: Store, private route: ActivatedRoute, @@ -73,6 +76,14 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat } }); + this.valid$ = this.store.select(fromResolver.getEntityIsValid); + + this.valid$ + .pipe(startWith(false)) + .subscribe((valid) => { + this.store.dispatch(new SetDisabled(!valid)); + }); + this.store.dispatch(new SetDefinition(this.sourceWizard)); this.store.select(fromWizard.getNext).subscribe(n => this.nextStep = n); @@ -80,6 +91,7 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat this.store.select(fromWizard.getWizardIndex).subscribe(i => this.currentPage = i); this.changes$ = this.store.select(fromResolver.getEntityChanges); + this.schema$ = this.store.select(fromWizard.getSchema); this.route.queryParams.subscribe(params => { if (params.index) { diff --git a/ui/src/app/metadata/resolver/effect/wizard.effect.ts b/ui/src/app/metadata/resolver/effect/wizard.effect.ts index 5e6c8d6b1..f53e0fa70 100644 --- a/ui/src/app/metadata/resolver/effect/wizard.effect.ts +++ b/ui/src/app/metadata/resolver/effect/wizard.effect.ts @@ -46,7 +46,8 @@ export class WizardEffects { const params = { ...this.activatedRoute.snapshot.queryParams, index }; this.router.navigate([], { relativeTo: this.activatedRoute, - queryParams: params + queryParams: params, + queryParamsHandling: 'preserve' }); }) ); @@ -61,7 +62,8 @@ export class WizardEffects { const params = { ...this.activatedRoute.snapshot.queryParams, entityId }; this.router.navigate([], { relativeTo: this.activatedRoute, - queryParams: params + queryParams: params, + queryParamsHandling: 'preserve' }); }) ); diff --git a/ui/src/app/schema-form/widget/button/icon-button.component.ts b/ui/src/app/schema-form/widget/button/icon-button.component.ts index 652490804..01efc1f74 100644 --- a/ui/src/app/schema-form/widget/button/icon-button.component.ts +++ b/ui/src/app/schema-form/widget/button/icon-button.component.ts @@ -2,7 +2,7 @@ import { Component, AfterViewInit, ChangeDetectorRef } from '@angular/core'; import { ButtonWidget } from 'ngx-schema-form'; -import { ɵb as ActionRegistry } from 'ngx-schema-form'; +import { ActionRegistry } from 'ngx-schema-form'; import { interval } from 'rxjs'; @Component({ diff --git a/ui/src/app/wizard/reducer/index.ts b/ui/src/app/wizard/reducer/index.ts index 6961e5be9..e3defa126 100644 --- a/ui/src/app/wizard/reducer/index.ts +++ b/ui/src/app/wizard/reducer/index.ts @@ -60,7 +60,8 @@ export const getSplitSchema = (schema: any, step: WizardStep) => { const keys = Object.keys(schema.properties).filter(key => step.fields.indexOf(key) > -1); const required = (schema.required || []).filter(val => keys.indexOf(val) > -1); let s: any = { - ...schema, + type: schema.type, + definitions: schema.definitions, properties: { ...keys.reduce( (properties, key) => ({ ...properties, [key]: schema.properties[key] }) , {}) } diff --git a/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json b/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json index 69adf788d..9e1d37308 100644 --- a/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json +++ b/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json @@ -50,6 +50,7 @@ ] }, { + "type": "group-lg", "fields": [ "xmlId", "metadataURL", diff --git a/ui/src/assets/schema/source/metadata-source.json b/ui/src/assets/schema/source/metadata-source.json index f8aa3c9a0..4784deeea 100644 --- a/ui/src/assets/schema/source/metadata-source.json +++ b/ui/src/assets/schema/source/metadata-source.json @@ -8,17 +8,22 @@ "entityId": { "title": "label.entity-id", "description": "tooltip.entity-id", - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 255 }, "serviceProviderName": { "title": "label.service-provider-name", "description": "tooltip.service-provider-name", - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 255 }, "serviceEnabled": { "title": "label.enable-this-service-upon-saving", "description": "tooltip.enable-this-service-upon-saving", - "type": "boolean" + "type": "boolean", + "default": false }, "organization": { "type": "object", @@ -119,13 +124,15 @@ "title": "label.logo-height", "description": "tooltip.mdui-logo-height", "min": 0, - "type": "integer" + "type": "integer", + "default": 0 }, "logoWidth": { "title": "label.logo-width", "description": "tooltip.mdui-logo-width", "min": 0, - "type": "integer" + "type": "integer", + "default": 0 } } }, @@ -251,6 +258,9 @@ ] } ], + "required": [ + "nameIdFormats" + ], "properties": { "protocolSupportEnum": { "title": "label.protocol-support-enumeration", @@ -361,16 +371,25 @@ "definitions": { "Contact": { "type": "object", + "required": [ + "name", + "type", + "emailAddress" + ], "properties": { "name": { "title": "label.contact-name", "description": "tooltip.contact-name", - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 255 }, "type": { "title": "label.contact-type", "description": "tooltip.contact-type", "type": "string", + "widget": "select", + "minLength": 1, "oneOf": [ { "enum": [ @@ -402,7 +421,9 @@ "title": "label.contact-email-address", "description": "tooltip.contact-email", "type": "string", - "pattern": "^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$" + "pattern": "^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$", + "minLength": 1, + "maxLength": 255 } } }, @@ -411,16 +432,24 @@ "widget": { "id": "fieldset" }, + "required": [ + "name", + "type", + "value" + ], "properties": { "name": { "title": "label.certificate-name-display-only", "description": "tooltip.certificate-name", - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 255 }, "type": { "title": "label.type", "description": "tooltip.certificate-type", "type": "string", + "widget": "radio", "oneOf": [ { "enum": [ @@ -446,7 +475,8 @@ "value": { "title": "label.certificate", "description": "tooltip.certificate", - "type": "string" + "type": "string", + "minLength": 1 } } }, @@ -460,7 +490,9 @@ "widget": { "id": "string", "help": "message.valid-url" - } + }, + "minLength": 1, + "maxLength": 255 }, "binding": { "title": "label.assertion-consumer-service-location-binding", @@ -496,13 +528,17 @@ "uniqueItems": true, "items": { "type": "string", - "widget": "datalist", - "data": [ - "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", - "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", - "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", - "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" - ] + "minLength": 1, + "maxLength": 255, + "widget": { + "id": "datalist", + "data": [ + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", + "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ] + } }, "default": null }, @@ -515,6 +551,8 @@ "items": { "type": "string", "title": "label.authentication-method", + "minLength": 1, + "maxLength": 255, "widget": { "id": "datalist", "data": [ @@ -542,16 +580,23 @@ ] } ], + "required": [ + "url", + "bindingType" + ], "properties": { "url": { "title": "label.url", "description": "tooltip.url", - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 255 }, "bindingType": { "title": "label.binding-type", "description": "tooltip.binding-type", "type": "string", + "widget": "select", "oneOf": [ { "enum": [