diff --git a/frontend/common/stores/feature-list-store.ts b/frontend/common/stores/feature-list-store.ts index 1227914103b7..d833f865aa9a 100644 --- a/frontend/common/stores/feature-list-store.ts +++ b/frontend/common/stores/feature-list-store.ts @@ -161,6 +161,10 @@ const controller = { }) .then((res) => { // onComplete calls back preserving the order of multivariate_options with their updated ids + if (res.error) { + store.saved({ error: res.error }) + return + } if (onComplete) { onComplete(res) } diff --git a/frontend/common/stores/project-store.js b/frontend/common/stores/project-store.js index 1747bc10de6c..67f75b79269c 100644 --- a/frontend/common/stores/project-store.js +++ b/frontend/common/stores/project-store.js @@ -84,15 +84,30 @@ const controller = { editEnv: (env) => { API.trackEvent(Constants.events.EDIT_ENVIRONMENT) - data.put(`${Project.api}environments/${env.api_key}/`, env).then((res) => { - const index = _.findIndex(store.model.environments, { id: env.id }) - store.model.environments[index] = res - store.saved() - getStore().dispatch( - environmentService.util.invalidateTags(['Environment']), - ) - AppActions.refreshOrganisation() - }) + data + .put(`${Project.api}environments/${env.api_key}/`, env) + .then((res) => { + const index = _.findIndex(store.model.environments, { id: env.id }) + store.model.environments[index] = res + store.saved() + getStore().dispatch( + environmentService.util.invalidateTags(['Environment']), + ) + AppActions.refreshOrganisation() + }) + .catch((e) => { + e.json() + .then((result) => { + if (result?.metadata?.[0]) { + toast(result.metadata[0], 'danger') + } else { + toast('Error updating the environment', 'danger') + } + }) + .catch((e) => { + API.ajaxHandler(store, e) + }) + }) }, editProject: (project) => { store.saving() diff --git a/frontend/common/utils/utils.tsx b/frontend/common/utils/utils.tsx index 0a5f66716bf1..d4e6e6e54676 100644 --- a/frontend/common/utils/utils.tsx +++ b/frontend/common/utils/utils.tsx @@ -383,6 +383,10 @@ const Utils = Object.assign({}, require('./base/_utils'), { valid = isEnterprise break } + case 'METADATA': { + valid = isEnterprise + break + } default: valid = true break @@ -536,16 +540,8 @@ const Utils = Object.assign({}, require('./base/_utils'), { return /^-?\d*\.?\d+$/.test(`${value}`) }, isValidURL(value: any) { - const pattern = new RegExp( - '^(https?:\\/\\/)?' + // protocol - '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name - '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address - '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path - '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string - '(\\#[-a-z\\d_]*)?$', - 'i', - ) - return !!pattern.test(value) + const regex = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/i + return regex.test(value) }, loadScriptPromise(url: string) { return new Promise((resolve) => { diff --git a/frontend/web/components/base/forms/InputGroup.js b/frontend/web/components/base/forms/InputGroup.js index b62e029658f7..6384aac4c5e9 100644 --- a/frontend/web/components/base/forms/InputGroup.js +++ b/frontend/web/components/base/forms/InputGroup.js @@ -22,9 +22,9 @@ const InputGroup = class extends Component { const { inputProps, size } = this.props return (
{this.props.tooltip ? ( ) : ( diff --git a/frontend/web/components/metadata/AddMetadataToEntity.tsx b/frontend/web/components/metadata/AddMetadataToEntity.tsx index 675504382384..187f39702706 100644 --- a/frontend/web/components/metadata/AddMetadataToEntity.tsx +++ b/frontend/web/components/metadata/AddMetadataToEntity.tsx @@ -9,12 +9,11 @@ import { useUpdateEnvironmentMutation, } from 'common/services/useEnvironment' import { MetadataField, Metadata } from 'common/types/responses' -import Input from 'components/base/forms/Input' import Utils from 'common/utils/utils' import { useGetProjectFlagQuery } from 'common/services/useProjectFlag' -import Tooltip from 'components/Tooltip' import { sortBy } from 'lodash' import Switch from 'components/Switch' +import InputGroup from 'components/base/forms/InputGroup' export type CustomMetadataField = MetadataField & { metadataModelFieldId: number | string | null @@ -247,8 +246,8 @@ const AddMetadataToEntity: FC = ({ }} renderNoResults={ - No custom fields configured for {entity} entity. Add custom fields in - your{' '} + No custom fields configured for {entity} entity. Add custom fields + in your{' '} = ({ {`${metadata?.name} ${ metadata?.isRequiredFor ? '*' : '' }`} - {metadata?.type !== 'bool' ? ( - - { - setMetadataValue(Utils.safeParseEventValue(e)) - setMetadataValueChanged(true) - }} - className='mr-2' - style={{ width: '250px' }} - placeholder='Field Value' - isValid={Utils.validateMetadataType( - metadata?.type, - metadataValue, - )} - /> - } - place='top' - > - {`This value has to be of type ${metadata?.type}`} - - - ) : ( + {metadata?.type === 'bool' ? ( = ({ }} /> + ) : ( + + { + setMetadataValue(Utils.safeParseEventValue(e)) + setMetadataValueChanged(true) + }} + type='text' + /> + )} ) diff --git a/frontend/web/components/modals/CreateFlag.js b/frontend/web/components/modals/CreateFlag.js index 27711dd42d7b..9dd51992ddb5 100644 --- a/frontend/web/components/modals/CreateFlag.js +++ b/frontend/web/components/modals/CreateFlag.js @@ -40,7 +40,6 @@ import { getSupportedContentType } from 'common/services/useSupportedContentType import { getGithubIntegration } from 'common/services/useGithubIntegration' import { removeUserOverride } from 'components/RemoveUserOverride' import ExternalResourcesLinkTab from 'components/ExternalResourcesLinkTab' -import MetadataTitle from 'components/metadata/MetadataTitle' import { saveFeatureWithValidation } from 'components/saveFeatureWithValidation' const CreateFlag = class extends Component { @@ -182,7 +181,10 @@ const CreateFlag = class extends Component { ) { this.getFeatureUsage() } - if (Utils.getFlagsmithHasFeature('enable_metadata')) { + if ( + Utils.getPlansPermission('METADATA') && + Utils.getFlagsmithHasFeature('enable_metadata') + ) { getSupportedContentType(getStore(), { organisation_id: AccountStore.getOrganisation().id, }).then((res) => { @@ -550,7 +552,9 @@ const CreateFlag = class extends Component { const hideIdentityOverridesTab = Utils.getShouldHideIdentityOverridesTab() const noPermissions = this.props.noPermissions let regexValid = true - const metadataEnable = Utils.getFlagsmithHasFeature('enable_metadata') + const metadataEnable = + Utils.getPlansPermission('METADATA') && + Utils.getFlagsmithHasFeature('enable_metadata') try { if (!isEdit && name && regex) { regexValid = name.match(new RegExp(regex)) @@ -580,32 +584,24 @@ const CreateFlag = class extends Component { )} {metadataEnable && featureContentType?.id && ( <> - { - this.setState({ visible: v }) + + { + this.setState({ + hasMetadataRequired: b, + }) + }} + onChange={(m) => { + this.setState({ + metadata: m, + }) }} - hasRequiredMetadata={this.state.hasMetadataRequired} /> - {(this.state.hasMetadataRequired || this.state.visible) && ( - { - this.setState({ - hasMetadataRequired: b, - }) - }} - onChange={(m) => { - this.setState({ - metadata: m, - }) - }} - /> - )} )} {!identity && projectFlag && ( @@ -1971,12 +1967,23 @@ const FeatureProvider = (WrappedComponent) => { this.listenTo( FeatureListStore, 'saved', - ({ changeRequest, createdFlag, isCreate } = {}) => { - toast( - `${createdFlag || isCreate ? 'Created' : 'Updated'} ${ - changeRequest ? 'Change Request' : 'Feature' - }`, - ) + ({ changeRequest, createdFlag, error, isCreate } = {}) => { + if (error?.data?.metadata) { + error.data.metadata?.forEach((m) => { + if (Object.keys(m).length > 0) { + toast(m.non_field_errors[0], 'danger') + } + }) + } else if (error?.data) { + toast('Error updating the Flag', 'danger') + return + } else { + toast( + `${createdFlag || isCreate ? 'Created' : 'Updated'} ${ + changeRequest ? 'Change Request' : 'Feature' + }`, + ) + } const envFlags = FeatureListStore.getEnvironmentFlags() if (createdFlag) { diff --git a/frontend/web/components/modals/CreateSegment.tsx b/frontend/web/components/modals/CreateSegment.tsx index 1af2ef0a032c..fc3fbf4b34f3 100644 --- a/frontend/web/components/modals/CreateSegment.tsx +++ b/frontend/web/components/modals/CreateSegment.tsx @@ -154,7 +154,9 @@ const CreateSegment: FC = ({ const [metadata, setMetadata] = useState( segment.metadata, ) - const metadataEnable = Utils.getFlagsmithHasFeature('enable_metadata') + const metadataEnable = + Utils.getPlansPermission('METADATA') && + Utils.getFlagsmithHasFeature('enable_metadata') const error = createError || updateError const totalSegments = ProjectStore.getTotalSegments() ?? 0 @@ -763,7 +765,9 @@ const CreateSegment: FC = ({ Custom Fields} + tabLabel={ + Custom Fields + } >
{MetadataTab}
diff --git a/frontend/web/components/pages/CreateEnvironmentPage.js b/frontend/web/components/pages/CreateEnvironmentPage.js index dd6b49584c20..682031e7feb5 100644 --- a/frontend/web/components/pages/CreateEnvironmentPage.js +++ b/frontend/web/components/pages/CreateEnvironmentPage.js @@ -33,7 +33,10 @@ const CreateEnvironmentPage = class extends Component { componentDidMount = () => { API.trackPage(Constants.pages.CREATE_ENVIRONMENT) - if (Utils.getFlagsmithHasFeature('enable_metadata')) { + if ( + Utils.getPlansPermission('METADATA') && + Utils.getFlagsmithHasFeature('enable_metadata') + ) { getSupportedContentType(getStore(), { organisation_id: AccountStore.getOrganisation().id, }).then((res) => { @@ -196,7 +199,8 @@ const CreateEnvironmentPage = class extends Component { )}
- {Utils.getFlagsmithHasFeature('enable_metadata') && + {Utils.getPlansPermission('METADATA') && + Utils.getFlagsmithHasFeature('enable_metadata') && envContentType?.id && ( diff --git a/frontend/web/components/pages/EnvironmentSettingsPage.js b/frontend/web/components/pages/EnvironmentSettingsPage.js index da8d8a1d5bea..26745fca1e6c 100644 --- a/frontend/web/components/pages/EnvironmentSettingsPage.js +++ b/frontend/web/components/pages/EnvironmentSettingsPage.js @@ -82,7 +82,10 @@ const EnvironmentSettingsPage = class extends Component { }) }) - if (Utils.getFlagsmithHasFeature('enable_metadata')) { + if ( + Utils.getPlansPermission('METADATA') && + Utils.getFlagsmithHasFeature('enable_metadata') + ) { getSupportedContentType(getStore(), { organisation_id: AccountStore.getOrganisation().id, }).then((res) => { @@ -260,7 +263,9 @@ const EnvironmentSettingsPage = class extends Component { }, } = this const has4EyesPermission = Utils.getPlansPermission('4_EYES') - const metadataEnable = Utils.getFlagsmithHasFeature('enable_metadata') + const metadataEnable = + Utils.getPlansPermission('METADATA') && + Utils.getFlagsmithHasFeature('enable_metadata') return (
diff --git a/frontend/web/components/pages/ProjectSettingsPage.js b/frontend/web/components/pages/ProjectSettingsPage.js index 618c303396ff..eb667e32f8ce 100644 --- a/frontend/web/components/pages/ProjectSettingsPage.js +++ b/frontend/web/components/pages/ProjectSettingsPage.js @@ -167,7 +167,9 @@ const ProjectSettingsPage = class extends Component { const { name, stale_flags_limit_days } = this.state const hasStaleFlagsPermission = Utils.getPlansPermission('STALE_FLAGS') - const metadataEnable = Utils.getFlagsmithHasFeature('enable_metadata') + const metadataEnable = + Utils.getPlansPermission('METADATA') && + Utils.getFlagsmithHasFeature('enable_metadata') return (
diff --git a/frontend/web/components/pages/SegmentsPage.tsx b/frontend/web/components/pages/SegmentsPage.tsx index babf1112f391..8d5a9d7c1f04 100644 --- a/frontend/web/components/pages/SegmentsPage.tsx +++ b/frontend/web/components/pages/SegmentsPage.tsx @@ -24,7 +24,7 @@ import PageTitle from 'components/PageTitle' import Switch from 'components/Switch' import { setModalTitle } from 'components/modals/base/ModalDefault' import classNames from 'classnames' -import InfoMessage from 'components/InfoMessage'; +import InfoMessage from 'components/InfoMessage' const CodeHelp = require('../../components/CodeHelp') type SegmentsPageType = {