diff --git a/ui/package.json b/ui/package.json
index 2964e049d..af5b3120b 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -26,7 +26,6 @@
"react-dom": "^17.0.2",
"react-hook-form": "^7.5.2",
"react-infinite-scroll-component": "^6.1.0",
- "react-jsonschema-form-layout-grid": "^2.1.0",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"react-scroll": "^1.8.2",
diff --git a/ui/public/assets/schema/provider/filebacked-http.schema.json b/ui/public/assets/schema/provider/filebacked-http.schema.json
index 5c921eec6..5940a8794 100644
--- a/ui/public/assets/schema/provider/filebacked-http.schema.json
+++ b/ui/public/assets/schema/provider/filebacked-http.schema.json
@@ -342,6 +342,10 @@
"id": "fieldset"
},
"properties": {
+ "@type": {
+ "type": "string",
+ "default": "RequiredValidUntil"
+ },
"maxValidityInterval": {
"title": "label.max-validity-interval",
"description": "tooltip.max-validity-interval",
@@ -358,6 +362,10 @@
"id": "fieldset"
},
"properties": {
+ "@type": {
+ "type": "string",
+ "default": "SignatureValidation"
+ },
"requireSignedRoot": {
"title": "label.require-signed-root",
"description": "tooltip.require-signed-root",
@@ -401,14 +409,27 @@
"id": "fieldset"
},
"properties": {
+ "@type": {
+ "type": "string",
+ "default": "EntityRoleWhiteList"
+ },
"retainedRoles": {
"title": "label.retained-roles",
"description": "tooltip.retained-roles",
"type": "array",
"items": {
- "enum": ["SPSSODescriptor", "AttributeAuthorityDescriptor"],
- "enumNames": ["value.spdescriptor", "value.attr-auth-descriptor"],
- "type": "string"
+ "widget": {
+ "id": "select"
+ },
+ "type": "string",
+ "enum": [
+ "SPSSODescriptor",
+ "AttributeAuthorityDescriptor"
+ ],
+ "enumNames": [
+ "value.spdescriptor",
+ "value.attr-auth-descriptor"
+ ]
}
},
"removeRolelessEntityDescriptors": {
diff --git a/ui/src/app/dashboard/view/ProvidersTab.js b/ui/src/app/dashboard/view/ProvidersTab.js
index 1bf1c4629..123e54f41 100644
--- a/ui/src/app/dashboard/view/ProvidersTab.js
+++ b/ui/src/app/dashboard/view/ProvidersTab.js
@@ -14,7 +14,9 @@ export function ProvidersTab () {
const [providers, setProviders] = React.useState([]);
- const { get, response } = useMetadataEntities('provider');
+ const { get, response } = useMetadataEntities('provider', {
+ cachePolicy: 'no-cache'
+ });
async function loadProviders() {
const providers = await get('')
diff --git a/ui/src/app/form/component/templates/FieldTemplate.js b/ui/src/app/form/component/templates/FieldTemplate.js
index 2e196376f..fea2b4831 100644
--- a/ui/src/app/form/component/templates/FieldTemplate.js
+++ b/ui/src/app/form/component/templates/FieldTemplate.js
@@ -31,13 +31,13 @@ export function FieldTemplate ({
return (
0 ? 'sr-only' : ''}`}>
- {error}
+ {error}
);
})}
- )}
+ )}
{rawHelp && rawErrors.length < 1 && (
0 ? "text-danger" : "text-muted"} id={id}>
diff --git a/ui/src/app/form/component/templates/ObjectFieldTemplate.js b/ui/src/app/form/component/templates/ObjectFieldTemplate.js
index c925802f4..0fe0089e4 100644
--- a/ui/src/app/form/component/templates/ObjectFieldTemplate.js
+++ b/ui/src/app/form/component/templates/ObjectFieldTemplate.js
@@ -16,7 +16,7 @@ const ObjectFieldTemplate = ({
idSchema,
schema,
hidden,
- formContext
+ ...props
}) => {
const displayTitle = (uiSchema["ui:title"] || (title && schema.title));
diff --git a/ui/src/app/metadata/NewProvider.js b/ui/src/app/metadata/NewProvider.js
index 3cf143726..75a521e01 100644
--- a/ui/src/app/metadata/NewProvider.js
+++ b/ui/src/app/metadata/NewProvider.js
@@ -1,10 +1,14 @@
import React from 'react';
import Translate from '../i18n/components/translate';
import { MetadataSchema } from './hoc/MetadataSchema';
+import { useMetadataProviderTypes } from './hooks/api';
import { MetadataWizard } from './view/MetadataWizard';
+import { MetadataSchemaSelector } from './wizard/MetadataSchemaSelector';
export function NewProvider() {
+ const { data } = useMetadataProviderTypes({}, []);
+
return (
@@ -16,9 +20,18 @@ export function NewProvider() {
-
-
-
+
+ {(data, onRestart) =>
+
+
+
+ }
+
diff --git a/ui/src/app/metadata/NewSource.js b/ui/src/app/metadata/NewSource.js
index 146d041b7..7139a453c 100644
--- a/ui/src/app/metadata/NewSource.js
+++ b/ui/src/app/metadata/NewSource.js
@@ -78,7 +78,7 @@ export function NewSource() {
- { setShowNav(s) }} />
+ { setShowNav(s) }} />
} />
diff --git a/ui/src/app/metadata/domain/index.js b/ui/src/app/metadata/domain/index.js
index 16d176953..70edb596c 100644
--- a/ui/src/app/metadata/domain/index.js
+++ b/ui/src/app/metadata/domain/index.js
@@ -1,5 +1,5 @@
import { MetadataFilterEditorTypes } from './filter';
-import { MetadataProviderEditorTypes } from './provider';
+import { MetadataProviderEditorTypes, MetadataProviderWizardTypes } from './provider';
import { SourceEditor, SourceWizard } from "./source/SourceDefinition";
export const editors = {
@@ -13,12 +13,15 @@ export const wizards = {
export const ProviderEditorTypes = [
...MetadataProviderEditorTypes
];
+export const ProviderWizardTypes = [
+ ...MetadataProviderWizardTypes
+];
export const FilterEditorTypes = [
...MetadataFilterEditorTypes
];
export const getWizard = (type) =>
- ProviderEditorTypes.find(def => def.type === type) ||
+ ProviderWizardTypes.find(def => def.type === type) ||
FilterEditorTypes.find(def => def.type === type) ||
SourceWizard;
diff --git a/ui/src/app/metadata/domain/provider/BaseProviderDefinition.js b/ui/src/app/metadata/domain/provider/BaseProviderDefinition.js
index 3148c1683..6fe346727 100644
--- a/ui/src/app/metadata/domain/provider/BaseProviderDefinition.js
+++ b/ui/src/app/metadata/domain/provider/BaseProviderDefinition.js
@@ -3,6 +3,22 @@ import { DurationOptions } from '../data';
export const BaseProviderDefinition = {
schemaPreprocessor: metadataFilterProcessor,
+ validator: (data = [], current = { resourceId: null }) => {
+ const providers = data.filter(p => p.resourceId !== current.resourceId);
+ const names = providers.map(s => s.name);
+ const ids = providers.map(s => s.xmlId);
+
+ return (formData, errors) => {
+ if (names.indexOf(formData.name) > -1) {
+ errors.name.addError('message.name-must-be-unique');
+ }
+
+ if (ids.indexOf(formData.xmlId) > -1) {
+ errors.xmlId.addError('message.id-unique');
+ }
+ return errors;
+ }
+ },
parser: (changes) => {
return (changes.metadataFilters ? ({
...changes,
@@ -14,9 +30,8 @@ export const BaseProviderDefinition = {
}) : changes)
},
formatter: (changes, schema) => {
-
const filterSchema = schema?.properties?.metadataFilters;
- if (!filterSchema) {
+ if (!filterSchema || !changes) {
return changes;
}
@@ -33,14 +48,13 @@ export const BaseProviderDefinition = {
return formatted;
},
display: (changes) => {
-
if (!changes.metadataFilters) {
return changes;
}
return {
...changes,
metadataFilters: {
- ...(changes.metadataFilters || []).reduce((collection, filter) => ({
+ ...changes.metadataFilters.reduce((collection, filter) => ({
...collection,
[filter['@type']]: filter
}), {})
@@ -51,7 +65,29 @@ export const BaseProviderDefinition = {
name: {
'ui:help': 'message.must-be-unique'
}
- }
+ },
+ steps: [
+ {
+ id: 'new',
+ label: 'label.select-metadata-provider-type',
+ index: 1,
+ initialValues: [],
+ fields: [
+ 'name',
+ '@type'
+ ],
+ fieldsets: [
+ {
+ type: 'section',
+ class: ['col-12'],
+ fields: [
+ 'name',
+ '@type'
+ ]
+ }
+ ]
+ }
+ ]
}
export const HttpMetadataResolverAttributesSchema = {
diff --git a/ui/src/app/metadata/domain/provider/DynamicHttpMetadataProviderDefinition.js b/ui/src/app/metadata/domain/provider/DynamicHttpMetadataProviderDefinition.js
index e22122e6b..a8f304121 100644
--- a/ui/src/app/metadata/domain/provider/DynamicHttpMetadataProviderDefinition.js
+++ b/ui/src/app/metadata/domain/provider/DynamicHttpMetadataProviderDefinition.js
@@ -12,6 +12,7 @@ export const DynamicHttpMetadataProviderWizard = {
schema: '/assets/schema/provider/dynamic-http.schema.json',
// schema: `${API_BASE_PATH}/ui/MetadataResolver/DynamicHttpMetadataResolver`,
steps: [
+ ...BaseProviderDefinition.steps,
{
id: 'common',
label: 'label.common-attributes',
@@ -56,16 +57,15 @@ export const DynamicHttpMetadataProviderWizard = {
layout: {
groups: [
{
- size: 9,
+ size: 8,
classNames: 'bg-light border rounded px-4 pt-4 pb-3 mb-4',
fields: [
'name',
- '@type',
- 'enabled'
+ '@type'
]
},
{
- size: 9,
+ size: 8,
fields: [
'xmlId',
'requireValidMetadata',
@@ -74,22 +74,28 @@ export const DynamicHttpMetadataProviderWizard = {
]
},
{
- size: 9,
+ size: 8,
fields: [
'dynamicMetadataResolverAttributes'
],
},
{
- size: 9,
+ size: 8,
fields: [
'metadataFilters'
],
},
{
- size: 9,
+ size: 8,
fields: [
'httpMetadataResolverAttributes'
]
+ },
+ {
+ size: 8,
+ fields: [
+ 'enabled'
+ ]
}
]
},
@@ -172,36 +178,7 @@ export const DynamicHttpMetadataProviderEditor = {
'enabled',
'requireValidMetadata',
'failFastInitialization'
- ],
- fieldsets: [
- {
- type: 'section',
- class: ['mb-3'],
- fields: [
- 'name',
- '@type'
- ]
- },
- {
- type: 'group-lg',
- class: ['col-12'],
- fields: [
- 'xmlId',
- 'metadataRequestURLConstructionScheme',
- 'enabled',
- 'requireValidMetadata',
- 'failFastInitialization'
- ]
- }
- ],
- override: {
- '@type': {
- type: 'string',
- readOnly: true,
- widget: 'string',
- oneOf: [{ enum: ['DynamicHttpMetadataResolver'], description: 'value.dynamic-http-metadata-provider' }]
- }
- }
+ ]
},
{
id: 'dynamic',
diff --git a/ui/src/app/metadata/domain/provider/FileBackedHttpMetadataProviderDefinition.js b/ui/src/app/metadata/domain/provider/FileBackedHttpMetadataProviderDefinition.js
index d7faad4d7..fb4d3818d 100644
--- a/ui/src/app/metadata/domain/provider/FileBackedHttpMetadataProviderDefinition.js
+++ b/ui/src/app/metadata/domain/provider/FileBackedHttpMetadataProviderDefinition.js
@@ -9,6 +9,7 @@ export const FileBackedHttpMetadataProviderWizard = {
type: 'FileBackedHttpMetadataResolver',
schema: '/assets/schema/provider/filebacked-http.schema.json',
steps: [
+ ...BaseProviderDefinition.steps,
{
id: 'common',
label: 'label.common-attributes',
@@ -60,16 +61,15 @@ export const FileBackedHttpMetadataProviderWizard = {
layout: {
groups: [
{
- size: 9,
+ size: 8,
classNames: 'bg-light border rounded px-4 pt-4 pb-3 mb-4',
fields: [
'name',
- '@type',
- 'enabled'
+ '@type'
]
},
{
- size: 9,
+ size: 8,
fields: [
'xmlId',
'metadataURL',
@@ -83,22 +83,28 @@ export const FileBackedHttpMetadataProviderWizard = {
]
},
{
- size: 9,
+ size: 8,
fields: [
'reloadableMetadataResolverAttributes'
],
},
{
- size: 9,
+ size: 8,
fields: [
'metadataFilters'
],
},
{
- size: 9,
+ size: 8,
fields: [
'httpMetadataResolverAttributes'
]
+ },
+ {
+ size: 8,
+ fields: [
+ 'enabled'
+ ]
}
]
},
diff --git a/ui/src/app/metadata/domain/provider/FileSystemMetadataProviderDefinition.js b/ui/src/app/metadata/domain/provider/FileSystemMetadataProviderDefinition.js
index 5fb5dc68d..7c72764e0 100644
--- a/ui/src/app/metadata/domain/provider/FileSystemMetadataProviderDefinition.js
+++ b/ui/src/app/metadata/domain/provider/FileSystemMetadataProviderDefinition.js
@@ -10,6 +10,7 @@ export const FileSystemMetadataProviderWizard = {
schema: '/assets/schema/provider/file-system.schema.json',
// schema: `${API_BASE_PATH}/ui/MetadataResolver/FilesystemMetadataResolver`,
steps: [
+ ...BaseProviderDefinition.steps,
{
id: 'common',
label: 'label.common-attributes',
@@ -19,17 +20,6 @@ export const FileSystemMetadataProviderWizard = {
'xmlId',
'metadataFile',
'doInitialization'
- ],
- fieldsets: [
- {
- type: 'group-lg',
- class: ['col-12'],
- fields: [
- 'xmlId',
- 'metadataFile',
- 'doInitialization'
- ]
- }
]
},
{
@@ -39,15 +29,6 @@ export const FileSystemMetadataProviderWizard = {
initialValues: [],
fields: [
'reloadableMetadataResolverAttributes'
- ],
- fieldsets: [
- {
- type: 'group-lg',
- class: ['col-12'],
- fields: [
- 'reloadableMetadataResolverAttributes'
- ]
- }
]
},
{
@@ -57,15 +38,6 @@ export const FileSystemMetadataProviderWizard = {
initialValues: [],
fields: [
'enabled'
- ],
- fieldsets: [
- {
- type: 'group-lg',
- class: ['col-12'],
- fields: [
- 'enabled'
- ]
- }
]
}
],
@@ -73,16 +45,15 @@ export const FileSystemMetadataProviderWizard = {
layout: {
groups: [
{
- size: 9,
+ size: 8,
classNames: 'bg-light border rounded px-4 pt-4 pb-3 mb-4',
fields: [
'name',
- '@type',
- 'enabled'
+ '@type'
]
},
{
- size: 9,
+ size: 8,
fields: [
'xmlId',
'metadataFile',
@@ -90,10 +61,16 @@ export const FileSystemMetadataProviderWizard = {
]
},
{
- size: 9,
+ size: 8,
fields: [
'reloadableMetadataResolverAttributes'
]
+ },
+ {
+ size: 8,
+ fields: [
+ 'enabled'
+ ]
}
]
},
diff --git a/ui/src/app/metadata/domain/provider/LocalDynamicMetadataProviderDefinition.js b/ui/src/app/metadata/domain/provider/LocalDynamicMetadataProviderDefinition.js
index b6e3847f6..94635ddfc 100644
--- a/ui/src/app/metadata/domain/provider/LocalDynamicMetadataProviderDefinition.js
+++ b/ui/src/app/metadata/domain/provider/LocalDynamicMetadataProviderDefinition.js
@@ -11,6 +11,7 @@ export const LocalDynamicMetadataProviderWizard = {
schema: '/assets/schema/provider/local-dynamic.schema.json',
// schema: `${API_BASE_PATH}/ui/MetadataResolver/LocalDynamicMetadataResolver`,
steps: [
+ ...BaseProviderDefinition.steps,
{
id: 'common',
label: 'label.common-attributes',
@@ -19,16 +20,6 @@ export const LocalDynamicMetadataProviderWizard = {
fields: [
'xmlId',
'sourceDirectory'
- ],
- fieldsets: [
- {
- type: 'group-lg',
- class: ['col-12'],
- fields: [
- 'xmlId',
- 'sourceDirectory'
- ]
- }
]
},
{
@@ -38,15 +29,6 @@ export const LocalDynamicMetadataProviderWizard = {
initialValues: [],
fields: [
'dynamicMetadataResolverAttributes'
- ],
- fieldsets: [
- {
- type: 'group-lg',
- class: ['col-12'],
- fields: [
- 'dynamicMetadataResolverAttributes'
- ]
- }
]
},
{
@@ -56,15 +38,6 @@ export const LocalDynamicMetadataProviderWizard = {
initialValues: [],
fields: [
'enabled'
- ],
- fieldsets: [
- {
- type: 'group-lg',
- class: ['col-12'],
- fields: [
- 'enabled'
- ]
- }
]
}
],
@@ -72,26 +45,31 @@ export const LocalDynamicMetadataProviderWizard = {
layout: {
groups: [
{
- size: 9,
+ size: 8,
classNames: 'bg-light border rounded px-4 pt-4 pb-3 mb-4',
fields: [
'name',
- '@type',
- 'enabled'
+ '@type'
]
},
{
- size: 9,
+ size: 8,
fields: [
'xmlId',
'sourceDirectory'
]
},
{
- size: 9,
+ size: 8,
fields: [
'dynamicMetadataResolverAttributes'
],
+ },
+ {
+ size: 8,
+ fields: [
+ 'enabled'
+ ]
}
]
},
diff --git a/ui/src/app/metadata/domain/provider/utility/providerFilterProcessor.js b/ui/src/app/metadata/domain/provider/utility/providerFilterProcessor.js
index dc415bae1..f436b50ff 100644
--- a/ui/src/app/metadata/domain/provider/utility/providerFilterProcessor.js
+++ b/ui/src/app/metadata/domain/provider/utility/providerFilterProcessor.js
@@ -1,4 +1,5 @@
export const metadataFilterProcessor = (schema) => {
+ console.log(schema);
if (!schema) {
return null;
}
@@ -12,7 +13,7 @@ export const metadataFilterProcessor = (schema) => {
...schema.properties,
metadataFilters: {
type: 'object',
- properties: filters.items.reduce((collection, filterType) => ({
+ properties: filters?.items?.reduce((collection, filterType) => ({
...collection,
[filterType.$id]: filterType
}), {})
diff --git a/ui/src/app/metadata/domain/source/SourceDefinition.js b/ui/src/app/metadata/domain/source/SourceDefinition.js
index 1674e8dac..493658213 100644
--- a/ui/src/app/metadata/domain/source/SourceDefinition.js
+++ b/ui/src/app/metadata/domain/source/SourceDefinition.js
@@ -18,59 +18,32 @@ export const SourceBase = {
display: (changes) => changes,
- getValidators: (entityIdList) => {
- const validators = {
- '/': (value, property, form_current) => {
- let errors;
- // iterate all customer
- Object.keys(value).forEach((key) => {
- const item = value[key];
- const validatorKey = `/${key}`;
- const validator = validators.hasOwnProperty(validatorKey) ? validators[validatorKey] : null;
- const error = validator ? validator(item, form_current.getProperty(key), form_current) : null;
- if (error && error.invalidate) {
- errors = errors || [];
- errors.push(error);
- }
- });
- return errors;
- },
- '/entityId': (value, property, form) => {
- const err = entityIdList.indexOf(value) > -1 ? {
- code: 'INVALID_ID',
- path: `#${property.path}`,
- message: 'message.id-unique',
- params: [value],
- invalidate: true
- } : null;
- return err;
- },
- '/relyingPartyOverrides': (value, property, form) => {
- if (!value.signAssertion && value.dontSignResponse) {
- return {
- code: 'INVALID_SIGNING',
- path: `#${property.path}`,
- message: 'message.invalid-signing',
- params: [value],
- invalidate: false
- };
- }
- return null;
- },
- '/serviceProviderSsoDescriptor': (value, property, form) => {
- if (value.nameIdFormats && value.nameIdFormats.length && !value.protocolSupportEnum) {
- return {
- code: 'PROTOCOL_SUPPORT_ENUM_REQUIRED',
- path: `#${property.path}`,
- message: 'message.protocol-support-required',
- params: [value],
- invalidate: true
- };
- }
- return null;
+ validator: (data = [], current = {id: null}) => {
+
+ const sources = current ? data.filter(s => s.id !== current.id) : data;
+ const entityIds = sources.map(s => s.entityId);
+
+ console.log(sources);
+
+ return (formData, errors) => {
+ console.log(formData)
+ if (entityIds.indexOf(formData.entityId) > -1) {
+ errors.entityId.addError('message.id-unique');
}
- };
- return validators;
+
+ /*if (!formData?.relyingPartyOverrides?.signAssertion && formData?.relyingPartyOverrides?.dontSignResponse) {
+ errors = {
+ ...errors,
+ relyingPartyOverrides: {
+ dontSignResponse: {
+ __errors: ['message.invalid-signing'],
+ nonBlocking: true
+ }
+ }
+ };
+ }*/
+ return errors;
+ }
},
uiSchema: {
'ui:order': ['serviceProviderName', '*'],
diff --git a/ui/src/app/metadata/editor/MetadataEditor.js b/ui/src/app/metadata/editor/MetadataEditor.js
index 4e4e219b2..5792e74ee 100644
--- a/ui/src/app/metadata/editor/MetadataEditor.js
+++ b/ui/src/app/metadata/editor/MetadataEditor.js
@@ -10,24 +10,23 @@ import { MetadataDefinitionContext, MetadataSchemaContext } from '../hoc/Metadat
import { MetadataEditorForm } from './MetadataEditorForm';
import { MetadataEditorNav } from './MetadataEditorNav';
-import { useMetadataEntity } from '../hooks/api';
+import { useMetadataEntities, useMetadataEntity } from '../hooks/api';
+import { MetadataObjectContext } from '../hoc/MetadataSelector';
export function MetadataEditor () {
const { type, id, section } = useParams();
- const { put, response } = useMetadataEntity(type, {}, []);
+ const { put, response, saving } = useMetadataEntity(type, {}, []);
-
+ const { data } = useMetadataEntities(type, {}, []);
const history = useHistory();
const definition = React.useContext(MetadataDefinitionContext);
const schema = React.useContext(MetadataSchemaContext);
-
- const [invalid] = React.useState(false);
- const [saving] = React.useState(false);
+ const current = React.useContext(MetadataObjectContext);
const { state, dispatch } = React.useContext(MetadataFormContext);
- const { metadata, errors} = state;
+ const { metadata, errors } = state;
const onChange = (changes) => {
dispatch(setFormDataAction(changes.formData));
@@ -57,6 +56,9 @@ export function MetadataEditor () {
const [blocking, setBlocking] = React.useState(false);
+ const validator = definition.validator(data, current);
+
+ // console.log(errors);
return (
@@ -93,7 +95,7 @@ export function MetadataEditor () {
diff --git a/ui/src/app/metadata/editor/MetadataEditorForm.js b/ui/src/app/metadata/editor/MetadataEditorForm.js
index 0f948ec1b..6413cbedf 100644
--- a/ui/src/app/metadata/editor/MetadataEditorForm.js
+++ b/ui/src/app/metadata/editor/MetadataEditorForm.js
@@ -14,7 +14,7 @@ function ErrorListTemplate () {
return (<>>);
}
-export function MetadataEditorForm ({ metadata, definition, schema, current, onChange }) {
+export function MetadataEditorForm({ metadata, definition, schema, current, onChange, validator }) {
const [locked, setLocked] = React.useState(true);
@@ -61,7 +61,8 @@ export function MetadataEditorForm ({ metadata, definition, schema, current, onC
widgets={widgets}
liveValidate={true}
transformErrors={transformErrors}
- ErrorList={ErrorListTemplate}>
+ ErrorList={ErrorListTemplate}
+ validate={validator}>
<>>
diff --git a/ui/src/app/metadata/hoc/MetadataFormContext.js b/ui/src/app/metadata/hoc/MetadataFormContext.js
index f03035a03..9637b8fa7 100644
--- a/ui/src/app/metadata/hoc/MetadataFormContext.js
+++ b/ui/src/app/metadata/hoc/MetadataFormContext.js
@@ -56,9 +56,12 @@ function reducer(state, action) {
}
/*eslint-disable react-hooks/exhaustive-deps*/
-function MetadataForm({ children }) {
+function MetadataForm({ children, initial = {} }) {
- const metadata = useFormattedMetadata();
+ const metadata = {
+ ...useFormattedMetadata(),
+ ...initial
+ };
const [state, dispatch] = React.useReducer(reducer, {
...initialState,
@@ -79,9 +82,16 @@ function useFormErrors () {
return errors;
}
+function useExtraErrors (metadata, validator) {
+ return validator(metadata);
+}
+
function usePagesWithErrors(definition) {
const errorList = useFormErrors();
const erroredProperties = uniq(errorList.map((e) => {
+ if (!e.hasOwnProperty('property')) {
+ return 'common';
+ }
let name = e.property.split('.').filter((p) => !!p && p !== "")[0];
if (name.indexOf('[')) {
name = name.split('[')[0];
@@ -100,7 +110,7 @@ function usePagesWithErrors(definition) {
return pages;
}
-function useFormattedMetadata() {
+function useFormattedMetadata(initial = {}) {
const definition = React.useContext(MetadataDefinitionContext);
const schema = React.useContext(MetadataSchemaContext);
return definition.formatter(React.useContext(MetadataObjectContext), schema);
@@ -139,6 +149,7 @@ export {
useMetadataFormState,
useMetadataFormData,
useMetadataFormErrors,
+ useExtraErrors,
MetadataForm,
MetadataFormContext,
Provider as MetadataFormProvider,
diff --git a/ui/src/app/metadata/hooks/api.js b/ui/src/app/metadata/hooks/api.js
index e72747c7e..7b957f67c 100644
--- a/ui/src/app/metadata/hooks/api.js
+++ b/ui/src/app/metadata/hooks/api.js
@@ -28,8 +28,8 @@ export function getSchemaPath(type) {
return `/${schema[type]}`;
}
-export function useMetadataEntities(type = 'source', opts = {}) {
- return useFetch(`${API_BASE_PATH}${getMetadataListPath(type)}`, opts);
+export function useMetadataEntities(type = 'source', opts = {}, onMount) {
+ return useFetch(`${API_BASE_PATH}${getMetadataListPath(type)}`, opts, onMount);
}
export function useMetadataEntity(type = 'source', opts = {
@@ -63,4 +63,12 @@ export function useMetadataHistory(type, id, opts = {}, i) {
export function useMetadataSources(opts = {}, onMount) {
return useFetch(`${API_BASE_PATH}${getMetadataListPath('source')}`, opts, onMount);
+}
+
+export function useMetadataProviders(opts = {}, onMount) {
+ return useFetch(`${API_BASE_PATH}${getMetadataListPath('provider')}`, opts, onMount);
+}
+
+export function useMetadataProviderTypes(opts = {}, onMount = null) {
+ return useFetch(`${API_BASE_PATH}/ui/MetadataResolver/types`, opts, onMount);
}
\ No newline at end of file
diff --git a/ui/src/app/metadata/hooks/configuration.js b/ui/src/app/metadata/hooks/configuration.js
index da0ba0c28..dc23b874f 100644
--- a/ui/src/app/metadata/hooks/configuration.js
+++ b/ui/src/app/metadata/hooks/configuration.js
@@ -18,7 +18,5 @@ export function useMetadataConfiguration(models, schema, definition, limited = f
return {};
}
- const processed = definition.schemaPreprocessor ? definition.schemaPreprocessor(schema) : schema;
-
- return getLimitedConfigurationsFn(getConfigurationSections(models, definition, processed), limited);
+ return getLimitedConfigurationsFn(getConfigurationSections(models, definition, schema), limited);
}
diff --git a/ui/src/app/metadata/view/MetadataComparison.js b/ui/src/app/metadata/view/MetadataComparison.js
index f7d5e06d5..566787a55 100644
--- a/ui/src/app/metadata/view/MetadataComparison.js
+++ b/ui/src/app/metadata/view/MetadataComparison.js
@@ -16,6 +16,7 @@ import Form from 'react-bootstrap/Form';
import { useTranslation } from '../../i18n/hooks';
import { MetadataFilterVersionList } from '../domain/filter/component/MetadataFilterVersionList';
import { MetadataFilterVersionContext } from '../domain/filter/component/MetadataFilterVersionContext';
+import { useMetadataSchema } from '../hooks/schema';
export function MetadataComparison () {
@@ -25,6 +26,8 @@ export function MetadataComparison () {
const schema = React.useContext(MetadataSchemaContext);
const definition = React.useContext(MetadataDefinitionContext);
+ const processed = useMetadataSchema(definition, schema);
+
const [limited, setLimited] = React.useState(false);
const toggleLimited = useTranslation('action.view-only-changes');
@@ -39,7 +42,7 @@ export function MetadataComparison () {
{versions &&
{(v) =>
-
+
{(config) =>
2 ? 'container-fluid' : 'container'}>
diff --git a/ui/src/app/metadata/view/MetadataOptions.js b/ui/src/app/metadata/view/MetadataOptions.js
index c5b48c187..4e00919ac 100644
--- a/ui/src/app/metadata/view/MetadataOptions.js
+++ b/ui/src/app/metadata/view/MetadataOptions.js
@@ -18,17 +18,19 @@ import { DeleteSourceConfirmation } from '../domain/source/component/DeleteSourc
import { MetadataFilters } from '../domain/filter/component/MetadataFilters';
import { MetadataFilterConfigurationList } from '../domain/filter/component/MetadataFilterConfigurationList';
import { MetadataFilterTypes } from '../domain/filter';
+import { useMetadataSchema } from '../hooks/schema';
export function MetadataOptions () {
const metadata = React.useContext(MetadataObjectContext);
const definition = React.useContext(MetadataDefinitionContext);
const schema = React.useContext(MetadataSchemaContext);
+ const processed = useMetadataSchema(definition, schema);
const history = useHistory();
const { type, id } = useParams();
- const configuration = useMetadataConfiguration([metadata], schema, definition);
+ const configuration = useMetadataConfiguration([metadata], processed, definition);
const onScrollTo = (element, offset = 0) => {
scroller.scrollTo(element, {
diff --git a/ui/src/app/metadata/view/MetadataUpload.js b/ui/src/app/metadata/view/MetadataUpload.js
index 74a114ead..a193c46f9 100644
--- a/ui/src/app/metadata/view/MetadataUpload.js
+++ b/ui/src/app/metadata/view/MetadataUpload.js
@@ -86,7 +86,7 @@ export function MetadataUpload() {
{isLast &&
-
+
{(config) => }
diff --git a/ui/src/app/metadata/wizard/MetadataWizardForm.js b/ui/src/app/metadata/wizard/MetadataWizardForm.js
index 8597eb577..1a815e54e 100644
--- a/ui/src/app/metadata/wizard/MetadataWizardForm.js
+++ b/ui/src/app/metadata/wizard/MetadataWizardForm.js
@@ -12,7 +12,7 @@ function ErrorListTemplate () {
return (<>>);
}
-export function MetadataWizardForm ({ metadata, definition, schema, current, onChange, onBlur = false }) {
+export function MetadataWizardForm ({ metadata, definition, schema, current, onChange, onBlur = false, validator }) {
const {uiSchema} = useUiSchema(definition, schema, current);
@@ -42,7 +42,8 @@ export function MetadataWizardForm ({ metadata, definition, schema, current, onC
widgets={widgets}
liveValidate={true}
transformErrors={transformErrors}
- ErrorList={ErrorListTemplate}>
+ ErrorList={ErrorListTemplate}
+ validate={validator}>
<>>
diff --git a/ui/src/app/metadata/wizard/Wizard.js b/ui/src/app/metadata/wizard/Wizard.js
index 44efbfbb3..cc52249fe 100644
--- a/ui/src/app/metadata/wizard/Wizard.js
+++ b/ui/src/app/metadata/wizard/Wizard.js
@@ -84,6 +84,11 @@ function useNextPage() {
return definition.steps[idx + 1];
}
+function useFirstPage() {
+ const definition = useMetadataDefinitionContext();
+ return definition.steps[0];
+}
+
function useLastPage () {
const definition = useMetadataDefinitionContext();
return definition.steps[definition.steps.length - 1];
@@ -114,6 +119,7 @@ export {
useCurrentPage,
useNextPage,
usePreviousPage,
+ useFirstPage,
useLastPage,
useIsFirstPage,
useIsLastPage,
diff --git a/ui/src/app/metadata/wizard/WizardNav.js b/ui/src/app/metadata/wizard/WizardNav.js
index 9be7ed9fb..5fdc91def 100644
--- a/ui/src/app/metadata/wizard/WizardNav.js
+++ b/ui/src/app/metadata/wizard/WizardNav.js
@@ -7,6 +7,7 @@ import {Translate} from '../../i18n/components/translate';
import {
useCurrentPage,
useLastPage,
+ useFirstPage,
useNextPage,
usePreviousPage,
setWizardIndexAction,
@@ -18,12 +19,13 @@ export const ICONS = {
INDEX: 'INDEX'
}
-export function WizardNav ({ disabled = false, onSave, saving }) {
+export function WizardNav ({ disabled = false, onSave, saving, onRestart }) {
const dispatch = useWizardDispatcher();
const current = useCurrentPage();
const previous = usePreviousPage();
+ const first = useFirstPage();
const next = useNextPage();
const last = useLastPage();
@@ -31,13 +33,21 @@ export function WizardNav ({ disabled = false, onSave, saving }) {
dispatch(setWizardIndexAction(idx));
};
+ const onPrevious = (idx) => {
+ if (idx === first.id && onRestart) {
+ onRestart();
+ } else {
+ onSetIndex(idx);
+ }
+ };
+
const currentIcon = (last && current.index === last.index) ? : current.index;
return (