From b48b8ca0ccb2de79143af6cdca61bef77606cc3f Mon Sep 17 00:00:00 2001 From: wood Date: Fri, 24 Jan 2025 18:25:49 +0100 Subject: [PATCH 01/11] refactor: migrated-feature-iso-to-typescript --- docker-compose.yml | 4 +- frontend/common/base/utils-base.js | 1 + frontend/common/providers/ProjectProvider.tsx | 2 +- frontend/common/utils/base/_utils.js | 1 + frontend/env/project_dev.js | 4 +- .../components/EnvironmentSettingsForm.tsx | 692 ++++++++++++ frontend/web/components/IntegrationList.tsx | 4 +- frontend/web/components/hooks/useWebhooks.ts | 91 ++ .../modals/ConfirmRemoveEnvironment.tsx | 1 + .../pages/EnvironmentSettingsPage.js | 16 +- .../pages/EnvironmentSettingsPage.tsx | 986 ++++++++++++++++++ frontend/web/routes.js | 2 +- 12 files changed, 1790 insertions(+), 14 deletions(-) create mode 100644 frontend/web/components/EnvironmentSettingsForm.tsx create mode 100644 frontend/web/components/hooks/useWebhooks.ts create mode 100644 frontend/web/components/pages/EnvironmentSettingsPage.tsx diff --git a/docker-compose.yml b/docker-compose.yml index 2c25b4032850..a6ac926b0b25 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,7 +21,9 @@ services: start_period: 20s flagsmith: - image: flagsmith.docker.scarf.sh/flagsmith/flagsmith:latest + build: + context: '.' + target: 'oss-api' environment: # All environments variables are available here: # API: https://docs.flagsmith.com/deployment/locally-api#environment-variables diff --git a/frontend/common/base/utils-base.js b/frontend/common/base/utils-base.js index 35b138004ad5..2eecc74487ec 100644 --- a/frontend/common/base/utils-base.js +++ b/frontend/common/base/utils-base.js @@ -1,5 +1,6 @@ export default { safeParseEventValue(e) { + console.log("yo") // safe attempt to parse form value if (!e) { return e diff --git a/frontend/common/providers/ProjectProvider.tsx b/frontend/common/providers/ProjectProvider.tsx index 42a8da1bafc2..a08a9bd7d709 100644 --- a/frontend/common/providers/ProjectProvider.tsx +++ b/frontend/common/providers/ProjectProvider.tsx @@ -22,7 +22,7 @@ export type ProjectProviderType = { isLoading: boolean isSaving: boolean project: Project | null - }) => ReactNode + }) => ReactNode | React.FC id?: string onRemove?: () => void onRemoveEnvironment?: (environment: Environment) => void diff --git a/frontend/common/utils/base/_utils.js b/frontend/common/utils/base/_utils.js index 77a809dba898..c7850e65f78c 100644 --- a/frontend/common/utils/base/_utils.js +++ b/frontend/common/utils/base/_utils.js @@ -491,6 +491,7 @@ emailRegex: /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*| }, safeParseEventValue(e) { + console.log('prout') // safe attempt to parse form value if (!e) { return e diff --git a/frontend/env/project_dev.js b/frontend/env/project_dev.js index 6c45318f17a1..84b7fc815941 100644 --- a/frontend/env/project_dev.js +++ b/frontend/env/project_dev.js @@ -1,12 +1,12 @@ const globalThis = typeof window === 'undefined' ? global : window module.exports = global.Project = { - api: 'https://api-staging.flagsmith.com/api/v1/', + api: 'http://localhost:8000/api/v1/', chargebee: { site: 'flagsmith-test', }, - env: 'staging', + env: 'dev', // This is our Bullet Train API key - Bullet Train runs on Bullet Train! flagsmith: 'ENktaJnfLVbLifybz34JmX', diff --git a/frontend/web/components/EnvironmentSettingsForm.tsx b/frontend/web/components/EnvironmentSettingsForm.tsx new file mode 100644 index 000000000000..e2bd792c1313 --- /dev/null +++ b/frontend/web/components/EnvironmentSettingsForm.tsx @@ -0,0 +1,692 @@ +import { Dispatch, FC, SetStateAction } from 'react' +import OrganisationStore from 'common/stores/organisation-store' +import AppActions from 'common/dispatcher/app-actions' +import { Environment, Metadata, Project } from 'common/types/responses' +import ProjectStore from 'common/stores/project-store' +import _ from 'lodash' +import moment from 'moment' +import Utils from 'common/utils/utils' +import Constants from 'common/constants' +import { getStore } from 'common/store' +import { enableFeatureVersioning } from 'common/services/useEnableFeatureVersioning' +import React, { Component, useEffect, useState } from 'react' +import ConfirmRemoveEnvironment from 'components/modals/ConfirmRemoveEnvironment' +import ConfigProvider from 'common/providers/ConfigProvider' +import withWebhooks from 'common/providers/withWebhooks' +import CreateWebhookModal from 'components/modals/CreateWebhook' +import ConfirmRemoveWebhook from 'components/modals/ConfirmRemoveWebhook' +import ConfirmToggleEnvFeature from 'components/modals/ConfirmToggleEnvFeature' +import EditPermissions from 'components/EditPermissions' +import Tabs from 'components/base/forms/Tabs' +import TabItem from 'components/base/forms/TabItem' +import JSONReference from 'components/JSONReference' +import ColourSelect from 'components/tags/ColourSelect' +import Switch from 'components/Switch' +import Icon from 'components/Icon' +import PageTitle from 'components/PageTitle' +import { getRoles } from 'common/services/useRole' +import { getRoleEnvironmentPermissions } from 'common/services/useRolePermission' +import AccountStore from 'common/stores/account-store' +import { Link, RouterChildContext } from 'react-router-dom' +import AddMetadataToEntity from 'components/metadata/AddMetadataToEntity' +import { getSupportedContentType } from 'common/services/useSupportedContentType' +import EnvironmentVersioningListener from 'components/EnvironmentVersioningListener' +import Format from 'common/utils/format' +import Setting from 'components/Setting' +import { useWebhooks } from 'components/hooks/useWebhooks' +import API from 'project/api' +{/* */} +interface EnvironmentSettingsFormProps { + deleteEnv: (env: Environment) => void + isLoading: boolean + isSaving: boolean + project: Project + match: { + params: { + environmentId: string + projectId: string + } + } + currentEnv: Environment + setCurrentEnv: Dispatch> +} + +const EnvironmentSettingsForm: FC = ({ match, deleteEnv, isLoading, isSaving, project, currentEnv, setCurrentEnv }) => { + const env = _.find(project.environments, { + api_key: match.params.environmentId, + }) + if ( + (env && + typeof env?.minimum_change_request_approvals === + 'undefined') || + env?.api_key !== match.params.environmentId + ) { + setTimeout(() => { + setCurrentEnv({ + allow_client_traits: !!env?.allow_client_traits, + banner_colour: env?.banner_colour || Constants.tagColors[0], + banner_text: env?.banner_text, + hide_disabled_flags: env?.hide_disabled_flags, + hide_sensitive_data: !!env?.hide_sensitive_data, + minimum_change_request_approvals: Utils.changeRequestsEnabled( + env?.minimum_change_request_approvals, + ) + ? env?.minimum_change_request_approvals + : null, + name: env?.name, + use_identity_composite_key_for_hashing: + !!env?.use_identity_composite_key_for_hashing, + use_identity_overrides_in_local_eval: + !!env?.use_identity_overrides_in_local_eval, + use_v2_feature_versioning: !!env?.use_v2_feature_versioning, + }) + }, 10) + } + + const onEnableVersioning = () => { + openConfirm({ + body: 'This will allow you to attach versions to updating feature values and segment overrides. Note: this may take several minutes to process', + title: 'Enable "Feature Versioning"', + onYes: async () => { + const res = await enableFeatureVersioning(getStore(), { + environmentId: env?.api_key, + }) + toast( + 'Feature Versioning Enabled, this may take several minutes to process.', + ) + setCurrentEnv((currentEnvState) => ({ + ...currentEnvState, + enabledFeatureVersioning: true, + })) + }, + }) + } + return ( + <> + + + {isLoading && ( +
+ +
+ )} + {!isLoading && ( + + +
+
General Settings
+ +
+
+ (this.input = e)} + value={ + typeof this.state.name === 'string' + ? this.state.name + : env.name + } + inputProps={{ + className: 'full-width', + name: 'env-name', + }} + className='full-width' + onChange={(e) => + this.setState({ + name: Utils.safeParseEventValue(e), + }) + } + isValid={name && name.length} + type='text' + title='Environment Name' + placeholder='Environment Name' + /> + (this.input = e)} + value={this.state?.env?.description ?? ''} + inputProps={{ + className: 'input--wide textarea-lg', + }} + onChange={(e) => + this.setState({ + env: { + ...this.state.env, + description: Utils.safeParseEventValue(e), + }, + }) + } + isValid={name && name.length} + type='text' + title='Environment Description' + placeholder='Environment Description' + /> +
+ +
+ +
+
+
+ + this.setState( + { + banner_text: value + ? `${env.name} Environment` + : null, + }, + this.saveEnv, + ) + } + checked={typeof this.state.banner_text === 'string'} + title={'Environment Banner'} + description={ +
+ This will show a banner whenever you view its + pages. +
+ This is generally used to warn people that they + are viewing and editing a sensitive environment. +
+ } + /> + {typeof this.state.banner_text === 'string' && ( + + + this.setState({ + banner_text: Utils.safeParseEventValue(e), + }) + } + className='full-width' + /> +
+ + this.setState({ banner_colour }) + } + /> +
+ +
+ )} +
+ {Utils.getFlagsmithHasFeature('feature_versioning') && ( +
+
+ {use_v2_feature_versioning === false && ( + { + this.setState({ + use_v2_feature_versioning: true, + }) + }} + /> + )} + + + Allows you to attach versions to updating + feature values and segment overrides. +
+ This setting may take up to a minute to take + affect. +
+
+ Enabling this is irreversible. +
+
+ } + disabled={ + use_v2_feature_versioning || + this.state.enabledFeatureVersioning + } + data-test={ + use_v2_feature_versioning + ? 'feature-versioning-enabled' + : 'enable-versioning' + } + checked={use_v2_feature_versioning} + onChange={onEnableVersioning} + /> +
+
+ )} +
+ { + this.confirmToggle( + 'Confirm Environment Setting', + 'hide_sensitive_data', + hide_sensitive_data, + ) + }} + description={ +
+ Exclude sensitive data from endpoints returning + flags and identity information to the SDKs or + via our REST API. +
+ For full information on the excluded fields see + documentation{' '} + +
+ Enabling this feature will change the response + from the API and could break your existing + code. +
+
+ } + /> +
+ + + this.setState( + { + minimum_change_request_approvals: v + ? 0 + : null, + }, + this.saveEnv, + ) + } + /> + {Utils.changeRequestsEnabled( + this.state.minimum_change_request_approvals, + ) && + has4EyesPermission && ( +
+
+ Minimum number of approvals +
+ + + (this.input = e)} + value={`${this.state.minimum_change_request_approvals}`} + inputClassName='input input--wide' + name='env-name' + min={0} + style={{ minWidth: 50 }} + onChange={(e) => { + if (!Utils.safeParseEventValue(e)) + return + this.setState({ + minimum_change_request_approvals: + parseInt( + Utils.safeParseEventValue(e), + ), + }) + }} + isValid={name && name.length} + type='number' + placeholder='Minimum number of approvals' + /> + + + +
+ )} +
+
+ + +
+
Delete Environment
+

+ This environment will be permanently deleted. +

+
+ +
+
+ +
+ +
+ +
+
+
+
+ Hide disabled flags from SDKs +
+ { + setCurrentEnv((currentEnvState) => ({ + ...currentEnvState, + banner_text: Utils.safeParseEventValue(e), + })) + }} + className='full-width' + /> +
+ + setCurrentEnv((currentEnvState) => ({ + ...currentEnvState, + banner_colour, + })) + } + /> +
+ + + )} +
+ {Utils.getFlagsmithHasFeature('feature_versioning') && ( +
+
+ {currentEnv?.use_v2_feature_versioning === false && ( + { + setCurrentEnv((currentEnvState) => ({ + ...currentEnvState, + use_v2_feature_versioning: true, + })) + }} + /> + )} + + + Allows you to attach versions to updating + feature values and segment overrides. +
+ This setting may take up to a minute to take + affect. +
+
+ Enabling this is irreversible. +
+
+ } + disabled={ + currentEnv?.use_v2_feature_versioning || + currentEnv?.enabledFeatureVersioning + } + data-test={ + currentEnv?.use_v2_feature_versioning + ? 'feature-versioning-enabled' + : 'enable-versioning' + } + checked={currentEnv?.use_v2_feature_versioning} + onChange={onEnableVersioning} + /> +
+
+ )} +
+ { + console.log('v', v) + confirmToggle( + 'Confirm Environment Setting', + 'hide_sensitive_data', + v, + ) + }} + description={ +
+ Exclude sensitive data from endpoints returning + flags and identity information to the SDKs or + via our REST API. +
+ For full information on the excluded fields see + documentation{' '} + +
+ Enabling this feature will change the response + from the API and could break your existing + code. +
+
+ } + /> +
+ + + setCurrentEnv((currentEnvState) => { + const newEnv = { + ...currentEnvState, + minimum_change_request_approvals: v + ? 0 + : null, + } + saveEnv(newEnv) + return newEnv + }) + } + /> + {Utils.changeRequestsEnabled( + currentEnv?.minimum_change_request_approvals, + ) && + has4EyesPermission && ( +
+
+ Minimum number of approvals +
+ + + { + if (!Utils.safeParseEventValue(e)) + return + setCurrentEnv((currentEnvState) => ({ + ...currentEnvState, + minimum_change_request_approvals: + parseInt( + Utils.safeParseEventValue(e), + ), + })) + }} + isValid={currentEnv?.minimum_change_request_approvals && currentEnv?.minimum_change_request_approvals.length} + type='number' + placeholder='Minimum number of approvals' + /> + + + +
+ )} +
+
+ + +
+
Delete Environment
+

+ This environment will be permanently deleted. +

+
+ +
+
+
+
+ +
+ +
+ +
+
+ Hide disabled flags from SDKs +
+ - this.setState({ - banner_text: Utils.safeParseEventValue(e), - }) - } - className='full-width' - /> -
- - this.setState({ banner_colour }) - } - /> -
- - - )} -
- {Utils.getFlagsmithHasFeature('feature_versioning') && ( -
-
- {use_v2_feature_versioning === false && ( - { - this.setState({ - use_v2_feature_versioning: true, - }) - }} - /> - )} - - - Allows you to attach versions to updating - feature values and segment overrides. -
- This setting may take up to a minute to take - affect. -
-
- Enabling this is irreversible. -
-
- } - disabled={ - use_v2_feature_versioning || - this.state.enabledFeatureVersioning - } - data-test={ - use_v2_feature_versioning - ? 'feature-versioning-enabled' - : 'enable-versioning' - } - checked={use_v2_feature_versioning} - onChange={onEnableVersioning} - /> -
-
- )} -
- { - this.confirmToggle( - 'Confirm Environment Setting', - 'hide_sensitive_data', - hide_sensitive_data, - ) - }} - description={ -
- Exclude sensitive data from endpoints returning - flags and identity information to the SDKs or - via our REST API. -
- For full information on the excluded fields see - documentation{' '} - -
- Enabling this feature will change the response - from the API and could break your existing - code. -
-
- } - /> -
- - - this.setState( - { - minimum_change_request_approvals: v - ? 0 - : null, - }, - this.saveEnv, - ) - } - /> - {Utils.changeRequestsEnabled( - this.state.minimum_change_request_approvals, - ) && - has4EyesPermission && ( -
-
- Minimum number of approvals -
- - - (this.input = e)} - value={`${this.state.minimum_change_request_approvals}`} - inputClassName='input input--wide' - name='env-name' - min={0} - style={{ minWidth: 50 }} - onChange={(e) => { - if (!Utils.safeParseEventValue(e)) - return - this.setState({ - minimum_change_request_approvals: - parseInt( - Utils.safeParseEventValue(e), - ), - }) - }} - isValid={name && name.length} - type='number' - placeholder='Minimum number of approvals' - /> - - - -
- )} -
-
- - -
-
Delete Environment
-

- This environment will be permanently deleted. -

-
- -
-
-
-
- -
- -
- -
-
- Hide disabled flags from SDKs -
- { - setCurrentEnv((currentEnvState) => ({ - ...currentEnvState, - banner_text: Utils.safeParseEventValue(e), - })) + onChange={(e: React.ChangeEvent) => { + const bannerText = Utils.safeParseEventValue(e) + updateCurrentEnv({ banner_text: bannerText }, false) }} className='full-width' />
- setCurrentEnv((currentEnvState) => ({ - ...currentEnvState, - banner_colour, - })) + updateCurrentEnv({ banner_colour }, false) } />
- @@ -493,17 +452,14 @@ const inputRef = useRef(null)
{currentEnv?.use_v2_feature_versioning === false && ( { - setCurrentEnv((currentEnvState) => ({ - ...currentEnvState, - use_v2_feature_versioning: true, - })) + updateCurrentEnv({ use_v2_feature_versioning: true }, false, true) }} /> )} - (null) { - console.log('v', v) + onChange={(value) => { confirmToggle( 'Confirm Environment Setting', 'hide_sensitive_data', - v, + value, ) }} description={ @@ -580,17 +535,8 @@ const inputRef = useRef(null) currentEnv?.minimum_change_request_approvals, ) } - onChange={(v) => - setCurrentEnv((currentEnvState) => { - const newEnv = { - ...currentEnvState, - minimum_change_request_approvals: v - ? 0 - : null, - } - saveEnv(newEnv) - return newEnv - }) + onChange={(value) => + updateCurrentEnv({ minimum_change_request_approvals: value ? 0 : undefined }, true) } /> {Utils.changeRequestsEnabled( @@ -610,15 +556,8 @@ const inputRef = useRef(null) min={0} style={{ minWidth: 50 }} onChange={(e) => { - if (!Utils.safeParseEventValue(e)) - return - setCurrentEnv((currentEnvState) => ({ - ...currentEnvState, - minimum_change_request_approvals: - parseInt( - Utils.safeParseEventValue(e), - ), - })) + const value = Utils.safeParseEventValue(e) + updateCurrentEnv({ minimum_change_request_approvals: value ? parseInt(value) : undefined }, false) }} isValid={currentEnv?.minimum_change_request_approvals && currentEnv?.minimum_change_request_approvals.length} type='number' @@ -653,22 +592,20 @@ const inputRef = useRef(null)
- - )} -
- {Utils.getFlagsmithHasFeature('feature_versioning') && ( -
-
- {use_v2_feature_versioning === false && ( - { - this.setState({ - use_v2_feature_versioning: true, - }) - }} - /> - )} - - - Allows you to attach versions to updating - feature values and segment overrides. -
- This setting may take up to a minute to take - affect. -
-
- Enabling this is irreversible. -
-
- } - disabled={ - use_v2_feature_versioning || - this.state.enabledFeatureVersioning - } - data-test={ - use_v2_feature_versioning - ? 'feature-versioning-enabled' - : 'enable-versioning' - } - checked={use_v2_feature_versioning} - onChange={onEnableVersioning} - /> -
-
- )} -
- { - this.confirmToggle( - 'Confirm Environment Setting', - 'hide_sensitive_data', - hide_sensitive_data, - ) - }} - description={ -
- Exclude sensitive data from endpoints returning - flags and identity information to the SDKs or - via our REST API. -
- For full information on the excluded fields see - documentation{' '} - -
- Enabling this feature will change the response - from the API and could break your existing - code. -
-
- } - /> -
- - - this.setState( - { - minimum_change_request_approvals: v - ? 0 - : null, - }, - this.saveEnv, - ) - } - /> - {Utils.changeRequestsEnabled( - this.state.minimum_change_request_approvals, - ) && - has4EyesPermission && ( -
-
- Minimum number of approvals -
- - - (this.input = e)} - value={`${this.state.minimum_change_request_approvals}`} - inputClassName='input input--wide' - name='env-name' - min={0} - style={{ minWidth: 50 }} - onChange={(e) => { - if (!Utils.safeParseEventValue(e)) - return - this.setState({ - minimum_change_request_approvals: - parseInt( - Utils.safeParseEventValue(e), - ), - }) - }} - isValid={name && name.length} - type='number' - placeholder='Minimum number of approvals' - /> - - - -
- )} -
-
- - -
-
Delete Environment
-

- This environment will be permanently deleted. -

-
- -
-
-
-
- -
- -
- -
-
- Hide disabled flags from SDKs -
-