diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy index 8cb3df104..92d53ff65 100644 --- a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy @@ -150,6 +150,37 @@ class MetadataResolverControllerVersionEndpointsIntegrationTests extends Specifi mrv2.body.name == 'resolverUPDATED' } + def "SHIBUI-2182"() { + given: + def mr = new DynamicHttpMetadataResolver().with { + it.name = 'resolver2' + it.metadataRequestURLConstructionScheme = new RegexScheme().with { + it.match = 'This is the match field' + it.content = 'some content' + it + } + it + } + mr = repository.save(mr) + //Will create a second version for UPDATE revision + mr.name = 'resolverUPDATED' + mr.metadataRequestURLConstructionScheme.match = 'This is the match field too' + repository.save(mr) + + when: + def allVersions = getAllMetadataResolverVersions(mr.resourceId, List) + def mrv1 = getMetadataResolverForVersion(mr.resourceId, allVersions.body[0].id, MetadataResolver) + def mrv2 = getMetadataResolverForVersion(mr.resourceId, allVersions.body[1].id, MetadataResolver) + + then: + mrv1.statusCodeValue == 200 + mrv1.body.name == 'resolver2' + mrv1.body.metadataRequestURLConstructionScheme.match == 'This is the match field' + mrv2.statusCodeValue == 200 + mrv2.body.name == 'resolverUPDATED' + mrv2.body.metadataRequestURLConstructionScheme.match == 'This is the match field too' + } + def "SHIBUI-1386"() { given: MetadataResolver mr = new FileBackedHttpMetadataResolver(name: 'testme') @@ -292,4 +323,4 @@ trait AttributeReleaseAndOverrides { Map overrides(int filterIndex) { (this.metadataFilters[filterIndex] as EntityAttributesFilter).relyingPartyOverrides } -} +} \ No newline at end of file diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy index 049869b64..79420f6d0 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy @@ -89,6 +89,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } void constructXmlNodeForFilter(EntityAttributesFilter filter, def markupBuilderDelegate) { + if (!filter.isFilterEnabled()) { return } markupBuilderDelegate.MetadataFilter('xsi:type': 'EntityAttributes') { // TODO: enhance. currently this does weird things with namespaces filter.attributes.each { attribute -> @@ -128,6 +129,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { // TODO: enhance void constructXmlNodeForFilter(EntityRoleWhiteListFilter filter, def markupBuilderDelegate) { + if (!filter.isFilterEnabled()) { return } if (!filter.retainedRoles?.isEmpty()) { markupBuilderDelegate.MetadataFilter( 'xsi:type': 'EntityRoleWhiteList', @@ -142,6 +144,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } void constructXmlNodeForFilter(NameIdFormatFilter filter, def markupBuilderDelegate) { + if (!filter.isFilterEnabled()) { return } def type = filter.nameIdFormatFilterTarget.nameIdFormatFilterTargetType markupBuilderDelegate.MetadataFilter( 'xsi:type': 'NameIDFormat', @@ -180,6 +183,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } void constructXmlNodeForFilter(RequiredValidUntilFilter filter, def markupBuilderDelegate) { + if (!filter.isFilterEnabled()) { return } if (filter.xmlShouldBeGenerated()) { markupBuilderDelegate.MetadataFilter( 'xsi:type': 'RequiredValidUntil', @@ -189,6 +193,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } void constructXmlNodeForFilter(SignatureValidationFilter filter, def markupBuilderDelegate) { + if (!filter.isFilterEnabled()) { return } if (filter.xmlShouldBeGenerated()) { markupBuilderDelegate.MetadataFilter(id: filter.name, 'xsi:type': 'SignatureValidation', @@ -459,8 +464,10 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } } mr.metadataFilters.each { edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter filter -> - doNamespaceProtectionFilter() - constructXmlNodeForFilter(filter, delegate) + if (filter.isFilterEnabled()) { + doNamespaceProtectionFilter() + constructXmlNodeForFilter(filter, delegate) + } } doNamespaceProtectionFilter() } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataRequestURLConstructionScheme.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataRequestURLConstructionScheme.java index a1223840e..e24b5cb02 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataRequestURLConstructionScheme.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataRequestURLConstructionScheme.java @@ -64,4 +64,7 @@ public String toString() { String type; String content; -} + + @Transient + String match; +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversMetadataResolverVersionService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversMetadataResolverVersionService.java index b0d90195f..558af5789 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversMetadataResolverVersionService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversMetadataResolverVersionService.java @@ -40,4 +40,4 @@ public MetadataResolver findSpecificVersionOfMetadataResolver(String resourceId, return resolver; } -} +} \ No newline at end of file diff --git a/backend/src/main/resources/dynamic-http-metadata-provider.schema.json b/backend/src/main/resources/dynamic-http-metadata-provider.schema.json index 8be8bbf9b..165fc904c 100644 --- a/backend/src/main/resources/dynamic-http-metadata-provider.schema.json +++ b/backend/src/main/resources/dynamic-http-metadata-provider.schema.json @@ -9,7 +9,7 @@ "properties": { "name": { "title": "label.metadata-provider-name-dashboard-display-only", - "description": "tooltip.metadata-provider-name-dashboard-display-only", + "description": "tooltip.metadata-provider-name", "type": "string" }, "@type": { @@ -47,13 +47,10 @@ ] }, "match": { + "$id": "match", "title": "label.match", "description": "tooltip.match", - "type": "string", - "widget": { - "id": "string", - "required": true - } + "type": "string" } }, "required": [ diff --git a/backend/src/main/resources/file-system-metadata-provider.schema.json b/backend/src/main/resources/file-system-metadata-provider.schema.json index d238018b9..7969495f2 100644 --- a/backend/src/main/resources/file-system-metadata-provider.schema.json +++ b/backend/src/main/resources/file-system-metadata-provider.schema.json @@ -9,7 +9,7 @@ "properties": { "name": { "title": "label.metadata-provider-name-dashboard-display-only", - "description": "tooltip.metadata-provider-name-dashboard-display-only", + "description": "tooltip.metadata-provider-name", "type": "string", "widget": { "id": "string", diff --git a/backend/src/main/resources/filebacked-http-metadata-provider.schema.json b/backend/src/main/resources/filebacked-http-metadata-provider.schema.json index 9ce22ca94..f678ee306 100644 --- a/backend/src/main/resources/filebacked-http-metadata-provider.schema.json +++ b/backend/src/main/resources/filebacked-http-metadata-provider.schema.json @@ -28,7 +28,7 @@ "properties": { "name": { "title": "label.metadata-provider-name-dashboard-display-only", - "description": "tooltip.metadata-provider-name-dashboard-display-only", + "description": "tooltip.metadata-provider-name", "type": "string", "widget": { "id": "string", diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties index 4c22a97ff..0a7880b36 100644 --- a/backend/src/main/resources/i18n/messages.properties +++ b/backend/src/main/resources/i18n/messages.properties @@ -69,12 +69,15 @@ action.groups=Groups action.source-group=Group action.enable=Enable action.disable=Disable +action.get-latest=Get latest changes action.add-new-role=Add new role action.roles=Roles action.source-role=Role action.select-bundle=Select Bundle +action.get-latest=Get latest + value.enabled=Enabled value.disabled=Disabled value.current=Current @@ -597,6 +600,7 @@ message.invalid-signing=Unless the response or the assertions are signed, SAML s message.session-timeout-heading=Session timed out message.session-timeout-body=Your session has timed out. Please login again. +message.session-timeout=An error has occurred while saving. Your session may have timed out. tooltip.entity-id=Entity ID tooltip.service-provider-name=Service Provider Name (Dashboard Display Only) @@ -662,7 +666,7 @@ tooltip.max-cache-entry-size=The maximum response body size that may be cached, tooltip.metadata-provider-name=Metadata Provider Name (for display on the Dashboard only) tooltip.metadata-provider-type=Metadata Provider Type tooltip.xml-id=Identifier for logging, identification for command line reload, etc. -tooltip.metadata-url=Identifier for logging, identification for command line reload, etc. +tooltip.metadata-url=The URL that the metadata is served from. tooltip.metadata-file=The absolute path to the local metadata file to be loaded. tooltip.init-from-backup=Flag indicating whether initialization should first attempt to load metadata from the backup file. If true, foreground initialization will be performed by loading the backing file, and then a refresh from the remote HTTP server will be scheduled to execute in a background thread, after a configured delay. This can improve IdP startup times when the remote HTTP file is large in size. tooltip.backing-file=Specifies where the backing file is located. If the remote server is unavailable at startup, the backing file is loaded instead. @@ -718,4 +722,6 @@ tooltip.group-name=Group Name tooltip.group-description=Group Description tooltip.role-name=Role Name -tooltip.role-description=Role Description \ No newline at end of file +tooltip.role-description=Role Description + +tooltip.contact-information=Contact Information \ 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 2fa33ffa9..f9f64d4d2 100644 --- a/backend/src/main/resources/i18n/messages_en.properties +++ b/backend/src/main/resources/i18n/messages_en.properties @@ -513,7 +513,7 @@ tooltip.max-cache-entry-size=The maximum response body size that may be cached, tooltip.metadata-provider-name=Metadata Provider Name (for display on the Dashboard only) tooltip.metadata-provider-type=Metadata Provider Type tooltip.xml-id=Identifier for logging, identification for command line reload, etc. -tooltip.metadata-url=Identifier for logging, identification for command line reload, etc. +tooltip.metadata-url=The URL that the metadata is served from. tooltip.metadata-file=The absolute path to the local metadata file to be loaded. tooltip.init-from-backup=Flag indicating whether initialization should first attempt to load metadata from the backup file. If true, foreground initialization will be performed by loading the backing file, and then a refresh from the remote HTTP server will be scheduled to execute in a background thread, after a configured delay. This can improve IdP startup times when the remote HTTP file is large in size. tooltip.backing-file=Specifies where the backing file is located. If the remote server is unavailable at startup, the backing file is loaded instead. diff --git a/backend/src/main/resources/local-dynamic-metadata-provider.schema.json b/backend/src/main/resources/local-dynamic-metadata-provider.schema.json index 1b1951dac..6e81a0a90 100644 --- a/backend/src/main/resources/local-dynamic-metadata-provider.schema.json +++ b/backend/src/main/resources/local-dynamic-metadata-provider.schema.json @@ -9,7 +9,7 @@ "properties": { "name": { "title": "label.metadata-provider-name-dashboard-display-only", - "description": "tooltip.metadata-provider-name-dashboard-display-only", + "description": "tooltip.metadata-provider-name", "type": "string", "widget": { "id": "string", diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy index a44b4beed..378995e99 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy @@ -38,7 +38,7 @@ class IncommonJPAMetadataResolverServiceImplTests extends AbstractBaseDataJpaTes def 'simple test generation of metadata-providers.xml'() { when: def mr = metadataResolverRepository.findAll().iterator().next() - mr.metadataFilters << new SignatureValidationFilter(requireSignedRoot: true, certificateFile: '%{idp.home}/credentials/inc-md-cert.pem') + mr.metadataFilters << new SignatureValidationFilter(enabled: true, requireSignedRoot: true, certificateFile: '%{idp.home}/credentials/inc-md-cert.pem') mr.metadataFilters << requiredValidUntilFilterForXmlGenerationTests() mr.metadataFilters << entityRoleWhiteListFilterForXmlGenerationTests() metadataResolverRepository.save(mr) @@ -52,9 +52,10 @@ class IncommonJPAMetadataResolverServiceImplTests extends AbstractBaseDataJpaTes when: //TODO: this might break later def mr = metadataResolverRepository.findAll().iterator().next() - mr.metadataFilters << new SignatureValidationFilter(requireSignedRoot: true, certificateFile: '%{idp.home}/credentials/inc-md-cert.pem') + mr.metadataFilters << new SignatureValidationFilter(enabled: true, requireSignedRoot: true, certificateFile: '%{idp.home}/credentials/inc-md-cert.pem') mr.metadataFilters << requiredValidUntilFilterForXmlGenerationTests() mr.metadataFilters.add(new EntityAttributesFilter().with { + it.enabled = true it.entityAttributesFilterTarget = new EntityAttributesFilterTarget().with { it.entityAttributesFilterTargetType = EntityAttributesFilterTarget.EntityAttributesFilterTargetType.ENTITY it.value = ['https://sp1.example.org'] @@ -81,6 +82,7 @@ class IncommonJPAMetadataResolverServiceImplTests extends AbstractBaseDataJpaTes EntityRoleWhiteListFilter entityRoleWhiteListFilterForXmlGenerationTests() { new EntityRoleWhiteListFilter().with { it.retainedRoles = ['md:SPSSODescriptor'] + it.enabled = true it } } @@ -88,6 +90,7 @@ class IncommonJPAMetadataResolverServiceImplTests extends AbstractBaseDataJpaTes RequiredValidUntilFilter requiredValidUntilFilterForXmlGenerationTests() { new RequiredValidUntilFilter().with { it.maxValidityInterval = 'P14D' + it.enabled = true it } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy index 28ba48d30..703e44f7a 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy @@ -149,6 +149,20 @@ class JPAMetadataResolverServiceImplTests extends AbstractBaseDataJpaTest { generatedXmlIsTheSameAsExpectedXml('/conf/661.xml', domBuilder.parseText(writer.toString())) } + def 'test generating xml when filter is disabled'() { + given: + def filter = testObjectGenerator.entityAttributesFilterWithConditionScript() + filter.setEnabled(Boolean.FALSE) + + when: + genXmlSnippet(markupBuilder) { + JPAMetadataResolverServiceImpl.cast(metadataResolverService).constructXmlNodeForFilter(filter, it) + } + + then: + generatedXmlIsTheSameAsExpectedXml('/conf/661.3.xml', domBuilder.parseText(writer.toString())) + } + def 'test generating EntityAttributesFilter xml snippet with regex'() { given: def filter = testObjectGenerator.entityAttributesFilterWithRegex() @@ -177,6 +191,7 @@ class JPAMetadataResolverServiceImplTests extends AbstractBaseDataJpaTest { def 'test generating RequiredValidUntilFilter xml snippet'() { given: def filter = new RequiredValidUntilFilter().with { + it.enabled = true it.maxValidityInterval = 'P14D' it } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy index 7ed0709df..bf17b107e 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy @@ -193,6 +193,7 @@ class TestObjectGenerator { it.dynamicTrustedNamesStrategyRef = generator.randomString(10) it.trustEngineRef = generator.randomString(10) it.publicKey = generator.randomString(50) + it.enabled = true; it } } @@ -202,6 +203,7 @@ class TestObjectGenerator { it.name = 'EntityRoleWhiteList' it.retainedRoles = ['role1', 'role2'] it.removeRolelessEntityDescriptors = true + it.enabled = true; it } } @@ -212,6 +214,7 @@ class TestObjectGenerator { it.setEntityAttributesFilterTarget(buildEntityAttributesFilterTarget()) it.setAttributes(buildAttributesList()) it.intoTransientRepresentation() + it.enabled = true; it } } @@ -221,6 +224,7 @@ class TestObjectGenerator { it.name = 'EntityAttributes' it.setEntityAttributesFilterTarget(buildEntityAttributesFilterTargetWithConditionScript()) it.intoTransientRepresentation() + it.enabled = true; it } } @@ -230,6 +234,7 @@ class TestObjectGenerator { it.name = 'EntityAttributes' it.setEntityAttributesFilterTarget(buildEntityAttributesFilterTargetWithRegex()) it.intoTransientRepresentation() + it.enabled = true; it } } @@ -237,6 +242,7 @@ class TestObjectGenerator { RequiredValidUntilFilter requiredValidUntilFilter() { return new RequiredValidUntilFilter().with { it.maxValidityInterval = 'P14D' + it.enabled = true; it } } @@ -246,6 +252,7 @@ class TestObjectGenerator { it.name = "NameIDFormat" it.formats = ['urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'] it.setNameIdFormatFilterTarget(new NameIdFormatFilterTarget(nameIdFormatFilterTargetType: ENTITY, singleValue: 'https://sp1.example.org')) + it.enabled = true; it } } @@ -255,6 +262,7 @@ class TestObjectGenerator { it.name = requiredValidUntilFilter.name it.resourceId = requiredValidUntilFilter.resourceId it.maxValidityInterval = requiredValidUntilFilter.maxValidityInterval + it.enabled = true; it } } @@ -270,6 +278,7 @@ class TestObjectGenerator { it.requireSignedRoot = signatureValidationFilter.requireSignedRoot it.certificateFile = signatureValidationFilter.certificateFile it.defaultCriteriaRef = signatureValidationFilter.defaultCriteriaRef + it.enabled = true; it } } diff --git a/backend/src/test/resources/conf/661.3.xml b/backend/src/test/resources/conf/661.3.xml new file mode 100644 index 000000000..39eabfe8e --- /dev/null +++ b/backend/src/test/resources/conf/661.3.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 9c9e650af..79bee945d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ name=shibui group=edu.internet2.tier.shibboleth.admin.ui -version=1.10.0-SNAPSHOT +version=1.11.0-SNAPSHOT shibboleth.version=3.4.4 opensaml.version=3.4.3 diff --git a/ui/src/app/admin/container/MetadataActions.js b/ui/src/app/admin/container/MetadataActions.js index 4af521db5..4ed567d23 100644 --- a/ui/src/app/admin/container/MetadataActions.js +++ b/ui/src/app/admin/container/MetadataActions.js @@ -2,7 +2,7 @@ import React from 'react'; import { DeleteConfirmation } from '../../core/components/DeleteConfirmation'; import { useMetadataActivator, useMetadataEntity } from '../../metadata/hooks/api'; -import { NotificationContext, createNotificationAction } from '../../notifications/hoc/Notifications'; +import { NotificationContext, createNotificationAction, NotificationTypes } from '../../notifications/hoc/Notifications'; export function MetadataActions ({type, children}) { @@ -21,6 +21,12 @@ export function MetadataActions ({type, children}) { `Metadata ${type} has been ${enabled ? 'enabled' : 'disabled'}.` )); cb(); + } else { + const { errorCode, errorMessage, cause } = activator?.response?.data; + dispatch(createNotificationAction( + `${errorCode}: ${errorMessage} ${cause ? `-${cause}` : ''}`, + NotificationTypes.ERROR + )); } } @@ -31,6 +37,12 @@ export function MetadataActions ({type, children}) { `Metadata ${type} has been deleted.` )); cb(); + } else { + const { errorCode, errorMessage, cause } = activator?.response?.data; + dispatch(createNotificationAction( + `${errorCode}: ${errorMessage} ${cause ? `-${cause}` : ''}`, + NotificationTypes.ERROR + )); } } diff --git a/ui/src/app/core/components/Header.js b/ui/src/app/core/components/Header.js index 7bb693b1e..ee7be969b 100644 --- a/ui/src/app/core/components/Header.js +++ b/ui/src/app/core/components/Header.js @@ -13,7 +13,7 @@ import Translate from '../../i18n/components/translate'; import { useTranslator } from '../../i18n/hooks'; import { brand } from '../../app.brand'; -import { useCurrentUser, useCurrentUserLoading, useIsAdmin } from '../user/UserContext'; +import { useCurrentUser, useCurrentUserLoading, useIsAdmin, useUserGroupNames } from '../user/UserContext'; import { BASE_PATH } from '../../App.constant'; export function Header () { @@ -22,16 +22,17 @@ export function Header () { const isAdmin = useIsAdmin(); - const { username, groupId } = useCurrentUser(); + const { username } = useCurrentUser(); + const name = useUserGroupNames(); const loading = useCurrentUserLoading(); return ( - + {brand.logo.alt} - + {loading ?
@@ -97,7 +98,7 @@ export function Header () { Groups - {groupId} + {name} diff --git a/ui/src/app/core/components/Header.test.js b/ui/src/app/core/components/Header.test.js index 70d186d91..59866943f 100644 --- a/ui/src/app/core/components/Header.test.js +++ b/ui/src/app/core/components/Header.test.js @@ -17,11 +17,13 @@ jest.mock('../../i18n/hooks', () => ({ const mockIsAdmin = jest.fn(); const mockCurrentUser = jest.fn(); const mockCurrentUserLoading = jest.fn(); +const mockUseUserGroupNames = jest.fn(); jest.mock('../user/UserContext', () => ({ useIsAdmin: () => mockIsAdmin(), useCurrentUser: () => mockCurrentUser(), - useCurrentUserLoading: () => mockCurrentUserLoading() + useCurrentUserLoading: () => mockCurrentUserLoading(), + useUserGroupNames: () => mockUseUserGroupNames() })); describe('header for admins', () => { @@ -29,6 +31,7 @@ describe('header for admins', () => { mockIsAdmin.mockReturnValue(true); mockCurrentUser.mockReturnValue({ username: 'foo', groupId: 'bar' }); mockCurrentUserLoading.mockReturnValue(false); + mockUseUserGroupNames.mockReturnValue('Foo'); }); it('should display logo and navigation', () => { diff --git a/ui/src/app/core/user/UserContext.js b/ui/src/app/core/user/UserContext.js index 14666abe8..f2438a9ef 100644 --- a/ui/src/app/core/user/UserContext.js +++ b/ui/src/app/core/user/UserContext.js @@ -72,7 +72,17 @@ function useCanEnable() { function useUserGroup() { const user = useCurrentUser(); - return user?.userGroups[0]; + return (user?.userGroups && user.userGroups.length > 0) ? user.userGroups[0] : null; +} + +function useUserGroups() { + const user = useCurrentUser(); + return (user?.userGroups && Array.isArray(user.userGroups)) ? user.userGroups : []; +} + +function useUserGroupNames() { + const groups = useUserGroups(); + return groups.map(g => g.name).join(', '); } function useUserGroupRegexValidator () { @@ -91,5 +101,6 @@ export { useCanEnable, useCurrentUserLoading, useUserGroupRegexValidator, - useUserGroup + useUserGroup, + useUserGroupNames }; diff --git a/ui/src/app/dashboard/view/SourcesTab.js b/ui/src/app/dashboard/view/SourcesTab.js index 36cdc1bdf..b564a3872 100644 --- a/ui/src/app/dashboard/view/SourcesTab.js +++ b/ui/src/app/dashboard/view/SourcesTab.js @@ -6,7 +6,7 @@ import SourceList from '../../metadata/domain/source/component/SourceList'; import { useMetadataEntities, useMetadataEntity } from '../../metadata/hooks/api'; import { Search } from '../component/Search'; -import { NotificationContext, createNotificationAction } from '../../notifications/hoc/Notifications'; +import { NotificationContext, createNotificationAction, NotificationTypes } from '../../notifications/hoc/Notifications'; const searchProps = ['serviceProviderName', 'entityId', 'createdBy']; @@ -42,6 +42,12 @@ export function SourcesTab () { if (updater.response.ok) { dispatch(createNotificationAction(`Updated group successfully.`)); loadSources(); + } else { + const { errorCode, errorMessage, cause } = updater?.response?.data; + dispatch(createNotificationAction( + `${errorCode}: ${errorMessage} ${cause ? `-${cause}` : ''}`, + NotificationTypes.ERROR + )); } } diff --git a/ui/src/app/form/component/IconButton.js b/ui/src/app/form/component/IconButton.js index 5cac9c37d..489345676 100644 --- a/ui/src/app/form/component/IconButton.js +++ b/ui/src/app/form/component/IconButton.js @@ -11,11 +11,12 @@ const mappings = { "arrow-down": , }; -const IconButton = (props) => { +const IconButton = ({children, ...props}) => { const { icon, ...otherProps } = props; return ( ); }; diff --git a/ui/src/app/form/component/InfoIcon.js b/ui/src/app/form/component/InfoIcon.js index 989428435..d813fe777 100644 --- a/ui/src/app/form/component/InfoIcon.js +++ b/ui/src/app/form/component/InfoIcon.js @@ -17,6 +17,7 @@ export function InfoIcon ({ value, placement='left', ...props }) { )} aria-label={translate('tooltip.instruction')}> diff --git a/ui/src/app/form/component/fields/FilterTargetField.js b/ui/src/app/form/component/fields/FilterTargetField.js index b9d44c725..49f3663e2 100644 --- a/ui/src/app/form/component/fields/FilterTargetField.js +++ b/ui/src/app/form/component/fields/FilterTargetField.js @@ -162,7 +162,7 @@ const FilterTargetField = ({ - +
diff --git a/ui/src/app/form/component/templates/ArrayFieldTemplate.js b/ui/src/app/form/component/templates/ArrayFieldTemplate.js index e138c10a0..637e7f7ea 100644 --- a/ui/src/app/form/component/templates/ArrayFieldTemplate.js +++ b/ui/src/app/form/component/templates/ArrayFieldTemplate.js @@ -86,7 +86,7 @@ const ObjectArrayItem = ({type, ...props}) => { props.disabled || props.readonly || !props.hasMoveUp } onClick={props.onReorderClick(props.index, props.index - 1)} - /> + >Move Up
)} @@ -100,7 +100,7 @@ const ObjectArrayItem = ({type, ...props}) => { props.disabled || props.readonly || !props.hasMoveDown } onClick={props.onReorderClick(props.index, props.index + 1)} - /> + >Move Down
)} @@ -114,7 +114,7 @@ const ObjectArrayItem = ({type, ...props}) => { style={btnStyle} disabled={props.disabled || props.readonly} onClick={props.onDropIndexClick(props.index)} - /> + >Delete )} @@ -156,7 +156,7 @@ const DefaultArrayItem = (props) => { props.disabled || props.readonly || !props.hasMoveUp } onClick={props.onReorderClick(props.index, props.index - 1)} - /> + >Move Up )} @@ -170,7 +170,7 @@ const DefaultArrayItem = (props) => { props.disabled || props.readonly || !props.hasMoveDown } onClick={props.onReorderClick(props.index, props.index + 1)} - /> + >Move Down )} @@ -184,7 +184,7 @@ const DefaultArrayItem = (props) => { style={btnStyle} disabled={props.disabled || props.readonly} onClick={props.onDropIndexClick(props.index)} - /> + >Delete )} @@ -210,7 +210,7 @@ const DefaultFixedArrayFieldTemplate = (props) => { className="array-item-add" onClick={props.onAddClick} disabled={props.disabled || props.readonly} - /> + >Add )} @@ -254,7 +254,7 @@ const DefaultNormalArrayFieldTemplate = (props) => { className="array-item-add mx-2" onClick={props.onAddClick} disabled={props.disabled || props.readonly} - /> + >Add )} {(props.uiSchema["ui:description"] || props.schema.description) && ( console.log(bundle), [bundle]); - const onMouseOver = (opt) => setBundle(opt); const onMouseOut = () => setBundle(null); diff --git a/ui/src/app/form/component/widgets/OptionWidget.js b/ui/src/app/form/component/widgets/OptionWidget.js index b1b43fee5..728e66c87 100644 --- a/ui/src/app/form/component/widgets/OptionWidget.js +++ b/ui/src/app/form/component/widgets/OptionWidget.js @@ -12,7 +12,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faAsterisk, faCaretDown, faCaretUp } from "@fortawesome/free-solid-svg-icons"; import { useTranslator } from "../../../i18n/hooks"; -const ToggleButton = ({ isOpen, onClick, disabled }) => ( +const ToggleButton = ({ isOpen, onClick, disabled, children }) => ( ); @@ -91,15 +92,16 @@ const OptionWidget = ({ return ( - 0) ? "text-danger" : ""}`}> + 0) ? "text-danger" : ""}`} htmlFor={`option-selector-${id}`}> - {(label || schema.title) && required ? : null} + {(label || schema.title) && required ? : Item {id + 1}} {schema.description && } {({ isMenuShown, toggleMenu }) => ( - toggleMenu()} disabled={disabled || readonly} /> + toggleMenu()} disabled={disabled || readonly}> + Options + )} {rawErrors?.length > 0 && touched && ( diff --git a/ui/src/app/form/component/widgets/SelectWidget.js b/ui/src/app/form/component/widgets/SelectWidget.js index 76dc99ec6..a2ad763e6 100644 --- a/ui/src/app/form/component/widgets/SelectWidget.js +++ b/ui/src/app/form/component/widgets/SelectWidget.js @@ -86,7 +86,7 @@ const SelectWidget = ({ return ( - 0 ? "text-danger" : ""}`}> + 0 ? "text-danger" : ""}`} htmlFor={id}> {(label || schema.title) && required ? : null} diff --git a/ui/src/app/form/component/widgets/TextWidget.js b/ui/src/app/form/component/widgets/TextWidget.js index 8554be911..c4150e0f2 100644 --- a/ui/src/app/form/component/widgets/TextWidget.js +++ b/ui/src/app/form/component/widgets/TextWidget.js @@ -41,7 +41,7 @@ const TextWidget = ({ // const classNames = [rawErrors?.length > 0 ? "is-invalid" : "", type === 'file' ? 'custom-file-label': ""] return ( - 0 && touched ? "text-danger" : ""}`}> + 0 && touched ? "text-danger" : ""}`} htmlFor={id}> {(label || schema.title) && required ? diff --git a/ui/src/app/metadata/Metadata.js b/ui/src/app/metadata/Metadata.js index 81cd1e35a..20fec9021 100644 --- a/ui/src/app/metadata/Metadata.js +++ b/ui/src/app/metadata/Metadata.js @@ -49,7 +49,7 @@ export function Metadata () { } /> - + } /> diff --git a/ui/src/app/metadata/copy/CopySource.js b/ui/src/app/metadata/copy/CopySource.js index 65e90a5e5..1034acde5 100644 --- a/ui/src/app/metadata/copy/CopySource.js +++ b/ui/src/app/metadata/copy/CopySource.js @@ -59,6 +59,10 @@ export function CopySource({ copy, onNext }) { setValue('properties', selected); }, [selected, setValue]); + React.useEffect(() => { + console.log(errors, formState); + }, [errors, formState]); + return ( <>
@@ -100,12 +104,10 @@ export function CopySource({ copy, onNext }) { - {errors?.target?.type === 'required' && + className={`form-text text-danger ${errors?.target?.type === 'required' ? '' : 'sr-only'}`}> Entity ID to copy is Required - }
@@ -156,14 +156,15 @@ export function CopySource({ copy, onNext }) { {sections.map((item, i) => - + onSelect(item, checked)} checked={selected.indexOf(item.property) > -1} + aria-labelledby={`property-checkbox-${i}`} /> diff --git a/ui/src/app/metadata/domain/filter/definition/BaseFilterDefinition.js b/ui/src/app/metadata/domain/filter/definition/BaseFilterDefinition.js index 8bdf9bfab..daed4cadc 100644 --- a/ui/src/app/metadata/domain/filter/definition/BaseFilterDefinition.js +++ b/ui/src/app/metadata/domain/filter/definition/BaseFilterDefinition.js @@ -11,7 +11,7 @@ export const BaseFilterDefinition = { return (formData, errors) => { if (names.indexOf(formData.name) > -1) { - errors.name.addError('message.name-unique'); + errors.name.addError('message.name-must-be-unique'); } if (formData.hasOwnProperty(targetProp)) { @@ -38,7 +38,10 @@ export const BaseFilterDefinition = { '@type': { 'ui:widget': 'hidden' }, - 'resourceId': { + resourceId: { + 'ui:widget': 'hidden' + }, + filterEnabled: { 'ui:widget': 'hidden' } } diff --git a/ui/src/app/metadata/domain/filter/definition/EntityAttributesFilterDefinition.js b/ui/src/app/metadata/domain/filter/definition/EntityAttributesFilterDefinition.js index 7ef0e08a9..b12d476e9 100644 --- a/ui/src/app/metadata/domain/filter/definition/EntityAttributesFilterDefinition.js +++ b/ui/src/app/metadata/domain/filter/definition/EntityAttributesFilterDefinition.js @@ -75,7 +75,8 @@ export const EntityAttributesFilterEditor= { 'name', '@type', 'resourceId', - 'entityAttributesFilterTarget' + 'entityAttributesFilterTarget', + 'filterEnabled' ] }, { diff --git a/ui/src/app/metadata/domain/filter/definition/NameIdFilterDefinition.js b/ui/src/app/metadata/domain/filter/definition/NameIdFilterDefinition.js index 358b51b34..d7f1492b9 100644 --- a/ui/src/app/metadata/domain/filter/definition/NameIdFilterDefinition.js +++ b/ui/src/app/metadata/domain/filter/definition/NameIdFilterDefinition.js @@ -42,7 +42,8 @@ export const NameIDFilterEditor = { 'name', '@type', 'resourceId', - 'nameIdFormatFilterTarget' + 'nameIdFormatFilterTarget', + 'filterEnabled' ] }, { diff --git a/ui/src/app/metadata/domain/provider/definition/BaseProviderDefinition.js b/ui/src/app/metadata/domain/provider/definition/BaseProviderDefinition.js index 207eb0a2e..55a2f671c 100644 --- a/ui/src/app/metadata/domain/provider/definition/BaseProviderDefinition.js +++ b/ui/src/app/metadata/domain/provider/definition/BaseProviderDefinition.js @@ -77,9 +77,13 @@ export const BaseProviderDefinition = { } }; }, + overrideSchema: (schema) => schema, uiSchema: { name: { 'ui:help': 'message.must-be-unique' + }, + enabled: { + 'ui:widget': 'hidden' } }, steps: [ diff --git a/ui/src/app/metadata/domain/provider/definition/DynamicHttpMetadataProviderDefinition.js b/ui/src/app/metadata/domain/provider/definition/DynamicHttpMetadataProviderDefinition.js index 84b694e9a..a7d110f0c 100644 --- a/ui/src/app/metadata/domain/provider/definition/DynamicHttpMetadataProviderDefinition.js +++ b/ui/src/app/metadata/domain/provider/definition/DynamicHttpMetadataProviderDefinition.js @@ -1,14 +1,57 @@ import { BaseProviderDefinition, HttpMetadataResolverAttributesSchema, MetadataFilterPluginsSchema } from './BaseProviderDefinition'; import API_BASE_PATH from '../../../../App.constant'; import defaultsDeep from 'lodash/defaultsDeep'; +import isNil from 'lodash/isNil'; import { DurationOptions } from '../../data'; import { isValidRegex } from '../../../../core/utility/is_valid_regex'; +function findById(o, id) { + //Early return + if (o.$id === id) { + return o; + } + var result, p; + for (p in o) { + if (o.hasOwnProperty(p) && typeof o[p] === 'object') { + result = findById(o[p], id); + if (result) { + return result; + } + } + } + return result; +} + + export const DynamicHttpMetadataProviderWizard = { ...BaseProviderDefinition, label: 'DynamicHttpMetadataProvider', type: 'DynamicHttpMetadataResolver', schema: `${API_BASE_PATH}/ui/MetadataResolver/DynamicHttpMetadataResolver`, + overrideSchema: (schema, models) => { + + const includeMatch = models.some(m => !isNil(m?.metadataRequestURLConstructionScheme?.match)); + + console.log(models) + + if (includeMatch) { + return ({ + ...schema, + properties: { + ...schema.properties, + metadataRequestURLConstructionScheme: { + ...schema.properties.metadataRequestURLConstructionScheme, + properties: { + ...schema.properties.metadataRequestURLConstructionScheme.properties, + match: findById(schema.properties.metadataRequestURLConstructionScheme.dependencies, 'match') + } + } + } + }); + } + + return schema; + }, validator: (data = [], current = { resourceId: null }, group, translator) => { const base = BaseProviderDefinition.validator(data, current, group); @@ -199,7 +242,8 @@ export const DynamicHttpMetadataProviderEditor = { 'xmlId', 'metadataRequestURLConstructionScheme', 'requireValidMetadata', - 'failFastInitialization' + 'failFastInitialization', + 'enabled' ] }, { diff --git a/ui/src/app/metadata/domain/provider/definition/FileBackedHttpMetadataProviderDefinition.js b/ui/src/app/metadata/domain/provider/definition/FileBackedHttpMetadataProviderDefinition.js index 5001895ce..106edfa55 100644 --- a/ui/src/app/metadata/domain/provider/definition/FileBackedHttpMetadataProviderDefinition.js +++ b/ui/src/app/metadata/domain/provider/definition/FileBackedHttpMetadataProviderDefinition.js @@ -198,7 +198,8 @@ export const FileBackedHttpMetadataProviderEditor = { 'requireValidMetadata', 'failFastInitialization', 'useDefaultPredicateRegistry', - 'satisfyAnyPredicates' + 'satisfyAnyPredicates', + 'enabled' ] }, { diff --git a/ui/src/app/metadata/domain/provider/definition/FileSystemMetadataProviderDefinition.js b/ui/src/app/metadata/domain/provider/definition/FileSystemMetadataProviderDefinition.js index 57d3447af..d97b8e041 100644 --- a/ui/src/app/metadata/domain/provider/definition/FileSystemMetadataProviderDefinition.js +++ b/ui/src/app/metadata/domain/provider/definition/FileSystemMetadataProviderDefinition.js @@ -107,7 +107,8 @@ export const FileSystemMetadataProviderEditor = { 'xmlId', '@type', 'metadataFile', - 'doInitialization' + 'doInitialization', + 'enabled' ], override: { '@type': { diff --git a/ui/src/app/metadata/domain/provider/definition/LocalDynamicMetadataProviderDefinition.js b/ui/src/app/metadata/domain/provider/definition/LocalDynamicMetadataProviderDefinition.js index eea5d3541..66b48d6c4 100644 --- a/ui/src/app/metadata/domain/provider/definition/LocalDynamicMetadataProviderDefinition.js +++ b/ui/src/app/metadata/domain/provider/definition/LocalDynamicMetadataProviderDefinition.js @@ -119,6 +119,7 @@ export const LocalDynamicMetadataProviderEditor = { '@type', 'xmlId', 'sourceDirectory', + 'enabled' ], override: { '@type': { diff --git a/ui/src/app/metadata/domain/source/component/SourceList.js b/ui/src/app/metadata/domain/source/component/SourceList.js index 41ce7f0cd..baf249c9f 100644 --- a/ui/src/app/metadata/domain/source/component/SourceList.js +++ b/ui/src/app/metadata/domain/source/component/SourceList.js @@ -35,7 +35,11 @@ export default function SourceList({ entities, onDelete, onEnable, onChangeGroup Created Date Enabled {isAdmin && onChangeGroup && Group } - {onDelete && isAdmin && } + {onDelete && isAdmin && + + Actions + + } diff --git a/ui/src/app/metadata/domain/source/definition/SourceDefinition.js b/ui/src/app/metadata/domain/source/definition/SourceDefinition.js index 6e2abdee3..70e686ed7 100644 --- a/ui/src/app/metadata/domain/source/definition/SourceDefinition.js +++ b/ui/src/app/metadata/domain/source/definition/SourceDefinition.js @@ -12,7 +12,7 @@ export const SourceBase = { type: '@MetadataProvider', steps: [], schema: `${API_BASE_PATH}/ui/MetadataSources`, - //schema: `/assets/schema/source/metadata-source.json`, + // schema: `/assets/schema/source/metadata-source.json`, parser: (data) => removeNull(data, true), @@ -158,6 +158,9 @@ export const SourceBase = { } ] }, + serviceEnabled: { + 'ui:widget': 'hidden' + }, contacts: { "ui:options": { orderable: false @@ -307,7 +310,8 @@ export const SourceEditor = { 'serviceProviderName', 'entityId', 'organization', - 'contacts' + 'contacts', + 'serviceEnabled' ] }, { diff --git a/ui/src/app/metadata/editor/MetadataEditor.js b/ui/src/app/metadata/editor/MetadataEditor.js index 0c8757d97..fac1c1fa7 100644 --- a/ui/src/app/metadata/editor/MetadataEditor.js +++ b/ui/src/app/metadata/editor/MetadataEditor.js @@ -21,14 +21,14 @@ import { checkChanges } from '../hooks/utility'; import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications'; import { useUserGroup } from '../../core/user/UserContext'; -export function MetadataEditor ({ current }) { +export function MetadataEditor ({ restore, current, reload }) { const translator = useTranslator(); const group = useUserGroup(); const { type, id, section } = useParams(); - const { update, loading } = useMetadataUpdater(`${ API_BASE_PATH }${getMetadataPath(type)}`, current); + const { update, loading } = useMetadataUpdater(`${ API_BASE_PATH }${getMetadataPath(type)}`, current, reload); const notificationDispatch = useNotificationDispatcher(); @@ -51,9 +51,8 @@ export function MetadataEditor ({ current }) { .then(() => { gotoDetail({ refresh: true }); }) - .catch(err => { - // window.location.reload(); - notificationDispatch(createNotificationAction(`${err.errorCode} - ${translator(err.errorMessage)}`, NotificationTypes.ERROR)) + .catch((err) => { + notificationDispatch(createNotificationAction(`Updated data with latest changes`, NotificationTypes.INFO)) }); }; @@ -71,10 +70,9 @@ export function MetadataEditor ({ current }) { const resetBlock = blocking; setBlocking(false); setTimeout(() => { - history.push(path); + history.push(restore ? `../${path}/edit` : path); setBlocking(resetBlock); }); - // setBlocking(resetBlock); }; const [blocking, setBlocking] = React.useState(false); @@ -83,7 +81,11 @@ export function MetadataEditor ({ current }) { const warnings = definition.warnings && definition.warnings(metadata); - const canFilter = FilterableProviders.indexOf(definition.type) > -1; + const canFilter = restore ? false : FilterableProviders.indexOf(definition.type) > -1; + + React.useEffect(() => { + dispatch(setFormDataAction(current)); + }, [current, dispatch]) return (
diff --git a/ui/src/app/metadata/editor/MetadataFilterEditor.js b/ui/src/app/metadata/editor/MetadataFilterEditor.js index 2fe4f9ca9..21c764f41 100644 --- a/ui/src/app/metadata/editor/MetadataFilterEditor.js +++ b/ui/src/app/metadata/editor/MetadataFilterEditor.js @@ -60,7 +60,7 @@ export function MetadataFilterEditor({children, onNavigate, block}) {

} - {errors.length === 0 && warnings && warnings.hasOwnProperty(section) && + {warnings && warnings.hasOwnProperty(section) && {warnings[section].map((w, widx) =>

diff --git a/ui/src/app/metadata/hoc/MetadataSchema.js b/ui/src/app/metadata/hoc/MetadataSchema.js index 6ee801c28..025e7a415 100644 --- a/ui/src/app/metadata/hoc/MetadataSchema.js +++ b/ui/src/app/metadata/hoc/MetadataSchema.js @@ -54,6 +54,4 @@ export function useMetadataDefinitionValidator(data, current, group) { return definition.validator(data, current, group, translator); } -//getConfigurationSections - export default MetadataSchema; \ No newline at end of file diff --git a/ui/src/app/metadata/hooks/api.js b/ui/src/app/metadata/hooks/api.js index 49e11f338..9c2d2f90c 100644 --- a/ui/src/app/metadata/hooks/api.js +++ b/ui/src/app/metadata/hooks/api.js @@ -97,7 +97,7 @@ export function useMetadataFilterTypes () { return MetadataFilterTypes; } -export function useMetadataUpdater (path, current) { +export function useMetadataUpdater (path, current, cancel) { const { request, put, get, error, response, ...props } = useFetch(path, { cachePolicy: 'no-cache' }); @@ -111,8 +111,9 @@ export function useMetadataUpdater (path, current) { return new Promise((resolve, reject) => { dispatch(openContentionModalAction(current, latest, body, async (resolution) => { resolve(await update(p, resolution)); - }, () => { - reject(); + }, (err) => { + cancel && cancel(); + reject(err); })); }); } diff --git a/ui/src/app/metadata/hooks/configuration.js b/ui/src/app/metadata/hooks/configuration.js index 0574359a1..0c78cd90f 100644 --- a/ui/src/app/metadata/hooks/configuration.js +++ b/ui/src/app/metadata/hooks/configuration.js @@ -18,5 +18,5 @@ export function useMetadataConfiguration(models, schema, definition, limited = f return {}; } - return getLimitedConfigurationsFn(getConfigurationSections(models, definition, schema), limited); + return getLimitedConfigurationsFn(getConfigurationSections(models, definition, definition.overrideSchema ? definition.overrideSchema(schema, models) : schema), limited); } diff --git a/ui/src/app/metadata/view/MetadataComparison.js b/ui/src/app/metadata/view/MetadataComparison.js index 4d25d0aba..41cc75540 100644 --- a/ui/src/app/metadata/view/MetadataComparison.js +++ b/ui/src/app/metadata/view/MetadataComparison.js @@ -17,6 +17,7 @@ import { useTranslation } from '../../i18n/hooks'; import { MetadataFilterVersionList } from '../domain/filter/component/MetadataFilterVersionList'; import { MetadataFilterVersionContext } from '../domain/filter/component/MetadataFilterVersionContext'; import { useMetadataSchema } from '../hooks/schema'; +import { FilterableProviders } from '../domain/provider'; export function MetadataComparison () { @@ -31,7 +32,9 @@ export function MetadataComparison () { const [limited, setLimited] = React.useState(false); const toggleLimited = useTranslation('action.view-only-changes'); - console.log(type) + + const canFilter = FilterableProviders.indexOf(definition.type) > -1; + return ( <>

@@ -61,7 +64,7 @@ export function MetadataComparison () {

- {type === 'provider' && v && + {type === 'provider' && canFilter && v &&

diff --git a/ui/src/app/metadata/view/MetadataEdit.js b/ui/src/app/metadata/view/MetadataEdit.js index 45d326312..6fcabd933 100644 --- a/ui/src/app/metadata/view/MetadataEdit.js +++ b/ui/src/app/metadata/view/MetadataEdit.js @@ -3,13 +3,13 @@ import { MetadataForm } from '../hoc/MetadataFormContext'; import { MetadataEditor } from '../editor/MetadataEditor'; import { useMetadataObject } from '../hoc/MetadataSelector'; -export function MetadataEdit() { +export function MetadataEdit({reload}) { const base = useMetadataObject(); return ( - + ); } \ No newline at end of file diff --git a/ui/src/app/metadata/view/MetadataRestore.js b/ui/src/app/metadata/view/MetadataRestore.js index 3dd257d4d..7d009c60f 100644 --- a/ui/src/app/metadata/view/MetadataRestore.js +++ b/ui/src/app/metadata/view/MetadataRestore.js @@ -16,7 +16,7 @@ export function MetadataRestore() { ...metadata, version: latest.version }}> - diff --git a/ui/src/app/metadata/view/MetadataUpload.js b/ui/src/app/metadata/view/MetadataUpload.js index 3c6ce4598..28b953ff4 100644 --- a/ui/src/app/metadata/view/MetadataUpload.js +++ b/ui/src/app/metadata/view/MetadataUpload.js @@ -142,7 +142,7 @@ export function MetadataUpload() { —

- + 0 } type="text" className="form-control"{...register('url')} />
diff --git a/ui/src/app/metadata/view/MetadataWizard.js b/ui/src/app/metadata/view/MetadataWizard.js index 4a4b4b5c7..6ba534ffd 100644 --- a/ui/src/app/metadata/view/MetadataWizard.js +++ b/ui/src/app/metadata/view/MetadataWizard.js @@ -7,10 +7,12 @@ import { Wizard } from '../wizard/Wizard'; import { useMetadataEntity } from '../hooks/api'; import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications'; import { Prompt, useHistory } from 'react-router'; +import { useTranslator } from '../../i18n/hooks'; export function MetadataWizard ({type, data, onCallback}) { const history = useHistory(); + const translator = useTranslator(); const { post, loading, response } = useMetadataEntity(type === 'source' ? 'source' : 'provider'); @@ -19,15 +21,21 @@ export function MetadataWizard ({type, data, onCallback}) { const [blocking, setBlocking] = React.useState(false); async function save(metadata) { - console.log(metadata); await post('', metadata); if (response.ok) { setBlocking(false); history.push(`/dashboard/metadata/manager/${type === 'source' ? 'resolvers' : 'providers'}`); } else { - const { errorCode, errorMessage, cause } = response.data; + let msg; + if (response.status) { + const { errorCode, errorMessage, cause } = response.data; + msg = `${errorCode}: ${errorMessage} ${cause ? `-${cause}` : ''}`; + } else { + msg = translator('message.session-timeout'); + } + notificationDispatch(createNotificationAction( - `${errorCode}: ${errorMessage} ${cause ? `-${cause}` : ''}`, + msg, NotificationTypes.ERROR )); } diff --git a/ui/src/app/metadata/wizard/MetadataProviderTypeSelector.js b/ui/src/app/metadata/wizard/MetadataProviderTypeSelector.js index 5a091b2d2..dc4acd4d4 100644 --- a/ui/src/app/metadata/wizard/MetadataProviderTypeSelector.js +++ b/ui/src/app/metadata/wizard/MetadataProviderTypeSelector.js @@ -44,6 +44,8 @@ export function MetadataProviderTypeSelector({ type, types = [], children}) { const providerNames = data.map(p => p.name); + console.log(errors); + return ( <>{showSelector ? <> @@ -82,7 +84,7 @@ export function MetadataProviderTypeSelector({ type, types = [], children}) { - + !(providerNames.indexOf(v) > -1) }})} /> - {errors?.name?.unique && } - {errors?.name?.required && } + {errors?.name?.type === 'unique' && } + {errors?.name?.type === 'required' && } diff --git a/ui/src/app/metadata/wizard/MetadataSourceWizard.js b/ui/src/app/metadata/wizard/MetadataSourceWizard.js index 364d74421..66e75e32b 100644 --- a/ui/src/app/metadata/wizard/MetadataSourceWizard.js +++ b/ui/src/app/metadata/wizard/MetadataSourceWizard.js @@ -12,16 +12,15 @@ import { useMetadataDefinitionContext, useMetadataSchemaContext, useMetadataDefi import { useMetadataFormDispatcher, setFormDataAction, setFormErrorAction, useMetadataFormData, useMetadataFormErrors } from '../hoc/MetadataFormContext'; import { MetadataConfiguration } from '../component/MetadataConfiguration'; import { Configuration } from '../hoc/Configuration'; -import { useMetadataEntity, useMetadataSources } from '../hooks/api'; +import { useMetadataSources } from '../hooks/api'; import Translate from '../../i18n/components/translate'; import { checkChanges } from '../hooks/utility'; import { useUserGroup } from '../../core/user/UserContext'; -export function MetadataSourceWizard ({ onShowNav, onSave, block }) { +export function MetadataSourceWizard ({ onShowNav, onSave, block, loading }) { - const { loading } = useMetadataEntity('source'); const group = useUserGroup(); const { data } = useMetadataSources({ @@ -61,6 +60,8 @@ export function MetadataSourceWizard ({ onShowNav, onSave, block }) { const validator = useMetadataDefinitionValidator(data, null, group); const warnings = definition.warnings && definition.warnings(metadata); + React.useEffect(() => console.log(loading), [loading]); + return ( <>
diff --git a/ui/src/testing/dynamic-http.schema.js b/ui/src/testing/dynamic-http.schema.js index 29d68da8c..a9318f635 100644 --- a/ui/src/testing/dynamic-http.schema.js +++ b/ui/src/testing/dynamic-http.schema.js @@ -1,3 +1,3 @@ -export const schema = { "type": "object", "required": ["name", "@type", "xmlId", "metadataRequestURLConstructionScheme"], "properties": { "name": { "title": "label.metadata-provider-name-dashboard-display-only", "description": "tooltip.metadata-provider-name-dashboard-display-only", "type": "string" }, "@type": { "title": "label.metadata-provider-type", "description": "tooltip.metadata-provider-type", "type": "string", "default": "DynamicHttpMetadataResolver" }, "enabled": { "title": "label.enable-provider-upon-saving", "description": "tooltip.enable-provider-upon-saving", "type": "boolean", "default": false }, "xmlId": { "title": "label.xml-id", "description": "tooltip.xml-id", "type": "string", "minLength": 1 }, "metadataRequestURLConstructionScheme": { "type": "object", "required": ["@type", "content"], "dependencies": { "@type": { "oneOf": [{ "properties": { "@type": { "enum": ["Regex"] }, "match": { "title": "label.match", "description": "tooltip.match", "type": "string", "widget": { "id": "string", "required": true } } }, "required": ["@type", "content", "match"] }, { "properties": { "@type": { "enum": ["MetadataQueryProtocol"] } }, "required": ["@type", "content"] }] } }, "properties": { "@type": { "title": "label.md-request-type", "description": "tooltip.md-request-type", "type": "string", "widget": { "id": "select" }, "enum": ["MetadataQueryProtocol", "Regex"] }, "content": { "title": "label.md-request-value", "description": "tooltip.md-request-value", "type": "string" } } }, "requireValidMetadata": { "title": "label.require-valid-metadata", "description": "tooltip.require-valid-metadata", "type": "boolean" }, "failFastInitialization": { "title": "label.fail-fast-init", "description": "tooltip.fail-fast-init", "type": "boolean" }, "dynamicMetadataResolverAttributes": { "type": "object", "dependencies": { "initializeFromPersistentCacheInBackground": { "oneOf": [{ "properties": { "initializeFromPersistentCacheInBackground": { "enum": [true] }, "backgroundInitializationFromCacheDelay": { "title": "label.background-init-from-cache-delay", "description": "tooltip.background-init-from-cache-delay", "type": "string", "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" } } }, { "properties": { "initializeFromPersistentCacheInBackground": { "enum": [false] } } }] } }, "properties": { "refreshDelayFactor": { "title": "label.refresh-delay-factor", "description": "tooltip.refresh-delay-factor", "type": "number", "multipleOf": 0.01, "minimum": 0.001, "maximum": 0.999 }, "minCacheDuration": { "title": "label.min-cache-duration", "description": "tooltip.min-cache-duration", "type": "string", "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "maxCacheDuration": { "title": "label.max-cache-duration", "description": "tooltip.max-cache-duration", "type": "string", "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "maxIdleEntityData": { "title": "label.max-idle-entity-data", "description": "tooltip.max-idle-entity-data", "type": "string", "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "removeIdleEntityData": { "title": "label.remove-idle-entity-data", "description": "tooltip.remove-idle-entity-data", "type": "boolean" }, "cleanupTaskInterval": { "title": "label.cleanup-task-interval", "description": "tooltip.cleanup-task-interval", "type": "string", "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "persistentCacheManagerDirectory": { "title": "label.persistent-cache-manager-directory", "description": "tooltip.persistent-cache-manager-directory", "type": "string", "minLength": 1 }, "initializeFromPersistentCacheInBackground": { "title": "label.initialize-from-persistent-cache-in-background", "description": "tooltip.initialize-from-persistent-cache-in-background", "type": "boolean" } } }, "httpMetadataResolverAttributes": { "order": [], "type": "object", "fieldsets": [{ "title": "label.http-security-attributes", "type": "section", "class": "col-12", "fields": ["disregardTLSCertificate"] }, { "title": "label.http-connection-attributes", "type": "section", "fields": ["connectionRequestTimeout", "connectionTimeout", "socketTimeout"] }, { "title": "label.http-proxy-attributes", "type": "section", "class": "col-12", "fields": ["proxyHost", "proxyPort", "proxyUser", "proxyPassword"] }, { "title": "label.http-caching-attributes", "type": "section", "class": "col-12", "fields": ["httpCaching", "httpCacheDirectory", "httpMaxCacheEntries", "httpMaxCacheEntrySize"] }, { "title": "", "type": "hidden", "class": "col-12", "fields": ["tlsTrustEngineRef", "httpClientSecurityParametersRef", "httpClientRef"] }], "properties": { "disregardTLSCertificate": { "type": "boolean", "title": "label.disregard-tls-cert", "description": "tooltip.disregard-tls-cert" }, "httpClientRef": { "type": "string", "title": "", "description": "", "widget": "hidden" }, "connectionRequestTimeout": { "type": "string", "title": "label.connection-request-timeout", "description": "tooltip.connection-request-timeout", "widget": { "id": "datalist", "data": ["PT0S", "PT30S", "PT1M", "PT10M", "PT30M", "PT1H", "PT4H", "PT12H", "PT24H"] }, "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "connectionTimeout": { "type": "string", "title": "label.connection-timeout", "description": "tooltip.connection-timeout", "widget": { "id": "datalist", "data": ["PT0S", "PT30S", "PT1M", "PT10M", "PT30M", "PT1H", "PT4H", "PT12H", "PT24H"] }, "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "socketTimeout": { "type": "string", "title": "label.socket-timeout", "description": "tooltip.socket-timeout", "widget": { "id": "datalist", "data": ["PT0S", "PT30S", "PT1M", "PT10M", "PT30M", "PT1H", "PT4H", "PT12H", "PT24H"] }, "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "tlsTrustEngineRef": { "type": "string", "title": "", "description": "" }, "httpClientSecurityParametersRef": { "type": "string", "title": "", "description": "" }, "proxyHost": { "type": "string", "title": "label.proxy-host", "description": "tooltip.proxy-host" }, "proxyPort": { "type": "integer", "title": "label.proxy-port", "description": "tooltip.proxy-port" }, "proxyUser": { "type": "string", "title": "label.proxy-user", "description": "tooltip.proxy-user" }, "proxyPassword": { "type": "string", "title": "label.proxy-password", "description": "tooltip.proxy-password" }, "httpCaching": { "type": "string", "title": "label.http-caching", "description": "tooltip.http-caching", "widget": { "id": "select" }, "oneOf": [{ "enum": ["none"], "description": "value.none" }, { "enum": ["file"], "description": "value.file" }, { "enum": ["memory"], "description": "value.memory" }] }, "httpCacheDirectory": { "type": "string", "title": "label.http-caching-directory", "description": "tooltip.http-caching-directory" }, "httpMaxCacheEntries": { "type": "integer", "title": "label.http-max-cache-entries", "description": "tooltip.http-max-cache-entries", "minimum": 0 }, "httpMaxCacheEntrySize": { "type": "integer", "title": "label.max-cache-entry-size", "description": "tooltip.max-cache-entry-size", "minimum": 0 } } }, "metadataFilters": { "$id": "metadataFilters", "title": "", "description": "", "type": "array", "items": [{ "$id": "RequiredValidUntil", "title": "label.required-valid-until", "type": "object", "widget": { "id": "fieldset" }, "properties": { "@type": { "type": "string", "default": "RequiredValidUntil" }, "maxValidityInterval": { "title": "label.max-validity-interval", "description": "tooltip.max-validity-interval", "type": "string", "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" } } }, { "$id": "SignatureValidation", "title": "label.signature-validation-filter", "type": "object", "widget": { "id": "fieldset" }, "properties": { "@type": { "type": "string", "default": "SignatureValidation" }, "requireSignedRoot": { "title": "label.require-signed-root", "description": "tooltip.require-signed-root", "type": "boolean" }, "certificateFile": { "title": "label.certificate-file", "description": "tooltip.certificate-file", "type": "string" } }, "dependencies": { "requireSignedRoot": { "oneOf": [{ "properties": { "requireSignedRoot": { "enum": [true] } }, "required": ["certificateFile"] }, { "properties": { "requireSignedRoot": { "enum": [false] } } }] } } }, { "$id": "EntityRoleWhiteList", "title": "label.entity-role-whitelist", "type": "object", "widget": { "id": "fieldset" }, "properties": { "@type": { "type": "string", "default": "EntityRoleWhiteList" }, "retainedRoles": { "title": "label.retained-roles", "description": "tooltip.retained-roles", "type": "array", "items": { "widget": { "id": "select" }, "type": "string", "enum": ["SPSSODescriptor", "AttributeAuthorityDescriptor"], "enumNames": ["value.spdescriptor", "value.attr-auth-descriptor"] } }, "removeRolelessEntityDescriptors": { "title": "label.remove-roleless-entity-descriptors", "description": "tooltip.remove-roleless-entity-descriptors", "type": "boolean" }, "removeEmptyEntitiesDescriptors": { "title": "label.remove-empty-entities-descriptors", "description": "tooltip.remove-empty-entities-descriptors", "type": "boolean" } } }] } } }; +export const schema = { "type": "object", "required": ["name", "@type", "xmlId", "metadataRequestURLConstructionScheme"], "properties": { "name": { "title": "label.metadata-provider-name-dashboard-display-only", "description": "tooltip.metadata-provider-name", "type": "string" }, "@type": { "title": "label.metadata-provider-type", "description": "tooltip.metadata-provider-type", "type": "string", "default": "DynamicHttpMetadataResolver" }, "enabled": { "title": "label.enable-provider-upon-saving", "description": "tooltip.enable-provider-upon-saving", "type": "boolean", "default": false }, "xmlId": { "title": "label.xml-id", "description": "tooltip.xml-id", "type": "string", "minLength": 1 }, "metadataRequestURLConstructionScheme": { "type": "object", "required": ["@type", "content"], "dependencies": { "@type": { "oneOf": [{ "properties": { "@type": { "enum": ["Regex"] }, "match": { "title": "label.match", "description": "tooltip.match", "type": "string", "widget": { "id": "string", "required": true } } }, "required": ["@type", "content", "match"] }, { "properties": { "@type": { "enum": ["MetadataQueryProtocol"] } }, "required": ["@type", "content"] }] } }, "properties": { "@type": { "title": "label.md-request-type", "description": "tooltip.md-request-type", "type": "string", "widget": { "id": "select" }, "enum": ["MetadataQueryProtocol", "Regex"] }, "content": { "title": "label.md-request-value", "description": "tooltip.md-request-value", "type": "string" } } }, "requireValidMetadata": { "title": "label.require-valid-metadata", "description": "tooltip.require-valid-metadata", "type": "boolean" }, "failFastInitialization": { "title": "label.fail-fast-init", "description": "tooltip.fail-fast-init", "type": "boolean" }, "dynamicMetadataResolverAttributes": { "type": "object", "dependencies": { "initializeFromPersistentCacheInBackground": { "oneOf": [{ "properties": { "initializeFromPersistentCacheInBackground": { "enum": [true] }, "backgroundInitializationFromCacheDelay": { "title": "label.background-init-from-cache-delay", "description": "tooltip.background-init-from-cache-delay", "type": "string", "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" } } }, { "properties": { "initializeFromPersistentCacheInBackground": { "enum": [false] } } }] } }, "properties": { "refreshDelayFactor": { "title": "label.refresh-delay-factor", "description": "tooltip.refresh-delay-factor", "type": "number", "multipleOf": 0.01, "minimum": 0.001, "maximum": 0.999 }, "minCacheDuration": { "title": "label.min-cache-duration", "description": "tooltip.min-cache-duration", "type": "string", "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "maxCacheDuration": { "title": "label.max-cache-duration", "description": "tooltip.max-cache-duration", "type": "string", "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "maxIdleEntityData": { "title": "label.max-idle-entity-data", "description": "tooltip.max-idle-entity-data", "type": "string", "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "removeIdleEntityData": { "title": "label.remove-idle-entity-data", "description": "tooltip.remove-idle-entity-data", "type": "boolean" }, "cleanupTaskInterval": { "title": "label.cleanup-task-interval", "description": "tooltip.cleanup-task-interval", "type": "string", "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "persistentCacheManagerDirectory": { "title": "label.persistent-cache-manager-directory", "description": "tooltip.persistent-cache-manager-directory", "type": "string", "minLength": 1 }, "initializeFromPersistentCacheInBackground": { "title": "label.initialize-from-persistent-cache-in-background", "description": "tooltip.initialize-from-persistent-cache-in-background", "type": "boolean" } } }, "httpMetadataResolverAttributes": { "order": [], "type": "object", "fieldsets": [{ "title": "label.http-security-attributes", "type": "section", "class": "col-12", "fields": ["disregardTLSCertificate"] }, { "title": "label.http-connection-attributes", "type": "section", "fields": ["connectionRequestTimeout", "connectionTimeout", "socketTimeout"] }, { "title": "label.http-proxy-attributes", "type": "section", "class": "col-12", "fields": ["proxyHost", "proxyPort", "proxyUser", "proxyPassword"] }, { "title": "label.http-caching-attributes", "type": "section", "class": "col-12", "fields": ["httpCaching", "httpCacheDirectory", "httpMaxCacheEntries", "httpMaxCacheEntrySize"] }, { "title": "", "type": "hidden", "class": "col-12", "fields": ["tlsTrustEngineRef", "httpClientSecurityParametersRef", "httpClientRef"] }], "properties": { "disregardTLSCertificate": { "type": "boolean", "title": "label.disregard-tls-cert", "description": "tooltip.disregard-tls-cert" }, "httpClientRef": { "type": "string", "title": "", "description": "", "widget": "hidden" }, "connectionRequestTimeout": { "type": "string", "title": "label.connection-request-timeout", "description": "tooltip.connection-request-timeout", "widget": { "id": "datalist", "data": ["PT0S", "PT30S", "PT1M", "PT10M", "PT30M", "PT1H", "PT4H", "PT12H", "PT24H"] }, "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "connectionTimeout": { "type": "string", "title": "label.connection-timeout", "description": "tooltip.connection-timeout", "widget": { "id": "datalist", "data": ["PT0S", "PT30S", "PT1M", "PT10M", "PT30M", "PT1H", "PT4H", "PT12H", "PT24H"] }, "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "socketTimeout": { "type": "string", "title": "label.socket-timeout", "description": "tooltip.socket-timeout", "widget": { "id": "datalist", "data": ["PT0S", "PT30S", "PT1M", "PT10M", "PT30M", "PT1H", "PT4H", "PT12H", "PT24H"] }, "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, "tlsTrustEngineRef": { "type": "string", "title": "", "description": "" }, "httpClientSecurityParametersRef": { "type": "string", "title": "", "description": "" }, "proxyHost": { "type": "string", "title": "label.proxy-host", "description": "tooltip.proxy-host" }, "proxyPort": { "type": "integer", "title": "label.proxy-port", "description": "tooltip.proxy-port" }, "proxyUser": { "type": "string", "title": "label.proxy-user", "description": "tooltip.proxy-user" }, "proxyPassword": { "type": "string", "title": "label.proxy-password", "description": "tooltip.proxy-password" }, "httpCaching": { "type": "string", "title": "label.http-caching", "description": "tooltip.http-caching", "widget": { "id": "select" }, "oneOf": [{ "enum": ["none"], "description": "value.none" }, { "enum": ["file"], "description": "value.file" }, { "enum": ["memory"], "description": "value.memory" }] }, "httpCacheDirectory": { "type": "string", "title": "label.http-caching-directory", "description": "tooltip.http-caching-directory" }, "httpMaxCacheEntries": { "type": "integer", "title": "label.http-max-cache-entries", "description": "tooltip.http-max-cache-entries", "minimum": 0 }, "httpMaxCacheEntrySize": { "type": "integer", "title": "label.max-cache-entry-size", "description": "tooltip.max-cache-entry-size", "minimum": 0 } } }, "metadataFilters": { "$id": "metadataFilters", "title": "", "description": "", "type": "array", "items": [{ "$id": "RequiredValidUntil", "title": "label.required-valid-until", "type": "object", "widget": { "id": "fieldset" }, "properties": { "@type": { "type": "string", "default": "RequiredValidUntil" }, "maxValidityInterval": { "title": "label.max-validity-interval", "description": "tooltip.max-validity-interval", "type": "string", "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" } } }, { "$id": "SignatureValidation", "title": "label.signature-validation-filter", "type": "object", "widget": { "id": "fieldset" }, "properties": { "@type": { "type": "string", "default": "SignatureValidation" }, "requireSignedRoot": { "title": "label.require-signed-root", "description": "tooltip.require-signed-root", "type": "boolean" }, "certificateFile": { "title": "label.certificate-file", "description": "tooltip.certificate-file", "type": "string" } }, "dependencies": { "requireSignedRoot": { "oneOf": [{ "properties": { "requireSignedRoot": { "enum": [true] } }, "required": ["certificateFile"] }, { "properties": { "requireSignedRoot": { "enum": [false] } } }] } } }, { "$id": "EntityRoleWhiteList", "title": "label.entity-role-whitelist", "type": "object", "widget": { "id": "fieldset" }, "properties": { "@type": { "type": "string", "default": "EntityRoleWhiteList" }, "retainedRoles": { "title": "label.retained-roles", "description": "tooltip.retained-roles", "type": "array", "items": { "widget": { "id": "select" }, "type": "string", "enum": ["SPSSODescriptor", "AttributeAuthorityDescriptor"], "enumNames": ["value.spdescriptor", "value.attr-auth-descriptor"] } }, "removeRolelessEntityDescriptors": { "title": "label.remove-roleless-entity-descriptors", "description": "tooltip.remove-roleless-entity-descriptors", "type": "boolean" }, "removeEmptyEntitiesDescriptors": { "title": "label.remove-empty-entities-descriptors", "description": "tooltip.remove-empty-entities-descriptors", "type": "boolean" } } }] } } }; export default schema; \ No newline at end of file diff --git a/ui/src/testing/uiSchema.js b/ui/src/testing/uiSchema.js index c1af6f4fa..8da257247 100644 --- a/ui/src/testing/uiSchema.js +++ b/ui/src/testing/uiSchema.js @@ -65,6 +65,9 @@ const schema = { ], "ui:widget": "hidden" }, + "serviceEnabled": { + "ui:widget": "hidden" + }, "contacts": { "ui:options": { "orderable": false diff --git a/ui/src/theme/project/buttons.scss b/ui/src/theme/project/buttons.scss index c1e8c5d79..df4a903ac 100644 --- a/ui/src/theme/project/buttons.scss +++ b/ui/src/theme/project/buttons.scss @@ -1,6 +1,10 @@ @import '../variables'; @import '~bootstrap/scss/_mixins'; +.btn { + -webkit-appearance: none; +} + .btn.btn-text { display: inline; padding: 0px; diff --git a/ui/src/theme/project/typography.scss b/ui/src/theme/project/typography.scss index 058e6666d..dbc6b09a0 100644 --- a/ui/src/theme/project/typography.scss +++ b/ui/src/theme/project/typography.scss @@ -18,4 +18,11 @@ line-height: 1.5; border-radius: 0.2rem; display: block; +} + +.navbar-light .navbar-text { + font-size: 1rem; + font-weight: normal; + margin: 0; + color: rgba(0, 0, 0, 0.75); } \ No newline at end of file