From 8db84a6e20d695d6cc665169e460aab99d980cee Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 18 Jun 2021 14:19:56 -0700 Subject: [PATCH 1/6] fixed typeahead --- ui/src/app/form/component/widgets/OptionWidget.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/form/component/widgets/OptionWidget.js b/ui/src/app/form/component/widgets/OptionWidget.js index 142941dd3..534595ead 100644 --- a/ui/src/app/form/component/widgets/OptionWidget.js +++ b/ui/src/app/form/component/widgets/OptionWidget.js @@ -80,7 +80,7 @@ const OptionWidget = ({ } }; - const defaultInputValue = typeof value === 'object' && value && value.label ? value.label : value; + const defaultInputValue = typeof value === 'object' && value && value.label ? value.label : value ? value : ''; const [ inputValue, setInputValue ] = React.useState( defaultInputValue ); From ae463d006904b6a8ea7e9da3c0987712dce2a5b5 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 23 Jun 2021 09:59:42 -0700 Subject: [PATCH 2/6] Added restoration confirmation page --- ui/src/app/core/components/FormattedDate.js | 11 ++- ui/src/app/metadata/Metadata.js | 6 +- .../app/metadata/hoc/MetadataVersionLoader.js | 10 +-- .../metadata/view/MetadataConfirmRestore.js | 70 +++++++++++++++++++ ui/src/app/metadata/view/MetadataRestore.js | 6 +- ui/src/app/metadata/view/MetadataVersion.js | 11 ++- 6 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 ui/src/app/metadata/view/MetadataConfirmRestore.js diff --git a/ui/src/app/core/components/FormattedDate.js b/ui/src/app/core/components/FormattedDate.js index e9298af73..4d74cc26d 100644 --- a/ui/src/app/core/components/FormattedDate.js +++ b/ui/src/app/core/components/FormattedDate.js @@ -1,8 +1,17 @@ import React from 'react'; import { format, parseISO } from 'date-fns'; +export function useDateFormatter() { + return (date, time) => format(parseISO(date), `MMM d, Y${time ? ' HH:mm:ss' : ''}`); +} + +export function useFormattedDate (date, time = false) { + const formatter = useDateFormatter(); + return React.useMemo(() => formatter(date, time), [date, time, formatter]); +} + export function FormattedDate ({ date, time = false }) { - const formatted = React.useMemo(() => format(parseISO(date), `MMM d, Y${time ? ' HH:mm:ss' : ''}`), [date, time]); + const formatted = useFormattedDate(date, time); return (<>{ formatted }); } diff --git a/ui/src/app/metadata/Metadata.js b/ui/src/app/metadata/Metadata.js index a9ff0b182..0d1e38b07 100644 --- a/ui/src/app/metadata/Metadata.js +++ b/ui/src/app/metadata/Metadata.js @@ -11,6 +11,7 @@ import { MetadataComparison } from './view/MetadataComparison'; import { MetadataVersion } from './view/MetadataVersion'; import { MetadataEdit } from './view/MetadataEdit'; import { MetadataRestore } from './view/MetadataRestore'; +import { MetadataConfirmRestore } from './view/MetadataConfirmRestore'; export function Metadata () { @@ -51,7 +52,10 @@ export function Metadata () { } /> - + + + } /> + } /> diff --git a/ui/src/app/metadata/hoc/MetadataVersionLoader.js b/ui/src/app/metadata/hoc/MetadataVersionLoader.js index b14e0f59a..e1d17747d 100644 --- a/ui/src/app/metadata/hoc/MetadataVersionLoader.js +++ b/ui/src/app/metadata/hoc/MetadataVersionLoader.js @@ -12,9 +12,7 @@ export function MetadataVersionLoader({children}) { const [metadata, setMetadata] = React.useState(); - const { get, response } = useMetadataEntity(type, { - cachePolicy: 'no-cache', - }, []); + const { get, response, loading } = useMetadataEntity(type, {}, []); async function loadVersion(v) { const l = await get(`/${id}/Versions/${v}`); @@ -29,10 +27,6 @@ export function MetadataVersionLoader({children}) { }, [versionId]); return ( - <> - {metadata && - {children(metadata)} - } - + {children(metadata, loading)} ); } diff --git a/ui/src/app/metadata/view/MetadataConfirmRestore.js b/ui/src/app/metadata/view/MetadataConfirmRestore.js new file mode 100644 index 000000000..abf791fbb --- /dev/null +++ b/ui/src/app/metadata/view/MetadataConfirmRestore.js @@ -0,0 +1,70 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { MetadataVersionLoader } from '../hoc/MetadataVersionLoader'; +import { Translate } from '../../i18n/components/translate'; +import { useDateFormatter } from '../../core/components/FormattedDate'; +import { faSpinner } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +export function MetadataConfirmRestore () { + + const formatter = useDateFormatter(); + + return ( + + {(metadata, loading) => +
+
+
+ + <>{loading ? +
+ + Loading... +
+ : + <>{metadata && + <> +

+ + Restore Version ( date ) + +

+
+
+
+

+ Create New Version from Previous Settings +

+

+ Restoring this version will copy the Version ( date ) configuration and create a new Version from the selected version settings. You can then edit the configuration before saving the new version. +

+ Cancel + Restore +
+
+
+ + } + } + + + +
+
+
+ } +
+ ) +} \ 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 9511c4c92..3dd257d4d 100644 --- a/ui/src/app/metadata/view/MetadataRestore.js +++ b/ui/src/app/metadata/view/MetadataRestore.js @@ -10,7 +10,8 @@ export function MetadataRestore() { return ( - {(metadata) => + {(metadata, loading) => <> + {metadata && - } + } + } ); } diff --git a/ui/src/app/metadata/view/MetadataVersion.js b/ui/src/app/metadata/view/MetadataVersion.js index 3bde185fa..16fda5c3e 100644 --- a/ui/src/app/metadata/view/MetadataVersion.js +++ b/ui/src/app/metadata/view/MetadataVersion.js @@ -5,7 +5,7 @@ import { Configuration } from '../hoc/Configuration'; import { MetadataConfiguration } from '../component/MetadataConfiguration'; import { Link, useParams } from 'react-router-dom'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faArrowUp, faHistory, faPlus } from '@fortawesome/free-solid-svg-icons'; +import { faArrowUp, faHistory, faPlus, faSpinner } from '@fortawesome/free-solid-svg-icons'; import { scroller } from 'react-scroll'; @@ -35,7 +35,13 @@ export function MetadataVersion() { return ( - {(metadata) => + {(metadata, loading) => + <> + {loading &&
+ + Loading... +
} + {metadata && {(config) => <> @@ -87,6 +93,7 @@ export function MetadataVersion() { } + } }
); From dd441c56772e44a166441d849cddbef4f2b0e070 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 23 Jun 2021 10:14:23 -0700 Subject: [PATCH 3/6] Fixed issue with loading sources --- ui/src/app/dashboard/view/Dashboard.js | 2 +- ui/src/app/metadata/hooks/api.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/app/dashboard/view/Dashboard.js b/ui/src/app/dashboard/view/Dashboard.js index 5869e5695..d7892f028 100644 --- a/ui/src/app/dashboard/view/Dashboard.js +++ b/ui/src/app/dashboard/view/Dashboard.js @@ -40,7 +40,7 @@ export function Dashboard () { } async function loadSources() { - const s = sourceLoader.get(); + const s = await sourceLoader.get(); if (response.ok) { setSources(s); } diff --git a/ui/src/app/metadata/hooks/api.js b/ui/src/app/metadata/hooks/api.js index d951b66a4..f628e847d 100644 --- a/ui/src/app/metadata/hooks/api.js +++ b/ui/src/app/metadata/hooks/api.js @@ -24,7 +24,7 @@ export function getMetadataPath(type) { } export function useNonAdminSources() { - return useFetch(`/${getMetadataPath('source')}/disabledNonAdmin`); + return useFetch(`${API_BASE_PATH}${getMetadataPath('source')}/disabledNonAdmin`); } export function getMetadataListPath(type) { From 3ca04e0da933ad8aca2ade6e5b00b7ca48e65d0b Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 23 Jun 2021 10:34:59 -0700 Subject: [PATCH 4/6] Fixed script validation --- ui/src/app/form/component/fields/FilterTargetField.js | 6 +++--- .../domain/filter/EntityAttributesFilterDefinition.js | 7 +++++++ .../app/metadata/domain/filter/NameIdFilterDefinition.js | 7 +++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/ui/src/app/form/component/fields/FilterTargetField.js b/ui/src/app/form/component/fields/FilterTargetField.js index 2bfd8484d..c273f3462 100644 --- a/ui/src/app/form/component/fields/FilterTargetField.js +++ b/ui/src/app/form/component/fields/FilterTargetField.js @@ -193,13 +193,13 @@ const FilterTargetField = ({ className="codearea form-control" rows="8" onChange={({ target: { value } }) => handleTextChange(value)} - html={ selectedTarget[0] } + html={ selectedTarget[0] ? selectedTarget[0] : '' } innerRef={ref} dangerouslySetInnerHTML={true}> - + {!selectedTarget[0] && Required for Scripts - + } } {targetType === 'REGEX' && <> diff --git a/ui/src/app/metadata/domain/filter/EntityAttributesFilterDefinition.js b/ui/src/app/metadata/domain/filter/EntityAttributesFilterDefinition.js index 727df9f8f..036557706 100644 --- a/ui/src/app/metadata/domain/filter/EntityAttributesFilterDefinition.js +++ b/ui/src/app/metadata/domain/filter/EntityAttributesFilterDefinition.js @@ -53,6 +53,13 @@ export const EntityAttributesFilterWizard = { errors.entityAttributesFilterTarget.value.addError('message.invalid-regex-pattern'); } } + + if (formData?.entityAttributesFilterTarget?.entityAttributesFilterTargetType === 'CONDITION_SCRIPT') { + const { entityAttributesFilterTarget: { value } } = formData; + if (!value[0]) { + errors.entityAttributesFilterTarget.value.addError('message.required-for-scripts'); + } + } return errors; } }, diff --git a/ui/src/app/metadata/domain/filter/NameIdFilterDefinition.js b/ui/src/app/metadata/domain/filter/NameIdFilterDefinition.js index ff16383e9..f092b5813 100644 --- a/ui/src/app/metadata/domain/filter/NameIdFilterDefinition.js +++ b/ui/src/app/metadata/domain/filter/NameIdFilterDefinition.js @@ -41,6 +41,13 @@ export const NameIDFilterWizard = { errors.nameIdFormatFilterTarget.value.addError('message.invalid-regex-pattern'); } } + + if (formData?.nameIdFormatFilterTarget?.nameIdFormatFilterTargetType === 'CONDITION_SCRIPT') { + const { nameIdFormatFilterTarget: { value } } = formData; + if (!value[0]) { + errors.nameIdFormatFilterTarget.value.addError('message.required-for-scripts'); + } + } return errors; } }, From 93f6527a99f2aaba5f48d639089b53535bde8336 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 23 Jun 2021 12:16:38 -0700 Subject: [PATCH 5/6] Fixed script editor --- ui/package-lock.json | 21 +++---------- ui/package.json | 2 +- .../component/fields/FilterTargetField.js | 31 ++++++++++--------- ui/src/theme/project/forms.scss | 18 +++++++---- 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index ab86d547d..8cbf388c2 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -13213,22 +13213,6 @@ } } }, - "react-contenteditable": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/react-contenteditable/-/react-contenteditable-3.3.5.tgz", - "integrity": "sha512-38A7hlRQfb2KQAQT0kIJC2YlQUU7jcyYM4eh1fj6kAYb3Hmk6hHlr0snelyxVSpPXjPdFllrnSsPkzUS5AtrEA==", - "requires": { - "fast-deep-equal": "^2.0.1", - "prop-types": "^15.7.1" - }, - "dependencies": { - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - } - } - }, "react-dev-utils": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", @@ -13607,6 +13591,11 @@ "prop-types": "^15.7.2" } }, + "react-simple-code-editor": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-simple-code-editor/-/react-simple-code-editor-0.11.0.tgz", + "integrity": "sha512-xGfX7wAzspl113ocfKQAR8lWPhavGWHL3xSzNLeseDRHysT+jzRBi/ExdUqevSMos+7ZtdfeuBOXtgk9HTwsrw==" + }, "react-transition-group": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", diff --git a/ui/package.json b/ui/package.json index 1046146a3..80e8551f8 100644 --- a/ui/package.json +++ b/ui/package.json @@ -20,12 +20,12 @@ "react": "^17.0.2", "react-bootstrap": "^1.5.2", "react-bootstrap-typeahead": "^5.1.4", - "react-contenteditable": "^3.3.5", "react-dom": "^17.0.2", "react-hook-form": "^7.5.2", "react-infinite-scroll-component": "^6.1.0", "react-router-dom": "^5.2.0", "react-scroll": "^1.8.2", + "react-simple-code-editor": "^0.11.0", "use-http": "^1.0.20", "use-query-params": "^1.2.2", "web-vitals": "^1.0.1" diff --git a/ui/src/app/form/component/fields/FilterTargetField.js b/ui/src/app/form/component/fields/FilterTargetField.js index c273f3462..959306b71 100644 --- a/ui/src/app/form/component/fields/FilterTargetField.js +++ b/ui/src/app/form/component/fields/FilterTargetField.js @@ -6,12 +6,15 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faAsterisk, faCaretDown, faCaretUp, faEye, faEyeSlash, faPlus, faSpinner, faTrash } from '@fortawesome/free-solid-svg-icons'; import { useTranslator } from '../../../i18n/hooks'; import { InfoIcon } from '../InfoIcon'; -import ContentEditable from 'react-contenteditable'; import { AsyncTypeahead } from 'react-bootstrap-typeahead'; import useFetch from 'use-http'; import queryString from 'query-string'; import API_BASE_PATH from '../../../App.constant'; import isNil from 'lodash/isNil'; +import Editor from 'react-simple-code-editor'; +// import { highlight, languages } from 'prismjs/components/prism-core'; +// import 'prismjs/components/prism-clike'; +// import 'prismjs/components/prism-javascript'; import { FilterTargetPreview } from '../../../metadata/hoc/FilterTargetPreview'; @@ -89,8 +92,6 @@ const FilterTargetField = ({ const displayType = selectedType?.label || ''; const targetType = selectedType?.value || null; - const ref = React.useRef(selectedTarget[0]); - var handleTextChange = function (value) { setSelectedTarget([value]); }; @@ -187,20 +188,22 @@ const FilterTargetField = ({ } { targetType === 'CONDITION_SCRIPT' && - <> - handleTextChange(value)} - html={ selectedTarget[0] ? selectedTarget[0] : '' } - innerRef={ref} - dangerouslySetInnerHTML={true}> - +
+ code} + onValueChange={(code) => handleTextChange(code)} + padding={10} + className={`codearea border rounded ${!selectedTarget[0] && 'is-invalid border-danger'}`} + style={{ + fontFamily: 'monospace', + fontSize: 15, + }}> + {!selectedTarget[0] && Required for Scripts } - } +
} {targetType === 'REGEX' && <> pre, &.is-invalid > textarea { + outline-color: transparent; } } @@ -123,3 +128,4 @@ mark { border-left: 0px; } } + From edd222fc492670302349ac571910a83882ec15a8 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 23 Jun 2021 13:25:03 -0700 Subject: [PATCH 6/6] Fixed issues with filters --- backend/src/main/resources/nameid-filter.schema.json | 2 +- .../metadata/component/properties/PrimitiveProperty.js | 6 +++++- .../app/metadata/component/properties/PropertyValue.js | 5 ++++- ui/src/app/metadata/hooks/api.js | 6 +++++- ui/src/app/metadata/new/NewFilter.js | 7 ++++++- ui/src/app/metadata/view/EditFilter.js | 10 +++++++--- ui/src/app/metadata/view/MetadataUpload.js | 2 -- 7 files changed, 28 insertions(+), 10 deletions(-) diff --git a/backend/src/main/resources/nameid-filter.schema.json b/backend/src/main/resources/nameid-filter.schema.json index d6d02c84f..0865d8f89 100644 --- a/backend/src/main/resources/nameid-filter.schema.json +++ b/backend/src/main/resources/nameid-filter.schema.json @@ -21,7 +21,7 @@ "type": "object", "properties": { "nameIdFormatFilterTargetType": { - "title": "", + "title": "label.filter-target-type", "type": "string", "default": "ENTITY", "enum": [ diff --git a/ui/src/app/metadata/component/properties/PrimitiveProperty.js b/ui/src/app/metadata/component/properties/PrimitiveProperty.js index f86a798b7..96440749f 100644 --- a/ui/src/app/metadata/component/properties/PrimitiveProperty.js +++ b/ui/src/app/metadata/component/properties/PrimitiveProperty.js @@ -9,6 +9,10 @@ export function PrimitiveProperty ({ property, columns }) { const width = usePropertyWidth(columns); + const getValue = (v) => { + return property.enum && property.enumNames ? property.enumNames[property.enum.indexOf(v)] : v; + } + return (
{property.differences && Changed: } @@ -21,7 +25,7 @@ export function PrimitiveProperty ({ property, columns }) { { property.name } {property.value.map((v, valIdx) => - + ) }
diff --git a/ui/src/app/metadata/component/properties/PropertyValue.js b/ui/src/app/metadata/component/properties/PropertyValue.js index 807c86e61..0476fdb01 100644 --- a/ui/src/app/metadata/component/properties/PropertyValue.js +++ b/ui/src/app/metadata/component/properties/PropertyValue.js @@ -3,6 +3,7 @@ import Popover from 'react-bootstrap/Popover'; import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; import { usePropertyWidth } from './hooks'; +import Translate from '../../../i18n/components/translate'; export function PropertyValue ({ name, value, columns, className }) { @@ -20,7 +21,9 @@ export function PropertyValue ({ name, value, columns, className }) { className={`d-block text-truncate ${className}`} role="definition" style={columns ? { width } : {}}> - {value !== undefined ? value.toString() : (value === false) ? value.toString() : '-'} + + {value !== undefined ? value.toString() : (value === false) ? value.toString() : '-'} + : -} diff --git a/ui/src/app/metadata/hooks/api.js b/ui/src/app/metadata/hooks/api.js index d951b66a4..f5b64c25c 100644 --- a/ui/src/app/metadata/hooks/api.js +++ b/ui/src/app/metadata/hooks/api.js @@ -109,7 +109,11 @@ export function useMetadataUpdater (path, current) { })); }); } - return Promise.resolve(req); + if (response.ok) { + return Promise.resolve(req); + } else { + return Promise.reject(req); + } } return { diff --git a/ui/src/app/metadata/new/NewFilter.js b/ui/src/app/metadata/new/NewFilter.js index 5fce5d31f..4aacb1574 100644 --- a/ui/src/app/metadata/new/NewFilter.js +++ b/ui/src/app/metadata/new/NewFilter.js @@ -9,12 +9,14 @@ import { MetadataForm } from '../hoc/MetadataFormContext'; import { MetadataSchema } from '../hoc/MetadataSchema'; import { useMetadataFilters, useMetadataFilterTypes } from '../hooks/api'; import { MetadataFilterTypeSelector } from '../wizard/MetadataFilterTypeSelector'; +import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications'; export function NewFilter() { const { id, section } = useParams(); const history = useHistory(); const types = useMetadataFilterTypes(); + const dispatch = useNotificationDispatcher(); const { post, response, loading } = useMetadataFilters(id, {}); @@ -22,9 +24,12 @@ export function NewFilter() { async function save(metadata) { - await post(``, metadata); + const resp = await post(``, metadata); if (response.ok) { + dispatch(createNotificationAction('Filter saved')); gotoDetail({ refresh: true }); + } else { + dispatch(createNotificationAction(resp.cause, NotificationTypes.DANGER)); } }; diff --git a/ui/src/app/metadata/view/EditFilter.js b/ui/src/app/metadata/view/EditFilter.js index bae6fa78e..5ac69fb8c 100644 --- a/ui/src/app/metadata/view/EditFilter.js +++ b/ui/src/app/metadata/view/EditFilter.js @@ -11,9 +11,12 @@ import { MetadataSchema } from '../hoc/MetadataSchema'; import { getMetadataPath, useMetadataUpdater } from '../hooks/api'; import { useMetadataFilterObject } from '../hoc/MetadataFilterSelector'; import API_BASE_PATH from '../../App.constant'; +import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications'; export function EditFilter() { + const dispatch = useNotificationDispatcher(); + const { id, filterId } = useParams(); const filter = useMetadataFilterObject(); const history = useHistory(); @@ -33,10 +36,11 @@ export function EditFilter() { function save(metadata) { setBlocking(false); - update(``, metadata).then(() => { + update(``, metadata).then((resp) => { + dispatch(createNotificationAction('Filter saved')); gotoDetail({ refresh: true }); - }).catch(() => { - window.location.reload(); + }).catch((error) => { + dispatch(createNotificationAction(error.cause, NotificationTypes.DANGER)); }); }; diff --git a/ui/src/app/metadata/view/MetadataUpload.js b/ui/src/app/metadata/view/MetadataUpload.js index d00be91fe..76ab28c5a 100644 --- a/ui/src/app/metadata/view/MetadataUpload.js +++ b/ui/src/app/metadata/view/MetadataUpload.js @@ -22,8 +22,6 @@ export function MetadataUpload() { async function save({serviceProviderName, file, url}) { - console.log(serviceProviderName, file); - setSaving(true); const f = file?.length > 0 ? file[0] : null;