diff --git a/ui/src/app/metadata/component/DeleteConfirmation.js b/ui/src/app/metadata/component/DeleteConfirmation.js index 0df156370..a0f2f8c09 100644 --- a/ui/src/app/metadata/component/DeleteConfirmation.js +++ b/ui/src/app/metadata/component/DeleteConfirmation.js @@ -7,16 +7,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; import Translate from '../../i18n/components/translate'; -export function DeleteConfirmation({ children, onConfirm, onCancel, body, title }) { +export function DeleteConfirmation({ children, body, title }) { const [deleting, setDeleting] = React.useState(false); const ref = React.useRef(); const onConfirmClick = () => { - if (onConfirm) { - onConfirm(); - } if (ref.current && typeof ref.current === 'function') { ref.current(); } @@ -24,9 +21,6 @@ export function DeleteConfirmation({ children, onConfirm, onCancel, body, title } const onCancelClick = () => { - if (onCancel) { - onCancel(); - } setDeleting(false); }; @@ -47,9 +41,9 @@ export function DeleteConfirmation({ children, onConfirm, onCancel, body, title

- {' '} + diff --git a/ui/src/app/metadata/component/DeleteConfirmation.test.js b/ui/src/app/metadata/component/DeleteConfirmation.test.js new file mode 100644 index 000000000..8aa4a57ee --- /dev/null +++ b/ui/src/app/metadata/component/DeleteConfirmation.test.js @@ -0,0 +1,46 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { act } from 'react-dom/test-utils'; + +import { DeleteConfirmation } from './DeleteConfirmation'; + +jest.mock('../../i18n/components/translate', () => { + return 'span'; +}) + +const noop = jest.fn(); + +let container; + +beforeEach(() => { + container = document.createElement('div'); + document.body.appendChild(container); +}); + +afterEach(() => { + document.body.removeChild(container); + container = null; +}); + +test('Delete confirmation', () => { + act(() => { + ReactDOM.render( + + {(block) => } + , + container); + }); + + const initiator = container.querySelector('button'); + + act(() => { + initiator.dispatchEvent(new MouseEvent('click', { bubbles: true })); + }); + + let modal = container.querySelector('.modal'); + const confirm = container.querySelector('.btn-danger'); + + expect(modal).toBeDefined(); + expect(confirm).toBeDefined(); + +}); \ No newline at end of file diff --git a/ui/src/app/metadata/domain/provider/BaseProviderDefinition.js b/ui/src/app/metadata/domain/provider/BaseProviderDefinition.js index 7afaca89b..948a43c39 100644 --- a/ui/src/app/metadata/domain/provider/BaseProviderDefinition.js +++ b/ui/src/app/metadata/domain/provider/BaseProviderDefinition.js @@ -174,9 +174,6 @@ export const HttpMetadataResolverAttributesSchema = { options: DurationOptions, 'ui:placeholder': 'label.duration' }, - proxyPort: { - 'ui:widget': 'updown' - }, httpClientRef: { 'ui:widget': 'hidden' }, diff --git a/ui/src/app/metadata/domain/provider/BaseProviderDefinition.test.js b/ui/src/app/metadata/domain/provider/BaseProviderDefinition.test.js new file mode 100644 index 000000000..d58cfe9b9 --- /dev/null +++ b/ui/src/app/metadata/domain/provider/BaseProviderDefinition.test.js @@ -0,0 +1,160 @@ +import { BaseProviderDefinition } from './BaseProviderDefinition'; +import schema from '../../../../testing/dynamic-http.schema'; +const addErrorMockFn = jest.fn(); + +const providers = [ + { + resourceId: 1, + name: 'foo', + xmlId: 'bar' + }, + { + resourceId: 2, + name: 'baz', + xmlId: 'xmlId' + } +]; + +const e = { + name: { addError: addErrorMockFn }, + xmlId: { addError: addErrorMockFn } +}; + +describe('validator function', () => { + it('should validate against the providers', () => { + const validator = BaseProviderDefinition.validator(providers, 2); + const errors = validator({ + name: 'foo', + xmlId: 'xmlId' + }, e); + + expect(addErrorMockFn).toHaveBeenCalledTimes(2); + }); + + it('should check against the current id', () => { + const validator = BaseProviderDefinition.validator(providers, 1); + const errors = validator({ + name: 'foo', + xmlId: 'naz' + }, e); + + expect(addErrorMockFn).toHaveBeenCalledTimes(1); + }); +}); + +describe('parser function', () => { + + const base = { + metadataFilters: [ + { + resourceId: 'foo', + name: 'foo', + '@type': 'NameIDFormat' + } + ] + }; + + const changes = { + metadataFilters: [ + { + '@type': "RequiredValidUntil", + audId: 19, + createdBy: "root", + createdDate: "2021-06-11T13:29:18.384219", + current: false, + filterEnabled: false, + maxValidityInterval: "PT30S", + resourceId: "299feea8-3e2c-433e-bc2e-48d28bf84a8c" + } + ] + }; + + it('should validate against the providers', () => { + const parsed = BaseProviderDefinition.parser( + changes, base + ); + + expect(parsed.metadataFilters.length).toBe(2); + }); +}); + +describe('formatter function', () => { + + const md = { + metadataFilters: [ + { + resourceId: 'foo', + name: 'foo', + '@type': 'NameIDFormat' + }, + { + '@type': "RequiredValidUntil", + audId: 19, + createdBy: "root", + createdDate: "2021-06-11T13:29:18.384219", + current: false, + filterEnabled: false, + maxValidityInterval: "PT30S", + resourceId: "299feea8-3e2c-433e-bc2e-48d28bf84a8c" + } + ] + }; + + it('should validate against the providers', () => { + const parsed = BaseProviderDefinition.formatter( + md, schema + ); + + expect(parsed.metadataFilters.length).toBe(3); + }); + + it('should return no changes if no filters are on the provider', () => { + const parsed = BaseProviderDefinition.formatter( + {}, schema + ); + + expect(parsed.metadataFilters).toBeUndefined(); + }); + + it('should return no changes if no provider is passed', () => { + const parsed = BaseProviderDefinition.formatter( + null, schema + ); + + expect(parsed).toBeNull(); + }); +}); + +describe('display function', () => { + + const md = { + metadataFilters: [ + { + resourceId: 'foo', + name: 'foo', + '@type': 'NameIDFormat' + }, + { + '@type': "RequiredValidUntil", + audId: 19, + createdBy: "root", + createdDate: "2021-06-11T13:29:18.384219", + current: false, + filterEnabled: false, + maxValidityInterval: "PT30S", + resourceId: "299feea8-3e2c-433e-bc2e-48d28bf84a8c" + } + ] + }; + + it('should validate against the providers', () => { + const parsed = BaseProviderDefinition.display(md); + expect(Array.isArray(parsed)).toBe(false); + }); + + it('should return an unmodified provider if no filters are present', () => { + const local = {}; + const parsed = BaseProviderDefinition.display(local); + expect(parsed).toEqual(local); + }); +}); \ No newline at end of file diff --git a/ui/src/app/metadata/domain/source/SourceDefinition.test.js b/ui/src/app/metadata/domain/source/SourceDefinition.test.js new file mode 100644 index 000000000..130cb1110 --- /dev/null +++ b/ui/src/app/metadata/domain/source/SourceDefinition.test.js @@ -0,0 +1,15 @@ +import { SourceBase } from './SourceDefinition'; + +describe('SourceDefinition', () => { + describe('parser', () => { + it('should remove null values', () => { + expect(SourceBase.parser({ + foo: null, + bar: 'baz', + baz: { + bar: null + } + })).toEqual({bar: 'baz'}); + }); + }) +}); \ No newline at end of file diff --git a/ui/src/app/metadata/editor/MetadataEditor.js b/ui/src/app/metadata/editor/MetadataEditor.js index e76b4e267..c38766404 100644 --- a/ui/src/app/metadata/editor/MetadataEditor.js +++ b/ui/src/app/metadata/editor/MetadataEditor.js @@ -35,7 +35,6 @@ export function MetadataEditor ({ current }) { const { state, dispatch } = React.useContext(MetadataFormContext); const { metadata, errors } = state; - const onChange = (changes) => { dispatch(setFormDataAction(changes.formData)); dispatch(setFormErrorAction(changes.errors)); diff --git a/ui/src/testing/dynamic-http.schema.js b/ui/src/testing/dynamic-http.schema.js new file mode 100644 index 000000000..29d68da8c --- /dev/null +++ b/ui/src/testing/dynamic-http.schema.js @@ -0,0 +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 default schema; \ No newline at end of file diff --git a/ui/yarn.lock b/ui/yarn.lock index dcbc3d1c9..36cf32e13 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -9400,6 +9400,11 @@ react-infinite-scroll-component@^6.1.0: dependencies: throttle-debounce "^2.1.0" +"react-is@^16.12.0 || ^17.0.0", react-is@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.9.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -9564,6 +9569,24 @@ react-scroll@^1.8.2: lodash.throttle "^4.1.1" prop-types "^15.7.2" +react-shallow-renderer@^16.13.1: + version "16.14.1" + resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz#bf0d02df8a519a558fd9b8215442efa5c840e124" + integrity sha512-rkIMcQi01/+kxiTE9D3fdS959U1g7gs+/rborw++42m1O9FAQiNI/UNRZExVUoAOprn4umcXf+pFRou8i4zuBg== + dependencies: + object-assign "^4.1.1" + react-is "^16.12.0 || ^17.0.0" + +react-test-renderer@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c" + integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ== + dependencies: + object-assign "^4.1.1" + react-is "^17.0.2" + react-shallow-renderer "^16.13.1" + scheduler "^0.20.2" + react-transition-group@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"