diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityAttributesFiltersUiDefinitionController.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityAttributesFiltersUiDefinitionController.groovy new file mode 100644 index 000000000..ffd3ee3ab --- /dev/null +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityAttributesFiltersUiDefinitionController.groovy @@ -0,0 +1,61 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller + +import com.fasterxml.jackson.databind.ObjectMapper +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocationRegistry +import edu.internet2.tier.shibboleth.admin.ui.service.JsonSchemaBuilderService +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +import javax.annotation.PostConstruct + +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.entityAttributesFiltersSchema +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR + +/** + * Controller implementing REST resource responsible for exposing structure definition for metadata sources user + * interface in terms of JSON schema. + * + * @author Dmitriy Kopylenko + * @author Bill Smith (wsmith@unicon.net) + */ +@RestController +@RequestMapping('/api/ui/EntityAttributesFilters') +class EntityAttributesFiltersUiDefinitionController { + + @Autowired + JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry + + JsonSchemaResourceLocation jsonSchemaLocation + + @Autowired + ObjectMapper jacksonObjectMapper + + @Autowired + JsonSchemaBuilderService jsonSchemaBuilderService + + @GetMapping + ResponseEntity getUiDefinitionJsonSchema() { + try { + def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaLocation.url, Map) + jsonSchemaBuilderService.addReleaseAttributesToJson(parsedJson['properties']['attributeRelease']['widget']) + jsonSchemaBuilderService.addRelyingPartyOverridesToJson(parsedJson['properties']['relyingPartyOverrides']) + jsonSchemaBuilderService.addRelyingPartyOverridesCollectionDefinitionsToJson(parsedJson["definitions"]) + return ResponseEntity.ok(parsedJson) + } + catch (Exception e) { + e.printStackTrace() + return ResponseEntity.status(INTERNAL_SERVER_ERROR) + .body([jsonParseError : e.getMessage(), + sourceUiSchemaDefinitionFile: this.jsonSchemaLocation.url]) + } + } + + @PostConstruct + void init() { + this.jsonSchemaLocation = entityAttributesFiltersSchema(this.jsonSchemaResourceLocationRegistry); + } +} diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy index 1425b5af1..5e8710220 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy @@ -1,11 +1,9 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import com.fasterxml.jackson.databind.ObjectMapper -import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration -import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocationRegistry -import org.springframework.beans.factory.BeanInitializationException +import edu.internet2.tier.shibboleth.admin.ui.service.JsonSchemaBuilderService import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping @@ -15,7 +13,6 @@ import org.springframework.web.bind.annotation.RestController import javax.annotation.PostConstruct import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.metadataSourcesSchema -import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.ShemaType.METADATA_SOURCES import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR /** @@ -38,15 +35,15 @@ class MetadataSourcesUiDefinitionController { ObjectMapper jacksonObjectMapper @Autowired - CustomPropertiesConfiguration customPropertiesConfiguration + JsonSchemaBuilderService jsonSchemaBuilderService @GetMapping ResponseEntity getUiDefinitionJsonSchema() { try { def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaLocation.url, Map) - addReleaseAttributesToJson(parsedJson['properties']['attributeRelease']['widget']) - addRelyingPartyOverridesToJson(parsedJson['properties']['relyingPartyOverrides']) - addRelyingPartyOverridesCollectionDefinitions(parsedJson["definitions"]) + jsonSchemaBuilderService.addReleaseAttributesToJson(parsedJson['properties']['attributeRelease']['widget']) + jsonSchemaBuilderService.addRelyingPartyOverridesToJson(parsedJson['properties']['relyingPartyOverrides']) + jsonSchemaBuilderService.addRelyingPartyOverridesCollectionDefinitionsToJson(parsedJson["definitions"]) return ResponseEntity.ok(parsedJson) } catch (Exception e) { @@ -61,52 +58,4 @@ class MetadataSourcesUiDefinitionController { void init() { this.jsonSchemaLocation = metadataSourcesSchema(this.jsonSchemaResourceLocationRegistry); } - - private void addReleaseAttributesToJson(Object json) { - json['data'] = customPropertiesConfiguration.getAttributes().collect { - [key: it['name'], label: it['displayName']] - } - } - - private void addRelyingPartyOverridesToJson(Object json) { - def properties = [:] - customPropertiesConfiguration.getOverrides().each { - def property - if (it['displayType'] == 'list' - || it['displayType'] == 'set') { - property = [$ref: '#/definitions/' + it['name']] - } else { - property = - [title : it['displayName'], - description: it['helpText'], - type : it['displayType'], - default : it['defaultValue']] - } - properties[(String) it['name']] = property - } - json['properties'] = properties - } - - private void addRelyingPartyOverridesCollectionDefinitions(Object json) { - customPropertiesConfiguration.getOverrides().stream().filter { - it -> it['displayType'] && (it['displayType'] == 'list' || it['displayType'] == 'set') - }.each { - def definition = [title : it['displayName'], - description: it['helpText'], - type : 'array', - default : null] - if (it['displayType'] == 'set') { - definition['uniqueItems'] = true - } else if (it['displayType'] == 'list') { - definition['uniqueItems'] = false - } - def items = [type : 'string', - minLength: '1', // TODO: should this be configurable? - maxLength: '255'] //TODO: or this? - items.widget = [id: 'datalist', data: it['defaultValues']] - - definition['items'] = items - json[(String) it['name']] = definition - } - } } diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy index cf342eef9..712142172 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy @@ -2,7 +2,6 @@ package edu.internet2.tier.shibboleth.admin.ui.jsonschema import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation import mjson.Json -import org.springframework.beans.factory.BeanInitializationException import org.springframework.beans.factory.annotation.Autowired import org.springframework.core.MethodParameter import org.springframework.http.HttpInputMessage @@ -18,7 +17,6 @@ import javax.annotation.PostConstruct import java.lang.reflect.Type import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.metadataSourcesSchema -import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.ShemaType.METADATA_SOURCES /** * Controller advice implementation for validating relying party overrides payload coming from UI layer diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JsonSchemaBuilderService.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JsonSchemaBuilderService.groovy new file mode 100644 index 000000000..8af0be7d3 --- /dev/null +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JsonSchemaBuilderService.groovy @@ -0,0 +1,61 @@ +package edu.internet2.tier.shibboleth.admin.ui.service + +import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration +import org.springframework.beans.factory.annotation.Autowired + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +class JsonSchemaBuilderService { + + @Autowired + CustomPropertiesConfiguration customPropertiesConfiguration + + void addReleaseAttributesToJson(Object json) { + json['data'] = customPropertiesConfiguration.getAttributes().collect { + [key: it['name'], label: it['displayName']] + } + } + + void addRelyingPartyOverridesToJson(Object json) { + def properties = [:] + customPropertiesConfiguration.getOverrides().each { + def property + if (it['displayType'] == 'list' + || it['displayType'] == 'set') { + property = [$ref: '#/definitions/' + it['name']] + } else { + property = + [title : it['displayName'], + description: it['helpText'], + type : it['displayType'], + default : it['defaultValue']] + } + properties[(String) it['name']] = property + } + json['properties'] = properties + } + + void addRelyingPartyOverridesCollectionDefinitionsToJson(Object json) { + customPropertiesConfiguration.getOverrides().stream().filter { + it -> it['displayType'] && (it['displayType'] == 'list' || it['displayType'] == 'set') + }.each { + def definition = [title : it['displayName'], + description: it['helpText'], + type : 'array', + default : null] + if (it['displayType'] == 'set') { + definition['uniqueItems'] = true + } else if (it['displayType'] == 'list') { + definition['uniqueItems'] = false + } + def items = [type : 'string', + minLength: '1', // TODO: should this be configurable? + maxLength: '255'] //TODO: or this? + items.widget = [id: 'datalist', data: it['defaultValues']] + + definition['items'] = items + json[(String) it['name']] = definition + } + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java index 8d70fa410..1bf2745c6 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java @@ -1,8 +1,8 @@ package edu.internet2.tier.shibboleth.admin.ui.configuration; import com.fasterxml.jackson.databind.ObjectMapper; -import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation; import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocationRegistry; +import edu.internet2.tier.shibboleth.admin.ui.service.JsonSchemaBuilderService; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -10,8 +10,8 @@ import org.springframework.core.io.ResourceLoader; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.*; -import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.ShemaType.ENTITY_ATTRIBUTES_FILTERS; -import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.ShemaType.METADATA_SOURCES; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ENTITY_ATTRIBUTES_FILTERS; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.METADATA_SOURCES; /** * @author Dmitriy Kopylenko @@ -47,4 +47,9 @@ public JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry(Res .build()); } + + @Bean + public JsonSchemaBuilderService jsonSchemaBuilderService() { + return new JsonSchemaBuilderService(); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/InMemoryJsonSchemaResourceLocationRegistry.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/InMemoryJsonSchemaResourceLocationRegistry.java index 56d6985bd..2840619b2 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/InMemoryJsonSchemaResourceLocationRegistry.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/InMemoryJsonSchemaResourceLocationRegistry.java @@ -14,18 +14,18 @@ */ class InMemoryJsonSchemaResourceLocationRegistry implements JsonSchemaResourceLocationRegistry { - private Map schemaLocations = - new EnumMap<>(JsonSchemaResourceLocation.ShemaType.class); + private Map schemaLocations = + new EnumMap<>(JsonSchemaResourceLocation.SchemaType.class); @Override - public JsonSchemaResourceLocationRegistry register(JsonSchemaResourceLocation.ShemaType type, JsonSchemaResourceLocation location) { + public JsonSchemaResourceLocationRegistry register(JsonSchemaResourceLocation.SchemaType type, JsonSchemaResourceLocation location) { this.schemaLocations.put(type, location); return this; } @Override - public Optional lookup(JsonSchemaResourceLocation.ShemaType type) { + public Optional lookup(JsonSchemaResourceLocation.SchemaType type) { return Optional.ofNullable(this.schemaLocations.get(type)); } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java index 9208a1630..e04dd72de 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.jsonschema; -import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.ShemaType.METADATA_SOURCES; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ENTITY_ATTRIBUTES_FILTERS; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.METADATA_SOURCES; /** * Utility methods for common JSON schema types lookups. @@ -21,4 +22,17 @@ public static JsonSchemaResourceLocation metadataSourcesSchema(JsonSchemaResourc .lookup(METADATA_SOURCES) .orElseThrow(() -> new IllegalStateException("JSON schema resource location for metadata sources is not registered.")); } + + /** + * Searches entity attributes filters JSON schema resource location object in the given location registry. + * + * @param resourceLocationRegistry + * @returnentity attributes filters JSON schema resource location object + * @throws IllegalStateException if schema is not found in the given registry + */ + public static JsonSchemaResourceLocation entityAttributesFiltersSchema(JsonSchemaResourceLocationRegistry resourceLocationRegistry) { + return resourceLocationRegistry + .lookup(ENTITY_ATTRIBUTES_FILTERS) + .orElseThrow(() -> new IllegalStateException("JSON schema resource location for entity attributes filters is not registered.")); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java index 0db5984bd..f08ac6069 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java @@ -90,7 +90,7 @@ public static JsonSchemaResourceLocation newSchemaLocation(String jsonSchemaLoca } } - public enum ShemaType { + public enum SchemaType { METADATA_SOURCES, ENTITY_ATTRIBUTES_FILTERS } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocationRegistry.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocationRegistry.java index 0882527d8..e5a6e88c5 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocationRegistry.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocationRegistry.java @@ -16,7 +16,7 @@ public interface JsonSchemaResourceLocationRegistry { * @param type of JSON schema * @param location of JSON schema resource */ - JsonSchemaResourceLocationRegistry register(JsonSchemaResourceLocation.ShemaType type, JsonSchemaResourceLocation location); + JsonSchemaResourceLocationRegistry register(JsonSchemaResourceLocation.SchemaType type, JsonSchemaResourceLocation location); /** * Look up json schema resource location by given schema type. @@ -24,7 +24,7 @@ public interface JsonSchemaResourceLocationRegistry { * @param type type of JSON schema * @return optional location of JSON schema resource */ - Optional lookup(JsonSchemaResourceLocation.ShemaType type); + Optional lookup(JsonSchemaResourceLocation.SchemaType type); /** * Factory method. diff --git a/backend/src/main/resources/entity-attributes-filters-ui-schema.json b/backend/src/main/resources/entity-attributes-filters-ui-schema.json index 2350a345c..5d71f0ba8 100644 --- a/backend/src/main/resources/entity-attributes-filters-ui-schema.json +++ b/backend/src/main/resources/entity-attributes-filters-ui-schema.json @@ -222,5 +222,7 @@ "attributeRelease" ] } - ] + ], + "definitions": { + } } \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_en.properties b/backend/src/main/resources/i18n/messages_en.properties index 7ae90de03..3d4670252 100644 --- a/backend/src/main/resources/i18n/messages_en.properties +++ b/backend/src/main/resources/i18n/messages_en.properties @@ -433,7 +433,7 @@ tooltip.backing-file=Specifies where the backing file is located. If the remote tooltip.backup-file-init-refresh-delay=Delay duration after which to schedule next HTTP refresh when initialized from the backing file. tooltip.require-valid-metadata=Whether candidate metadata found by the resolver must be valid in order to be returned (where validity is implementation specific, but in SAML cases generally depends on a validUntil attribute.) If this flag is true, then invalid candidate metadata will not be returned. tooltip.fail-fast-init=Whether to fail initialization of the underlying MetadataResolverService (and possibly the IdP as a whole) if the initialization of a metadata provider fails. When false, the IdP may start, and will continue to attempt to reload valid metadata if configured to do so, but operations that require valid metadata will fail until it does. -tooltip.use-default-predicate-reg=Whether to fail initialization of the underlying MetadataResolverService (and possibly the IdP as a whole) if the initialization of a metadata provider fails. When false, the IdP may start, and will continue to attempt to reload valid metadata if configured to do so, but operations that require valid metadata will fail until it does. +tooltip.use-default-predicate-reg=Flag which determines whether the default CriterionPredicateRegistry will be used if a custom one is not supplied explicitly. tooltip.satisfy-any-predicates=Flag which determines whether predicates used in filtering are connected by a logical 'OR' (true) or by logical 'AND' (false). tooltip.enable-provider-upon-saving=Enable Metadata Provider upon saving? diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests.groovy index 979f72a63..c881d81c7 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests.groovy @@ -12,7 +12,8 @@ import org.springframework.test.context.ActiveProfiles import spock.lang.Specification import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.* -import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.ShemaType.METADATA_SOURCES +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.ENTITY_ATTRIBUTES_FILTERS +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.METADATA_SOURCES /** * @author Dmitriy Kopylenko @@ -49,6 +50,12 @@ class BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests extends Speci .jacksonMapper(jacksonMapper) .detectMalformedJson(false) .build()) + .register(ENTITY_ATTRIBUTES_FILTERS, JsonSchemaLocationBuilder.with() + .jsonSchemaLocation('classpath:entity-attributes-filters-ui-schema.json') + .resourceLoader(resourceLoader) + .jacksonMapper(jacksonMapper) + .detectMalformedJson(false) + .build()) } } } \ No newline at end of file diff --git a/ui/src/app/metadata/domain/component/unsaved-entity.dialog.html b/ui/src/app/metadata/domain/component/unsaved-entity.dialog.html index 6c210c045..5966b73c8 100644 --- a/ui/src/app/metadata/domain/component/unsaved-entity.dialog.html +++ b/ui/src/app/metadata/domain/component/unsaved-entity.dialog.html @@ -1,14 +1,28 @@ - +
diff --git a/ui/src/app/metadata/domain/component/unsaved-entity.dialog.ts b/ui/src/app/metadata/domain/component/unsaved-entity.dialog.ts index 7efe195c8..10180a2a8 100644 --- a/ui/src/app/metadata/domain/component/unsaved-entity.dialog.ts +++ b/ui/src/app/metadata/domain/component/unsaved-entity.dialog.ts @@ -10,6 +10,8 @@ import { Subject } from 'rxjs/Subject'; export class UnsavedEntityComponent { readonly subject: Subject = new Subject(); + @Input() message; + constructor( public activeModal: NgbActiveModal ) { } diff --git a/ui/src/app/metadata/provider/container/provider-wizard-step.component.ts b/ui/src/app/metadata/provider/container/provider-wizard-step.component.ts index 05d121bba..9ff4ded23 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard-step.component.ts +++ b/ui/src/app/metadata/provider/container/provider-wizard-step.component.ts @@ -1,6 +1,6 @@ import { Component, OnDestroy } from '@angular/core'; import { Observable, Subject } from 'rxjs'; -import { withLatestFrom, map, distinctUntilChanged, skipWhile } from 'rxjs/operators'; +import { withLatestFrom, map, distinctUntilChanged, skipWhile, filter } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import * as fromProvider from '../reducer'; @@ -42,7 +42,9 @@ export class ProviderWizardStepComponent implements OnDestroy { constructor( private store: Store, ) { - this.schema$ = this.store.select(fromWizard.getParsedSchema); + this.schema$ = this.store.select(fromWizard.getSchema).pipe( + filter(s => s && Object.keys(s.properties).length > 0) + ); this.definition$ = this.store.select(fromWizard.getWizardDefinition); this.changes$ = this.store.select(fromProvider.getEntityChanges); diff --git a/ui/src/app/metadata/provider/model/base.provider.form.ts b/ui/src/app/metadata/provider/model/base.provider.form.ts index 459eaa956..ecbcab666 100644 --- a/ui/src/app/metadata/provider/model/base.provider.form.ts +++ b/ui/src/app/metadata/provider/model/base.provider.form.ts @@ -1,6 +1,5 @@ import { Wizard } from '../../../wizard/model'; import { BaseMetadataProvider } from '../../domain/model/providers'; -import { UriValidator } from '../../../shared/validation/uri.validator'; export const BaseMetadataProviderEditor: Wizard = { label: 'BaseMetadataProvider', @@ -30,14 +29,6 @@ export const BaseMetadataProviderEditor: Wizard = { params: [value] } : null; return err; - }, - '/metadataURL': (value, property, form) => { - return !UriValidator.isUri(value) ? { - code: 'INVALID_URI', - path: `#${property.path}`, - message: 'message.uri-valid-format', - params: [value] - } : null; } }; return validators; diff --git a/ui/src/app/metadata/provider/model/file-backed-http.provider.form.spec.ts b/ui/src/app/metadata/provider/model/file-backed-http.provider.form.spec.ts index aa07918f0..68e19359b 100644 --- a/ui/src/app/metadata/provider/model/file-backed-http.provider.form.spec.ts +++ b/ui/src/app/metadata/provider/model/file-backed-http.provider.form.spec.ts @@ -113,8 +113,8 @@ describe('FileBackedHttpMetadataProviderWizard', () => { expect(Object.keys(getValidators([]))).toEqual([ '/', '/name', - '/metadataURL', - '/xmlId' + '/xmlId', + '/metadataURL' ]); }); }); diff --git a/ui/src/app/metadata/provider/model/file-backed-http.provider.form.ts b/ui/src/app/metadata/provider/model/file-backed-http.provider.form.ts index 2c8103438..8dbe80204 100644 --- a/ui/src/app/metadata/provider/model/file-backed-http.provider.form.ts +++ b/ui/src/app/metadata/provider/model/file-backed-http.provider.form.ts @@ -1,6 +1,7 @@ import { Wizard } from '../../../wizard/model'; import { FileBackedHttpMetadataProvider } from '../../domain/model/providers/file-backed-http-metadata-provider'; import { BaseMetadataProviderEditor } from './base.provider.form'; +import UriValidator from '../../../shared/validation/uri.validator'; export const FileBackedHttpMetadataProviderWizard: Wizard = { ...BaseMetadataProviderEditor, @@ -17,6 +18,15 @@ export const FileBackedHttpMetadataProviderWizard: Wizard { + return !UriValidator.isUri(value) ? { + code : 'INVALID_URI', + path: `#${property.path}`, + message: 'message.uri-valid-format', + params: [value] + } : null; + }; + return validators; }, steps: [ diff --git a/ui/src/app/metadata/provider/model/provider.form.ts b/ui/src/app/metadata/provider/model/provider.form.ts index 28e5794c5..71ba6174b 100644 --- a/ui/src/app/metadata/provider/model/provider.form.ts +++ b/ui/src/app/metadata/provider/model/provider.form.ts @@ -17,6 +17,16 @@ export const MetadataProviderWizard: Wizard = { fields: [ 'name', '@type' + ], + fieldsets: [ + { + type: 'section', + class: ['col-12'], + fields: [ + 'name', + '@type' + ] + } ] } ] as WizardStep[] 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 bf886ae6d..ba2221fda 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts +++ b/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts @@ -28,7 +28,7 @@ import { LoadSchemaRequest } from '../../../wizard/action/wizard.action'; import { UnsavedEntityComponent } from '../../domain/component/unsaved-entity.dialog'; import { ModalService } from '../../../core/service/modal.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { UpdateChanges } from '../action/entity.action'; +import { UpdateChanges, Clear } from '../action/entity.action'; @Component({ selector: 'resolver-wizard-page', @@ -127,6 +127,8 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat } )) ); + + this.changes$.pipe(takeUntil(this.ngUnsubscribe)).subscribe(c => this.changes = c); } next(): void { @@ -170,12 +172,15 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat currentState: RouterStateSnapshot, nextState: RouterStateSnapshot ): Observable { - if (nextState.url.match('wizard')) { return of(true); } + if (nextState.url.match('blank')) { return of(true); } if (Object.keys(this.changes).length > 0) { let modal = this.modalService.open(UnsavedEntityComponent); - modal.componentInstance.action = new UpdateChanges(this.latest); + modal.componentInstance.message = 'resolver'; modal.result.then( - () => this.router.navigate([nextState.url]), + () => { + this.store.dispatch(new Clear()); + this.router.navigate([nextState.url]); + }, () => console.warn('denied') ); } diff --git a/ui/src/app/metadata/resolver/resolver.routing.ts b/ui/src/app/metadata/resolver/resolver.routing.ts index 5312f5537..1d080bd5f 100644 --- a/ui/src/app/metadata/resolver/resolver.routing.ts +++ b/ui/src/app/metadata/resolver/resolver.routing.ts @@ -26,7 +26,9 @@ export const ResolverRoutes: Routes = [ { path: 'blank/:index', component: ResolverWizardComponent, - canDeactivate: [], + canDeactivate: [ + CanDeactivateGuard + ], children: [ { path: '', diff --git a/ui/src/app/wizard/reducer/index.ts b/ui/src/app/wizard/reducer/index.ts index 0d7176565..56054616c 100644 --- a/ui/src/app/wizard/reducer/index.ts +++ b/ui/src/app/wizard/reducer/index.ts @@ -61,12 +61,15 @@ export const getSplitSchema = (schema: any, step: WizardStep) => { const required = (schema.required || []).filter(val => keys.indexOf(val) > -1); let s: any = { type: schema.type, - definitions: schema.definitions, properties: { ...keys.reduce( (properties, key) => ({ ...properties, [key]: schema.properties[key] }) , {}) } }; + if (schema.definitions) { + s.definitions = schema.definitions; + } + if (required && required.length) { s.required = required; }