From 35262e404b0514b48076461c994e8de9ac00e578 Mon Sep 17 00:00:00 2001 From: olav Date: Wed, 4 May 2022 15:16:34 +0200 Subject: [PATCH] refactor: clean up strategy parameter types (#944) * refactor: fix splash page button background color * refactor: regenerate OpenAPI client * refactor: clean up strategy parameter types * refactor: remove index signature from IConstraint * refactor: fix never-seen status in features list --- .../integration/feature/feature.spec.ts | 4 +- .../FreeTextInput/FreeTextInput.tsx | 15 +- .../FeatureStrategyEdit.tsx | 4 +- .../FeatureStrategyType.tsx | 8 +- .../FeatureToggleListItem.tsx | 11 +- .../FeatureOverviewExecution.tsx | 64 ++++--- .../FeatureStatus/FeatureStatus.tsx | 6 +- .../FlexibleStrategy/FlexibleStrategy.tsx | 22 ++- .../GeneralStrategy/GeneralStrategy.tsx | 42 ++--- .../UserWithIdStrategy/UserWithId.tsx | 14 +- .../SplashPageOperators.styles.tsx | 2 +- .../StrategiesList/StrategiesList.tsx | 16 +- .../strategies/StrategyForm/StrategyForm.tsx | 6 +- .../StrategyParameter/StrategyParameter.tsx | 8 +- .../StrategyParameters/StrategyParameters.tsx | 6 +- .../StrategyDetails/StrategyDetails.tsx | 4 +- .../strategies/hooks/useStrategyForm.ts | 4 +- .../actions/useFeatureApi/useFeatureApi.ts | 2 +- .../useFeatureStrategyApi.ts | 6 +- .../useStrategiesApi/useStrategiesApi.ts | 12 +- frontend/src/hooks/useFeaturesFilter.test.ts | 8 +- frontend/src/interfaces/strategy.ts | 61 +++---- frontend/src/openapi/apis/AdminApi.ts | 170 ++++++++++++++++++ .../src/openapi/models/ChangeProjectSchema.ts | 56 ++++++ .../src/openapi/models/ConstraintSchema.ts | 24 +++ .../openapi/models/CreateStrategySchema.ts | 87 +++++++++ .../models/CreateStrategySchemaConstraints.ts | 96 ++++++++++ frontend/src/openapi/models/FeatureSchema.ts | 36 ++-- .../openapi/models/FeatureSchemaOverrides.ts | 64 +++++++ .../openapi/models/FeatureSchemaStrategies.ts | 87 +++++++++ .../openapi/models/FeatureSchemaVariants.ts | 103 +++++++++++ frontend/src/openapi/models/FeaturesSchema.ts | 18 +- .../openapi/models/FeaturesSchemaFeatures.ts | 149 +++++++++++++++ frontend/src/openapi/models/StrategySchema.ts | 22 +-- frontend/src/openapi/models/VariantSchema.ts | 18 +- frontend/src/openapi/models/index.ts | 7 + frontend/src/utils/cleanConstraint.test.ts | 31 ++++ frontend/src/utils/cleanConstraint.ts | 29 +-- frontend/src/utils/getStrategyObject.ts | 11 +- frontend/src/utils/parseParameter.test.ts | 36 ++++ frontend/src/utils/parseParameter.ts | 23 +++ .../src/utils/resolveDefaultParamValue.ts | 4 +- 42 files changed, 1155 insertions(+), 241 deletions(-) create mode 100644 frontend/src/openapi/models/ChangeProjectSchema.ts create mode 100644 frontend/src/openapi/models/CreateStrategySchema.ts create mode 100644 frontend/src/openapi/models/CreateStrategySchemaConstraints.ts create mode 100644 frontend/src/openapi/models/FeatureSchemaOverrides.ts create mode 100644 frontend/src/openapi/models/FeatureSchemaStrategies.ts create mode 100644 frontend/src/openapi/models/FeatureSchemaVariants.ts create mode 100644 frontend/src/openapi/models/FeaturesSchemaFeatures.ts create mode 100644 frontend/src/utils/cleanConstraint.test.ts create mode 100644 frontend/src/utils/parseParameter.test.ts create mode 100644 frontend/src/utils/parseParameter.ts diff --git a/frontend/cypress/integration/feature/feature.spec.ts b/frontend/cypress/integration/feature/feature.spec.ts index f58e403cab..b413f42d9a 100644 --- a/frontend/cypress/integration/feature/feature.spec.ts +++ b/frontend/cypress/integration/feature/feature.spec.ts @@ -118,7 +118,7 @@ describe('feature', () => { expect(req.body.name).to.equal('flexibleRollout'); expect(req.body.parameters.groupId).to.equal(featureToggleName); expect(req.body.parameters.stickiness).to.equal('default'); - expect(req.body.parameters.rollout).to.equal(100); + expect(req.body.parameters.rollout).to.equal('100'); if (ENTERPRISE) { expect(req.body.constraints.length).to.equal(1); @@ -159,7 +159,7 @@ describe('feature', () => { req => { expect(req.body.parameters.groupId).to.equal('new-group-id'); expect(req.body.parameters.stickiness).to.equal('sessionId'); - expect(req.body.parameters.rollout).to.equal(100); + expect(req.body.parameters.rollout).to.equal('100'); if (ENTERPRISE) { expect(req.body.constraints.length).to.equal(1); diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/FreeTextInput/FreeTextInput.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/FreeTextInput/FreeTextInput.tsx index 4b1098dd94..77ee0825ee 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/FreeTextInput/FreeTextInput.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/FreeTextInput/FreeTextInput.tsx @@ -4,6 +4,7 @@ import Input from 'component/common/Input/Input'; import StringTruncator from 'component/common/StringTruncator/StringTruncator'; import React, { useState } from 'react'; import { ConstraintFormHeader } from '../ConstraintFormHeader/ConstraintFormHeader'; +import { parseParameterStrings } from 'utils/parseParameter'; interface IFreeTextInputProps { values: string[]; @@ -74,17 +75,9 @@ export const FreeTextInput = ({ return; } setError(''); - - if (inputValues.includes(',')) { - const newValues = inputValues - .split(',') - .filter(values => values) - .map(value => value.trim()); - setValues(uniqueValues([...values, ...newValues])); - } else { - setValues(uniqueValues([...values, inputValues.trim()])); - } - + setValues( + uniqueValues([...values, ...parseParameterStrings(inputValues)]) + ); setInputValues(''); }; diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx index a729171551..6651c93cbd 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx @@ -8,7 +8,7 @@ import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFe import { formatUnknownError } from 'utils/formatUnknownError'; import { useHistory } from 'react-router-dom'; import useToast from 'hooks/useToast'; -import { IFeatureStrategy, IStrategyPayload } from 'interfaces/strategy'; +import { IFeatureStrategy, IFeatureStrategyPayload } from 'interfaces/strategy'; import { UPDATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions'; import { ISegment } from 'interfaces/segment'; import { useSegmentsApi } from 'hooks/api/actions/useSegmentsApi/useSegmentsApi'; @@ -122,7 +122,7 @@ export const FeatureStrategyEdit = () => { export const createStrategyPayload = ( strategy: Partial -): IStrategyPayload => { +): IFeatureStrategyPayload => { return { name: strategy.name, constraints: strategy.constraints ?? [], diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyType/FeatureStrategyType.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyType/FeatureStrategyType.tsx index 321cc8b8e9..6ab2beaf9c 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyType/FeatureStrategyType.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyType/FeatureStrategyType.tsx @@ -28,7 +28,7 @@ export const FeatureStrategyType = ({ return definition.name === strategy.name; }); - const updateParameter = (field: string, value: unknown) => { + const updateParameter = (field: string, value: string) => { setStrategy( produce(draft => { draft.parameters = draft.parameters ?? {}; @@ -48,7 +48,7 @@ export const FeatureStrategyType = ({ return ( @@ -56,7 +56,7 @@ export const FeatureStrategyType = ({ case 'userWithId': return ( @@ -65,7 +65,7 @@ export const FeatureStrategyType = ({ return ( diff --git a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListItem/FeatureToggleListItem.tsx b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListItem/FeatureToggleListItem.tsx index 79e0e9ec5f..e543042df2 100644 --- a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListItem/FeatureToggleListItem.tsx +++ b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListItem/FeatureToggleListItem.tsx @@ -72,14 +72,9 @@ export const FeatureToggleListItem = memo( className={classnames(styles.listItem, className)} > - ( - - )} + - + ); case 'userIds': case 'UserIds': - const users = parameters[key] - .split(',') - .filter((userId: string) => userId); - + const users = parseParameterStrings(parameters[key]); return ( hosts); - + const hosts = parseParameterStrings(parameters[key]); return ( ); case 'IPs': - const IPs = parameters[key] - .split(',') - .filter((hosts: string) => hosts); - + const IPs = parseParameterStrings(parameters[key]); return ( val); - + const values = parseParameterStrings( + strategy?.parameters[param.name] + ); return ( - ); case 'string': - const value = strategy.parameters[param.name]; + const value = parseParameterString( + strategy.parameters[param.name] + ); return ( @@ -198,7 +210,9 @@ const FeatureOverviewExecution = ({ /> ); case 'number': - const number = strategy.parameters[param.name]; + const number = parseParameterNumber( + strategy.parameters[param.name] + ); return ( ( - } + )} elseShow={ void; + parameters: IFeatureStrategyParameters; + updateParameter: (field: string, value: string) => void; context: any; editable: boolean; } @@ -54,15 +58,15 @@ const FlexibleStrategy = ({ const stickinessOptions = resolveStickiness(); const rollout = - parameters.rollout !== undefined ? parameters.rollout : '100'; - const stickiness = parameters.stickiness; - const groupId = parameters.groupId; + parameters.rollout !== undefined + ? parseParameterNumber(parameters.rollout) + : 100; return (
@@ -86,7 +90,7 @@ const FlexibleStrategy = ({ name="stickiness" label="Stickiness" options={stickinessOptions} - value={stickiness} + value={parseParameterString(parameters.stickiness)} disabled={!editable} data-testid={FLEXIBLE_STRATEGY_STICKINESS_ID} onChange={e => onUpdate('stickiness')(e.target.value)} @@ -109,7 +113,7 @@ const FlexibleStrategy = ({ onUpdate('groupId')(e.target.value)} data-testid={FLEXIBLE_STRATEGY_GROUP_ID} diff --git a/frontend/src/component/feature/StrategyTypes/GeneralStrategy/GeneralStrategy.tsx b/frontend/src/component/feature/StrategyTypes/GeneralStrategy/GeneralStrategy.tsx index 8972020fe0..8e767a59f0 100644 --- a/frontend/src/component/feature/StrategyTypes/GeneralStrategy/GeneralStrategy.tsx +++ b/frontend/src/component/feature/StrategyTypes/GeneralStrategy/GeneralStrategy.tsx @@ -2,13 +2,18 @@ import React from 'react'; import { FormControlLabel, Switch, TextField, Tooltip } from '@mui/material'; import StrategyInputList from '../StrategyInputList/StrategyInputList'; import RolloutSlider from '../RolloutSlider/RolloutSlider'; -import { IParameter, IStrategy } from 'interfaces/strategy'; +import { IStrategy, IFeatureStrategyParameters } from 'interfaces/strategy'; import { useStyles } from './GeneralStrategy.styles'; +import { + parseParameterNumber, + parseParameterStrings, + parseParameterString, +} from 'utils/parseParameter'; interface IGeneralStrategyProps { - parameters: IParameter; + parameters: IFeatureStrategyParameters; strategyDefinition: IStrategy; - updateParameter: (field: string, value: any) => void; + updateParameter: (field: string, value: string) => void; editable: boolean; } @@ -29,9 +34,13 @@ const GeneralStrategy = ({ updateParameter(field, value); }; - const onChangePercentage = (field: string, evt: Event, newValue: any) => { + const onChangePercentage = ( + field: string, + evt: Event, + newValue: number | number[] + ) => { evt.preventDefault(); - updateParameter(field, newValue); + updateParameter(field, newValue.toString()); }; const handleSwitchChange = (field: string, currentValue: any) => { @@ -47,15 +56,8 @@ const GeneralStrategy = ({ <> {strategyDefinition.parameters.map( ({ name, type, description, required }) => { - let value = parameters[name]; - if (type === 'percentage') { - if ( - value == null || - (typeof value === 'string' && value === '') - ) { - value = 0; - } + const value = parseParameterNumber(parameters[name]); return (

@@ -66,7 +68,7 @@ const GeneralStrategy = ({ name )} disabled={!editable} - value={1 * value} + value={value} minLabel="off" maxLabel="on" /> @@ -78,15 +80,12 @@ const GeneralStrategy = ({
); } else if (type === 'list') { - let list: string[] = []; - if (typeof value === 'string') { - list = value.trim().split(',').filter(Boolean); - } + const values = parseParameterStrings(parameters[name]); return (
@@ -99,8 +98,9 @@ const GeneralStrategy = ({ ); } else if (type === 'number') { const regex = new RegExp('^\\d+$'); + const value = parseParameterString(parameters[name]); const error = - value?.length > 0 ? !regex.test(value) : false; + value.length > 0 ? !regex.test(value) : false; return (
@@ -130,6 +130,7 @@ const GeneralStrategy = ({
); } else if (type === 'boolean') { + const value = parseParameterString(parameters[name]); return (
); } else { + const value = parseParameterString(parameters[name]); return (
void; editable: boolean; } @@ -12,18 +13,11 @@ const UserWithIdStrategy = ({ parameters, updateParameter, }: IUserWithIdStrategyProps) => { - const value = parameters.userIds; - - let list: string[] = []; - if (typeof value === 'string') { - list = value.trim().split(',').filter(Boolean); - } - return (
diff --git a/frontend/src/component/splash/SplashPageOperators/SplashPageOperators.styles.tsx b/frontend/src/component/splash/SplashPageOperators/SplashPageOperators.styles.tsx index a73dc9fe75..fcc3ab7944 100644 --- a/frontend/src/component/splash/SplashPageOperators/SplashPageOperators.styles.tsx +++ b/frontend/src/component/splash/SplashPageOperators/SplashPageOperators.styles.tsx @@ -72,7 +72,7 @@ export const useStyles = makeStyles()(theme => ({ color: 'inherit', }, button: { - background: 'white', + background: 'white !important', color: theme.palette.primary.main, }, })); diff --git a/frontend/src/component/strategies/StrategiesList/StrategiesList.tsx b/frontend/src/component/strategies/StrategiesList/StrategiesList.tsx index 650da08e5c..7bfe0afcfc 100644 --- a/frontend/src/component/strategies/StrategiesList/StrategiesList.tsx +++ b/frontend/src/component/strategies/StrategiesList/StrategiesList.tsx @@ -36,7 +36,7 @@ import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies'; import useStrategiesApi from 'hooks/api/actions/useStrategiesApi/useStrategiesApi'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; -import { ICustomStrategy } from 'interfaces/strategy'; +import { IStrategy } from 'interfaces/strategy'; interface IDialogueMetaData { show: boolean; @@ -102,7 +102,7 @@ export const StrategiesList = () => { ); - const onReactivateStrategy = (strategy: ICustomStrategy) => { + const onReactivateStrategy = (strategy: IStrategy) => { setDialogueMetaData({ show: true, title: 'Really reactivate strategy?', @@ -122,7 +122,7 @@ export const StrategiesList = () => { }); }; - const onDeprecateStrategy = (strategy: ICustomStrategy) => { + const onDeprecateStrategy = (strategy: IStrategy) => { setDialogueMetaData({ show: true, title: 'Really deprecate strategy?', @@ -142,7 +142,7 @@ export const StrategiesList = () => { }); }; - const onDeleteStrategy = (strategy: ICustomStrategy) => { + const onDeleteStrategy = (strategy: IStrategy) => { setDialogueMetaData({ show: true, title: 'Really delete strategy?', @@ -162,7 +162,7 @@ export const StrategiesList = () => { }); }; - const reactivateButton = (strategy: ICustomStrategy) => ( + const reactivateButton = (strategy: IStrategy) => ( onReactivateStrategy(strategy)} permission={UPDATE_STRATEGY} @@ -172,7 +172,7 @@ export const StrategiesList = () => { ); - const deprecateButton = (strategy: ICustomStrategy) => ( + const deprecateButton = (strategy: IStrategy) => ( { /> ); - const editButton = (strategy: ICustomStrategy) => ( + const editButton = (strategy: IStrategy) => ( { /> ); - const deleteButton = (strategy: ICustomStrategy) => ( + const deleteButton = (strategy: IStrategy) => ( >; setStrategyDesc: React.Dispatch>; - setParams: React.Dispatch>; + setParams: React.Dispatch>; handleSubmit: (e: React.FormEvent) => void; handleCancel: () => void; errors: { [key: string]: string }; diff --git a/frontend/src/component/strategies/StrategyForm/StrategyParameters/StrategyParameter/StrategyParameter.tsx b/frontend/src/component/strategies/StrategyForm/StrategyParameters/StrategyParameter/StrategyParameter.tsx index 9e5948b7fa..808de26ba6 100644 --- a/frontend/src/component/strategies/StrategyForm/StrategyParameters/StrategyParameter/StrategyParameter.tsx +++ b/frontend/src/component/strategies/StrategyForm/StrategyParameters/StrategyParameter/StrategyParameter.tsx @@ -5,7 +5,7 @@ import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect'; import Input from 'component/common/Input/Input'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import React from 'react'; -import { ICustomStrategyParameter } from 'interfaces/strategy'; +import { IStrategyParameter } from 'interfaces/strategy'; const paramTypesOptions = [ { @@ -41,10 +41,10 @@ const paramTypesOptions = [ interface IStrategyParameterProps { set: React.Dispatch>; - input: ICustomStrategyParameter; + input: IStrategyParameter; index: number; - params: ICustomStrategyParameter[]; - setParams: React.Dispatch>; + params: IStrategyParameter[]; + setParams: React.Dispatch>; errors: { [key: string]: string }; } diff --git a/frontend/src/component/strategies/StrategyForm/StrategyParameters/StrategyParameters.tsx b/frontend/src/component/strategies/StrategyForm/StrategyParameters/StrategyParameters.tsx index 319644ebb1..9e07bb53f9 100644 --- a/frontend/src/component/strategies/StrategyForm/StrategyParameters/StrategyParameters.tsx +++ b/frontend/src/component/strategies/StrategyForm/StrategyParameters/StrategyParameters.tsx @@ -1,11 +1,11 @@ import { StrategyParameter } from './StrategyParameter/StrategyParameter'; import React from 'react'; -import { ICustomStrategyParameter } from 'interfaces/strategy'; +import { IStrategyParameter } from 'interfaces/strategy'; interface IStrategyParametersProps { - input: ICustomStrategyParameter[]; + input: IStrategyParameter[]; updateParameter: (index: number, updated: object) => void; - setParams: React.Dispatch>; + setParams: React.Dispatch>; errors: { [key: string]: string }; } diff --git a/frontend/src/component/strategies/StrategyView/StrategyDetails/StrategyDetails.tsx b/frontend/src/component/strategies/StrategyView/StrategyDetails/StrategyDetails.tsx index adaf77c05a..f1ffe72421 100644 --- a/frontend/src/component/strategies/StrategyView/StrategyDetails/StrategyDetails.tsx +++ b/frontend/src/component/strategies/StrategyView/StrategyDetails/StrategyDetails.tsx @@ -11,7 +11,7 @@ import { AppsLinkList } from 'component/common'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import styles from '../../strategies.module.scss'; import { TogglesLinkList } from 'component/strategies/TogglesLinkList/TogglesLinkList'; -import { IParameter, IStrategy } from 'interfaces/strategy'; +import { IStrategy, IStrategyParameter } from 'interfaces/strategy'; import { IApplication } from 'interfaces/application'; import { FeatureSchema } from 'openapi'; @@ -27,7 +27,7 @@ export const StrategyDetails = ({ toggles, }: IStrategyDetailsProps) => { const { parameters = [] } = strategy; - const renderParameters = (params: IParameter[]) => { + const renderParameters = (params: IStrategyParameter[]) => { if (params.length > 0) { return params.map(({ name, type, description, required }, i) => ( diff --git a/frontend/src/component/strategies/hooks/useStrategyForm.ts b/frontend/src/component/strategies/hooks/useStrategyForm.ts index 28b7ee97e2..3911381c80 100644 --- a/frontend/src/component/strategies/hooks/useStrategyForm.ts +++ b/frontend/src/component/strategies/hooks/useStrategyForm.ts @@ -1,11 +1,11 @@ -import { ICustomStrategyParameter } from 'interfaces/strategy'; +import { IStrategyParameter } from 'interfaces/strategy'; import { useEffect, useState } from 'react'; import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies'; export const useStrategyForm = ( initialStrategyName: string = '', initialStrategyDesc: string = '', - initialParams: ICustomStrategyParameter[] = [] + initialParams: IStrategyParameter[] = [] ) => { const [strategyName, setStrategyName] = useState(initialStrategyName); const [strategyDesc, setStrategyDesc] = useState(initialStrategyDesc); diff --git a/frontend/src/hooks/api/actions/useFeatureApi/useFeatureApi.ts b/frontend/src/hooks/api/actions/useFeatureApi/useFeatureApi.ts index f3f3c0f639..13a11522da 100644 --- a/frontend/src/hooks/api/actions/useFeatureApi/useFeatureApi.ts +++ b/frontend/src/hooks/api/actions/useFeatureApi/useFeatureApi.ts @@ -1,9 +1,9 @@ import { ITag } from 'interfaces/tags'; import useAPI from '../useApi/useApi'; import { Operation } from 'fast-json-patch'; -import { IConstraint } from 'interfaces/strategy'; import { CreateFeatureSchema } from 'openapi'; import { openApiAdmin } from 'utils/openapiClient'; +import { IConstraint } from 'interfaces/strategy'; const useFeatureApi = () => { const { makeRequest, createRequest, errors, loading } = useAPI({ diff --git a/frontend/src/hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi.ts b/frontend/src/hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi.ts index d32a8fd614..bf695c844a 100644 --- a/frontend/src/hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi.ts +++ b/frontend/src/hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi.ts @@ -1,4 +1,4 @@ -import { IStrategyPayload, IFeatureStrategy } from 'interfaces/strategy'; +import { IFeatureStrategyPayload, IFeatureStrategy } from 'interfaces/strategy'; import useAPI from '../useApi/useApi'; const useFeatureStrategyApi = () => { @@ -10,7 +10,7 @@ const useFeatureStrategyApi = () => { projectId: string, featureId: string, environmentId: string, - payload: IStrategyPayload + payload: IFeatureStrategyPayload ): Promise => { const path = `api/admin/projects/${projectId}/features/${featureId}/environments/${environmentId}/strategies`; const req = createRequest( @@ -41,7 +41,7 @@ const useFeatureStrategyApi = () => { featureId: string, environmentId: string, strategyId: string, - payload: IStrategyPayload + payload: IFeatureStrategyPayload ): Promise => { const path = `api/admin/projects/${projectId}/features/${featureId}/environments/${environmentId}/strategies/${strategyId}`; const req = createRequest( diff --git a/frontend/src/hooks/api/actions/useStrategiesApi/useStrategiesApi.ts b/frontend/src/hooks/api/actions/useStrategiesApi/useStrategiesApi.ts index d8cf8fd0d4..3e406798a2 100644 --- a/frontend/src/hooks/api/actions/useStrategiesApi/useStrategiesApi.ts +++ b/frontend/src/hooks/api/actions/useStrategiesApi/useStrategiesApi.ts @@ -1,4 +1,4 @@ -import { ICustomStrategyPayload } from 'interfaces/strategy'; +import { IStrategyPayload } from 'interfaces/strategy'; import useAPI from '../useApi/useApi'; const useStrategiesApi = () => { @@ -7,7 +7,7 @@ const useStrategiesApi = () => { }); const URI = 'api/admin/strategies'; - const createStrategy = async (strategy: ICustomStrategyPayload) => { + const createStrategy = async (strategy: IStrategyPayload) => { const req = createRequest(URI, { method: 'POST', body: JSON.stringify(strategy), @@ -22,7 +22,7 @@ const useStrategiesApi = () => { } }; - const updateStrategy = async (strategy: ICustomStrategyPayload) => { + const updateStrategy = async (strategy: IStrategyPayload) => { const path = `${URI}/${strategy.name}`; const req = createRequest(path, { method: 'PUT', @@ -38,7 +38,7 @@ const useStrategiesApi = () => { } }; - const removeStrategy = async (strategy: ICustomStrategyPayload) => { + const removeStrategy = async (strategy: IStrategyPayload) => { const path = `${URI}/${strategy.name}`; const req = createRequest(path, { method: 'DELETE' }); @@ -51,7 +51,7 @@ const useStrategiesApi = () => { } }; - const deprecateStrategy = async (strategy: ICustomStrategyPayload) => { + const deprecateStrategy = async (strategy: IStrategyPayload) => { const path = `${URI}/${strategy.name}/deprecate`; const req = createRequest(path, { method: 'POST', @@ -66,7 +66,7 @@ const useStrategiesApi = () => { } }; - const reactivateStrategy = async (strategy: ICustomStrategyPayload) => { + const reactivateStrategy = async (strategy: IStrategyPayload) => { const path = `${URI}/${strategy.name}/reactivate`; const req = createRequest(path, { method: 'POST' }); diff --git a/frontend/src/hooks/useFeaturesFilter.test.ts b/frontend/src/hooks/useFeaturesFilter.test.ts index 1b8bdc894d..7f42822370 100644 --- a/frontend/src/hooks/useFeaturesFilter.test.ts +++ b/frontend/src/hooks/useFeaturesFilter.test.ts @@ -1,8 +1,8 @@ import { renderHook, act } from '@testing-library/react-hooks'; import { useFeaturesFilter } from 'hooks/useFeaturesFilter'; -import { IConstraint, IFeatureStrategy } from 'interfaces/strategy'; -import { FeatureSchema } from 'openapi'; +import { FeatureSchema, FeatureSchemaStrategies } from 'openapi'; import parseISO from 'date-fns/parseISO'; +import { IConstraint } from 'interfaces/strategy'; test('useFeaturesFilter empty', () => { const { result } = renderHook(() => useFeaturesFilter([])); @@ -108,8 +108,8 @@ const mockFeatureToggle = ( }; const mockFeatureStrategy = ( - overrides?: Partial -): IFeatureStrategy => { + overrides?: Partial +): FeatureSchemaStrategies => { return { id: '1', name: '1', diff --git a/frontend/src/interfaces/strategy.ts b/frontend/src/interfaces/strategy.ts index 7c3ddd394b..f81df26ad6 100644 --- a/frontend/src/interfaces/strategy.ts +++ b/frontend/src/interfaces/strategy.ts @@ -5,19 +5,42 @@ export interface IFeatureStrategy { strategyName?: string; name: string; constraints: IConstraint[]; - parameters: IParameter; + parameters: IFeatureStrategyParameters; featureName?: string; projectId?: string; environment?: string; } +export interface IFeatureStrategyParameters { + [key: string]: string | number | undefined; +} + +export interface IFeatureStrategyPayload { + name?: string; + constraints: IConstraint[]; + parameters: IFeatureStrategyParameters; +} + export interface IStrategy { name: string; displayName: string; editable: boolean; deprecated: boolean; description: string; - parameters: IParameter[]; + parameters: IStrategyParameter[]; +} + +export interface IStrategyParameter { + name: string; + description: string; + required: boolean; + type: string; +} + +export interface IStrategyPayload { + name: string; + description: string; + parameters: IStrategyParameter[]; } export interface IConstraint { @@ -27,38 +50,4 @@ export interface IConstraint { caseInsensitive?: boolean; operator: Operator; contextName: string; - [index: string]: unknown; -} - -export interface IParameter { - groupId?: string; - rollout?: string; - stickiness?: string; - - [index: string]: any; -} - -export interface IStrategyPayload { - name?: string; - constraints: IConstraint[]; - parameters: IParameter; -} -export interface ICustomStrategyParameter { - name: string; - description: string; - required: boolean; - type: string; -} - -export interface ICustomStrategyPayload { - name: string; - description: string; - parameters: IParameter[]; -} - -export interface ICustomStrategy { - name: string; - description: string; - parameters: IParameter[]; - editable: boolean; } diff --git a/frontend/src/openapi/apis/AdminApi.ts b/frontend/src/openapi/apis/AdminApi.ts index c17c86b9dc..c97e9f0f0d 100644 --- a/frontend/src/openapi/apis/AdminApi.ts +++ b/frontend/src/openapi/apis/AdminApi.ts @@ -15,21 +15,51 @@ import * as runtime from '../runtime'; import { + ChangeProjectSchema, + ChangeProjectSchemaFromJSON, + ChangeProjectSchemaToJSON, CreateFeatureSchema, CreateFeatureSchemaFromJSON, CreateFeatureSchemaToJSON, + CreateStrategySchema, + CreateStrategySchemaFromJSON, + CreateStrategySchemaToJSON, FeatureSchema, FeatureSchemaFromJSON, FeatureSchemaToJSON, FeaturesSchema, FeaturesSchemaFromJSON, FeaturesSchemaToJSON, + StrategySchema, + StrategySchemaFromJSON, + StrategySchemaToJSON, } from '../models'; export interface ApiAdminArchiveFeaturesProjectIdGetRequest { projectId: string; } +export interface ApiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPostRequest { + projectId: string; + featureName: string; + changeProjectSchema: ChangeProjectSchema; +} + +export interface ApiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesPostRequest { + projectId: string; + featureName: string; + environment: string; + createStrategySchema: CreateStrategySchema; +} + +export interface ApiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesStrategyIdPutRequest { + projectId: string; + featureName: string; + environment: string; + strategyId: string; + createStrategySchema: CreateStrategySchema; +} + export interface ApiAdminProjectsProjectIdFeaturesFeatureNameGetRequest { projectId: string; featureName: string; @@ -137,6 +167,146 @@ export class AdminApi extends runtime.BaseAPI { return await response.value(); } + /** + */ + async apiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPostRaw(requestParameters: ApiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPostRequest, initOverrides?: RequestInit): Promise> { + if (requestParameters.projectId === null || requestParameters.projectId === undefined) { + throw new runtime.RequiredError('projectId','Required parameter requestParameters.projectId was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPost.'); + } + + if (requestParameters.featureName === null || requestParameters.featureName === undefined) { + throw new runtime.RequiredError('featureName','Required parameter requestParameters.featureName was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPost.'); + } + + if (requestParameters.changeProjectSchema === null || requestParameters.changeProjectSchema === undefined) { + throw new runtime.RequiredError('changeProjectSchema','Required parameter requestParameters.changeProjectSchema was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPost.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = this.configuration.apiKey("Authorization"); // apiKey authentication + } + + const response = await this.request({ + path: `/api/admin/projects/{projectId}/features/{featureName}/changeProject`.replace(`{${"projectId"}}`, encodeURIComponent(String(requestParameters.projectId))).replace(`{${"featureName"}}`, encodeURIComponent(String(requestParameters.featureName))), + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: ChangeProjectSchemaToJSON(requestParameters.changeProjectSchema), + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPost(requestParameters: ApiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPostRequest, initOverrides?: RequestInit): Promise { + await this.apiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPostRaw(requestParameters, initOverrides); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesPostRaw(requestParameters: ApiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesPostRequest, initOverrides?: RequestInit): Promise> { + if (requestParameters.projectId === null || requestParameters.projectId === undefined) { + throw new runtime.RequiredError('projectId','Required parameter requestParameters.projectId was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesPost.'); + } + + if (requestParameters.featureName === null || requestParameters.featureName === undefined) { + throw new runtime.RequiredError('featureName','Required parameter requestParameters.featureName was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesPost.'); + } + + if (requestParameters.environment === null || requestParameters.environment === undefined) { + throw new runtime.RequiredError('environment','Required parameter requestParameters.environment was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesPost.'); + } + + if (requestParameters.createStrategySchema === null || requestParameters.createStrategySchema === undefined) { + throw new runtime.RequiredError('createStrategySchema','Required parameter requestParameters.createStrategySchema was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesPost.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = this.configuration.apiKey("Authorization"); // apiKey authentication + } + + const response = await this.request({ + path: `/api/admin/projects/{projectId}/features/{featureName}/environments/{environment}/strategies`.replace(`{${"projectId"}}`, encodeURIComponent(String(requestParameters.projectId))).replace(`{${"featureName"}}`, encodeURIComponent(String(requestParameters.featureName))).replace(`{${"environment"}}`, encodeURIComponent(String(requestParameters.environment))), + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: CreateStrategySchemaToJSON(requestParameters.createStrategySchema), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => StrategySchemaFromJSON(jsonValue)); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesPost(requestParameters: ApiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesPostRequest, initOverrides?: RequestInit): Promise { + const response = await this.apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesPostRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesStrategyIdPutRaw(requestParameters: ApiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesStrategyIdPutRequest, initOverrides?: RequestInit): Promise> { + if (requestParameters.projectId === null || requestParameters.projectId === undefined) { + throw new runtime.RequiredError('projectId','Required parameter requestParameters.projectId was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesStrategyIdPut.'); + } + + if (requestParameters.featureName === null || requestParameters.featureName === undefined) { + throw new runtime.RequiredError('featureName','Required parameter requestParameters.featureName was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesStrategyIdPut.'); + } + + if (requestParameters.environment === null || requestParameters.environment === undefined) { + throw new runtime.RequiredError('environment','Required parameter requestParameters.environment was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesStrategyIdPut.'); + } + + if (requestParameters.strategyId === null || requestParameters.strategyId === undefined) { + throw new runtime.RequiredError('strategyId','Required parameter requestParameters.strategyId was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesStrategyIdPut.'); + } + + if (requestParameters.createStrategySchema === null || requestParameters.createStrategySchema === undefined) { + throw new runtime.RequiredError('createStrategySchema','Required parameter requestParameters.createStrategySchema was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesStrategyIdPut.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = this.configuration.apiKey("Authorization"); // apiKey authentication + } + + const response = await this.request({ + path: `/api/admin/projects/{projectId}/features/{featureName}/environments/{environment}/strategies/{strategyId}`.replace(`{${"projectId"}}`, encodeURIComponent(String(requestParameters.projectId))).replace(`{${"featureName"}}`, encodeURIComponent(String(requestParameters.featureName))).replace(`{${"environment"}}`, encodeURIComponent(String(requestParameters.environment))).replace(`{${"strategyId"}}`, encodeURIComponent(String(requestParameters.strategyId))), + method: 'PUT', + headers: headerParameters, + query: queryParameters, + body: CreateStrategySchemaToJSON(requestParameters.createStrategySchema), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => StrategySchemaFromJSON(jsonValue)); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesStrategyIdPut(requestParameters: ApiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesStrategyIdPutRequest, initOverrides?: RequestInit): Promise { + const response = await this.apiAdminProjectsProjectIdFeaturesFeatureNameEnvironmentsEnvironmentStrategiesStrategyIdPutRaw(requestParameters, initOverrides); + return await response.value(); + } + /** */ async apiAdminProjectsProjectIdFeaturesFeatureNameGetRaw(requestParameters: ApiAdminProjectsProjectIdFeaturesFeatureNameGetRequest, initOverrides?: RequestInit): Promise> { diff --git a/frontend/src/openapi/models/ChangeProjectSchema.ts b/frontend/src/openapi/models/ChangeProjectSchema.ts new file mode 100644 index 0000000000..3a8d409fd7 --- /dev/null +++ b/frontend/src/openapi/models/ChangeProjectSchema.ts @@ -0,0 +1,56 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface ChangeProjectSchema + */ +export interface ChangeProjectSchema { + /** + * + * @type {string} + * @memberof ChangeProjectSchema + */ + newProjectId: string; +} + +export function ChangeProjectSchemaFromJSON(json: any): ChangeProjectSchema { + return ChangeProjectSchemaFromJSONTyped(json, false); +} + +export function ChangeProjectSchemaFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangeProjectSchema { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'newProjectId': json['newProjectId'], + }; +} + +export function ChangeProjectSchemaToJSON(value?: ChangeProjectSchema | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'newProjectId': value.newProjectId, + }; +} + diff --git a/frontend/src/openapi/models/ConstraintSchema.ts b/frontend/src/openapi/models/ConstraintSchema.ts index 5920ee9730..e613702b91 100644 --- a/frontend/src/openapi/models/ConstraintSchema.ts +++ b/frontend/src/openapi/models/ConstraintSchema.ts @@ -31,12 +31,30 @@ export interface ConstraintSchema { * @memberof ConstraintSchema */ operator: string; + /** + * + * @type {boolean} + * @memberof ConstraintSchema + */ + caseInsensitive?: boolean; + /** + * + * @type {boolean} + * @memberof ConstraintSchema + */ + inverted?: boolean; /** * * @type {Array} * @memberof ConstraintSchema */ values?: Array; + /** + * + * @type {string} + * @memberof ConstraintSchema + */ + value?: string; } export function ConstraintSchemaFromJSON(json: any): ConstraintSchema { @@ -51,7 +69,10 @@ export function ConstraintSchemaFromJSONTyped(json: any, ignoreDiscriminator: bo 'contextName': json['contextName'], 'operator': json['operator'], + 'caseInsensitive': !exists(json, 'caseInsensitive') ? undefined : json['caseInsensitive'], + 'inverted': !exists(json, 'inverted') ? undefined : json['inverted'], 'values': !exists(json, 'values') ? undefined : json['values'], + 'value': !exists(json, 'value') ? undefined : json['value'], }; } @@ -66,7 +87,10 @@ export function ConstraintSchemaToJSON(value?: ConstraintSchema | null): any { 'contextName': value.contextName, 'operator': value.operator, + 'caseInsensitive': value.caseInsensitive, + 'inverted': value.inverted, 'values': value.values, + 'value': value.value, }; } diff --git a/frontend/src/openapi/models/CreateStrategySchema.ts b/frontend/src/openapi/models/CreateStrategySchema.ts new file mode 100644 index 0000000000..a2390c31e5 --- /dev/null +++ b/frontend/src/openapi/models/CreateStrategySchema.ts @@ -0,0 +1,87 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + CreateStrategySchemaConstraints, + CreateStrategySchemaConstraintsFromJSON, + CreateStrategySchemaConstraintsFromJSONTyped, + CreateStrategySchemaConstraintsToJSON, +} from './CreateStrategySchemaConstraints'; + +/** + * + * @export + * @interface CreateStrategySchema + */ +export interface CreateStrategySchema { + /** + * + * @type {string} + * @memberof CreateStrategySchema + */ + name?: string; + /** + * + * @type {number} + * @memberof CreateStrategySchema + */ + sortOrder?: number; + /** + * + * @type {Array} + * @memberof CreateStrategySchema + */ + constraints?: Array; + /** + * + * @type {{ [key: string]: string; }} + * @memberof CreateStrategySchema + */ + parameters?: { [key: string]: string; }; +} + +export function CreateStrategySchemaFromJSON(json: any): CreateStrategySchema { + return CreateStrategySchemaFromJSONTyped(json, false); +} + +export function CreateStrategySchemaFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreateStrategySchema { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'name': !exists(json, 'name') ? undefined : json['name'], + 'sortOrder': !exists(json, 'sortOrder') ? undefined : json['sortOrder'], + 'constraints': !exists(json, 'constraints') ? undefined : ((json['constraints'] as Array).map(CreateStrategySchemaConstraintsFromJSON)), + 'parameters': !exists(json, 'parameters') ? undefined : json['parameters'], + }; +} + +export function CreateStrategySchemaToJSON(value?: CreateStrategySchema | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'name': value.name, + 'sortOrder': value.sortOrder, + 'constraints': value.constraints === undefined ? undefined : ((value.constraints as Array).map(CreateStrategySchemaConstraintsToJSON)), + 'parameters': value.parameters, + }; +} + diff --git a/frontend/src/openapi/models/CreateStrategySchemaConstraints.ts b/frontend/src/openapi/models/CreateStrategySchemaConstraints.ts new file mode 100644 index 0000000000..1a3022d0f5 --- /dev/null +++ b/frontend/src/openapi/models/CreateStrategySchemaConstraints.ts @@ -0,0 +1,96 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface CreateStrategySchemaConstraints + */ +export interface CreateStrategySchemaConstraints { + /** + * + * @type {string} + * @memberof CreateStrategySchemaConstraints + */ + contextName: string; + /** + * + * @type {string} + * @memberof CreateStrategySchemaConstraints + */ + operator: string; + /** + * + * @type {boolean} + * @memberof CreateStrategySchemaConstraints + */ + caseInsensitive?: boolean; + /** + * + * @type {boolean} + * @memberof CreateStrategySchemaConstraints + */ + inverted?: boolean; + /** + * + * @type {Array} + * @memberof CreateStrategySchemaConstraints + */ + values?: Array; + /** + * + * @type {string} + * @memberof CreateStrategySchemaConstraints + */ + value?: string; +} + +export function CreateStrategySchemaConstraintsFromJSON(json: any): CreateStrategySchemaConstraints { + return CreateStrategySchemaConstraintsFromJSONTyped(json, false); +} + +export function CreateStrategySchemaConstraintsFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreateStrategySchemaConstraints { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'contextName': json['contextName'], + 'operator': json['operator'], + 'caseInsensitive': !exists(json, 'caseInsensitive') ? undefined : json['caseInsensitive'], + 'inverted': !exists(json, 'inverted') ? undefined : json['inverted'], + 'values': !exists(json, 'values') ? undefined : json['values'], + 'value': !exists(json, 'value') ? undefined : json['value'], + }; +} + +export function CreateStrategySchemaConstraintsToJSON(value?: CreateStrategySchemaConstraints | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'contextName': value.contextName, + 'operator': value.operator, + 'caseInsensitive': value.caseInsensitive, + 'inverted': value.inverted, + 'values': value.values, + 'value': value.value, + }; +} + diff --git a/frontend/src/openapi/models/FeatureSchema.ts b/frontend/src/openapi/models/FeatureSchema.ts index 2dbe5bf063..2edf440106 100644 --- a/frontend/src/openapi/models/FeatureSchema.ts +++ b/frontend/src/openapi/models/FeatureSchema.ts @@ -14,17 +14,17 @@ import { exists, mapValues } from '../runtime'; import { - StrategySchema, - StrategySchemaFromJSON, - StrategySchemaFromJSONTyped, - StrategySchemaToJSON, -} from './StrategySchema'; + FeatureSchemaStrategies, + FeatureSchemaStrategiesFromJSON, + FeatureSchemaStrategiesFromJSONTyped, + FeatureSchemaStrategiesToJSON, +} from './FeatureSchemaStrategies'; import { - VariantSchema, - VariantSchemaFromJSON, - VariantSchemaFromJSONTyped, - VariantSchemaToJSON, -} from './VariantSchema'; + FeatureSchemaVariants, + FeatureSchemaVariantsFromJSON, + FeatureSchemaVariantsFromJSONTyped, + FeatureSchemaVariantsToJSON, +} from './FeatureSchemaVariants'; /** * @@ -88,16 +88,16 @@ export interface FeatureSchema { lastSeenAt?: Date | null; /** * - * @type {Array} + * @type {Array} * @memberof FeatureSchema */ - strategies?: Array; + strategies?: Array; /** * - * @type {Array} + * @type {Array} * @memberof FeatureSchema */ - variants?: Array; + variants?: Array; } export function FeatureSchemaFromJSON(json: any): FeatureSchema { @@ -119,8 +119,8 @@ export function FeatureSchemaFromJSONTyped(json: any, ignoreDiscriminator: boole 'impressionData': !exists(json, 'impressionData') ? undefined : json['impressionData'], 'createdAt': !exists(json, 'createdAt') ? undefined : (json['createdAt'] === null ? null : new Date(json['createdAt'])), 'lastSeenAt': !exists(json, 'lastSeenAt') ? undefined : (json['lastSeenAt'] === null ? null : new Date(json['lastSeenAt'])), - 'strategies': !exists(json, 'strategies') ? undefined : ((json['strategies'] as Array).map(StrategySchemaFromJSON)), - 'variants': !exists(json, 'variants') ? undefined : ((json['variants'] as Array).map(VariantSchemaFromJSON)), + 'strategies': !exists(json, 'strategies') ? undefined : ((json['strategies'] as Array).map(FeatureSchemaStrategiesFromJSON)), + 'variants': !exists(json, 'variants') ? undefined : ((json['variants'] as Array).map(FeatureSchemaVariantsFromJSON)), }; } @@ -142,8 +142,8 @@ export function FeatureSchemaToJSON(value?: FeatureSchema | null): any { 'impressionData': value.impressionData, 'createdAt': value.createdAt === undefined ? undefined : (value.createdAt === null ? null : value.createdAt.toISOString().substr(0,10)), 'lastSeenAt': value.lastSeenAt === undefined ? undefined : (value.lastSeenAt === null ? null : value.lastSeenAt.toISOString().substr(0,10)), - 'strategies': value.strategies === undefined ? undefined : ((value.strategies as Array).map(StrategySchemaToJSON)), - 'variants': value.variants === undefined ? undefined : ((value.variants as Array).map(VariantSchemaToJSON)), + 'strategies': value.strategies === undefined ? undefined : ((value.strategies as Array).map(FeatureSchemaStrategiesToJSON)), + 'variants': value.variants === undefined ? undefined : ((value.variants as Array).map(FeatureSchemaVariantsToJSON)), }; } diff --git a/frontend/src/openapi/models/FeatureSchemaOverrides.ts b/frontend/src/openapi/models/FeatureSchemaOverrides.ts new file mode 100644 index 0000000000..d7465adaee --- /dev/null +++ b/frontend/src/openapi/models/FeatureSchemaOverrides.ts @@ -0,0 +1,64 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface FeatureSchemaOverrides + */ +export interface FeatureSchemaOverrides { + /** + * + * @type {string} + * @memberof FeatureSchemaOverrides + */ + contextName: string; + /** + * + * @type {Array} + * @memberof FeatureSchemaOverrides + */ + values: Array; +} + +export function FeatureSchemaOverridesFromJSON(json: any): FeatureSchemaOverrides { + return FeatureSchemaOverridesFromJSONTyped(json, false); +} + +export function FeatureSchemaOverridesFromJSONTyped(json: any, ignoreDiscriminator: boolean): FeatureSchemaOverrides { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'contextName': json['contextName'], + 'values': json['values'], + }; +} + +export function FeatureSchemaOverridesToJSON(value?: FeatureSchemaOverrides | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'contextName': value.contextName, + 'values': value.values, + }; +} + diff --git a/frontend/src/openapi/models/FeatureSchemaStrategies.ts b/frontend/src/openapi/models/FeatureSchemaStrategies.ts new file mode 100644 index 0000000000..043d645a2b --- /dev/null +++ b/frontend/src/openapi/models/FeatureSchemaStrategies.ts @@ -0,0 +1,87 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + CreateStrategySchemaConstraints, + CreateStrategySchemaConstraintsFromJSON, + CreateStrategySchemaConstraintsFromJSONTyped, + CreateStrategySchemaConstraintsToJSON, +} from './CreateStrategySchemaConstraints'; + +/** + * + * @export + * @interface FeatureSchemaStrategies + */ +export interface FeatureSchemaStrategies { + /** + * + * @type {string} + * @memberof FeatureSchemaStrategies + */ + id: string; + /** + * + * @type {string} + * @memberof FeatureSchemaStrategies + */ + name: string; + /** + * + * @type {Array} + * @memberof FeatureSchemaStrategies + */ + constraints: Array; + /** + * + * @type {{ [key: string]: string; }} + * @memberof FeatureSchemaStrategies + */ + parameters: { [key: string]: string; }; +} + +export function FeatureSchemaStrategiesFromJSON(json: any): FeatureSchemaStrategies { + return FeatureSchemaStrategiesFromJSONTyped(json, false); +} + +export function FeatureSchemaStrategiesFromJSONTyped(json: any, ignoreDiscriminator: boolean): FeatureSchemaStrategies { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'id': json['id'], + 'name': json['name'], + 'constraints': ((json['constraints'] as Array).map(CreateStrategySchemaConstraintsFromJSON)), + 'parameters': json['parameters'], + }; +} + +export function FeatureSchemaStrategiesToJSON(value?: FeatureSchemaStrategies | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'id': value.id, + 'name': value.name, + 'constraints': ((value.constraints as Array).map(CreateStrategySchemaConstraintsToJSON)), + 'parameters': value.parameters, + }; +} + diff --git a/frontend/src/openapi/models/FeatureSchemaVariants.ts b/frontend/src/openapi/models/FeatureSchemaVariants.ts new file mode 100644 index 0000000000..a91d60071d --- /dev/null +++ b/frontend/src/openapi/models/FeatureSchemaVariants.ts @@ -0,0 +1,103 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + FeatureSchemaOverrides, + FeatureSchemaOverridesFromJSON, + FeatureSchemaOverridesFromJSONTyped, + FeatureSchemaOverridesToJSON, +} from './FeatureSchemaOverrides'; + +/** + * + * @export + * @interface FeatureSchemaVariants + */ +export interface FeatureSchemaVariants { + /** + * + * @type {string} + * @memberof FeatureSchemaVariants + */ + name: string; + /** + * + * @type {number} + * @memberof FeatureSchemaVariants + */ + weight: number; + /** + * + * @type {string} + * @memberof FeatureSchemaVariants + */ + weightType: string; + /** + * + * @type {string} + * @memberof FeatureSchemaVariants + */ + stickiness: string; + /** + * + * @type {object} + * @memberof FeatureSchemaVariants + */ + payload?: object; + /** + * + * @type {Array} + * @memberof FeatureSchemaVariants + */ + overrides?: Array; +} + +export function FeatureSchemaVariantsFromJSON(json: any): FeatureSchemaVariants { + return FeatureSchemaVariantsFromJSONTyped(json, false); +} + +export function FeatureSchemaVariantsFromJSONTyped(json: any, ignoreDiscriminator: boolean): FeatureSchemaVariants { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'name': json['name'], + 'weight': json['weight'], + 'weightType': json['weightType'], + 'stickiness': json['stickiness'], + 'payload': !exists(json, 'payload') ? undefined : json['payload'], + 'overrides': !exists(json, 'overrides') ? undefined : ((json['overrides'] as Array).map(FeatureSchemaOverridesFromJSON)), + }; +} + +export function FeatureSchemaVariantsToJSON(value?: FeatureSchemaVariants | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'name': value.name, + 'weight': value.weight, + 'weightType': value.weightType, + 'stickiness': value.stickiness, + 'payload': value.payload, + 'overrides': value.overrides === undefined ? undefined : ((value.overrides as Array).map(FeatureSchemaOverridesToJSON)), + }; +} + diff --git a/frontend/src/openapi/models/FeaturesSchema.ts b/frontend/src/openapi/models/FeaturesSchema.ts index 5594b87ca2..afddb49251 100644 --- a/frontend/src/openapi/models/FeaturesSchema.ts +++ b/frontend/src/openapi/models/FeaturesSchema.ts @@ -14,11 +14,11 @@ import { exists, mapValues } from '../runtime'; import { - FeatureSchema, - FeatureSchemaFromJSON, - FeatureSchemaFromJSONTyped, - FeatureSchemaToJSON, -} from './FeatureSchema'; + FeaturesSchemaFeatures, + FeaturesSchemaFeaturesFromJSON, + FeaturesSchemaFeaturesFromJSONTyped, + FeaturesSchemaFeaturesToJSON, +} from './FeaturesSchemaFeatures'; /** * @@ -34,10 +34,10 @@ export interface FeaturesSchema { version: number; /** * - * @type {Array} + * @type {Array} * @memberof FeaturesSchema */ - features: Array; + features: Array; } export function FeaturesSchemaFromJSON(json: any): FeaturesSchema { @@ -51,7 +51,7 @@ export function FeaturesSchemaFromJSONTyped(json: any, ignoreDiscriminator: bool return { 'version': json['version'], - 'features': ((json['features'] as Array).map(FeatureSchemaFromJSON)), + 'features': ((json['features'] as Array).map(FeaturesSchemaFeaturesFromJSON)), }; } @@ -65,7 +65,7 @@ export function FeaturesSchemaToJSON(value?: FeaturesSchema | null): any { return { 'version': value.version, - 'features': ((value.features as Array).map(FeatureSchemaToJSON)), + 'features': ((value.features as Array).map(FeaturesSchemaFeaturesToJSON)), }; } diff --git a/frontend/src/openapi/models/FeaturesSchemaFeatures.ts b/frontend/src/openapi/models/FeaturesSchemaFeatures.ts new file mode 100644 index 0000000000..17acfb3543 --- /dev/null +++ b/frontend/src/openapi/models/FeaturesSchemaFeatures.ts @@ -0,0 +1,149 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + FeatureSchemaStrategies, + FeatureSchemaStrategiesFromJSON, + FeatureSchemaStrategiesFromJSONTyped, + FeatureSchemaStrategiesToJSON, +} from './FeatureSchemaStrategies'; +import { + FeatureSchemaVariants, + FeatureSchemaVariantsFromJSON, + FeatureSchemaVariantsFromJSONTyped, + FeatureSchemaVariantsToJSON, +} from './FeatureSchemaVariants'; + +/** + * + * @export + * @interface FeaturesSchemaFeatures + */ +export interface FeaturesSchemaFeatures { + /** + * + * @type {string} + * @memberof FeaturesSchemaFeatures + */ + name: string; + /** + * + * @type {string} + * @memberof FeaturesSchemaFeatures + */ + type?: string; + /** + * + * @type {string} + * @memberof FeaturesSchemaFeatures + */ + description?: string; + /** + * + * @type {string} + * @memberof FeaturesSchemaFeatures + */ + project: string; + /** + * + * @type {boolean} + * @memberof FeaturesSchemaFeatures + */ + enabled?: boolean; + /** + * + * @type {boolean} + * @memberof FeaturesSchemaFeatures + */ + stale?: boolean; + /** + * + * @type {boolean} + * @memberof FeaturesSchemaFeatures + */ + impressionData?: boolean; + /** + * + * @type {Date} + * @memberof FeaturesSchemaFeatures + */ + createdAt?: Date | null; + /** + * + * @type {Date} + * @memberof FeaturesSchemaFeatures + */ + lastSeenAt?: Date | null; + /** + * + * @type {Array} + * @memberof FeaturesSchemaFeatures + */ + strategies?: Array; + /** + * + * @type {Array} + * @memberof FeaturesSchemaFeatures + */ + variants?: Array; +} + +export function FeaturesSchemaFeaturesFromJSON(json: any): FeaturesSchemaFeatures { + return FeaturesSchemaFeaturesFromJSONTyped(json, false); +} + +export function FeaturesSchemaFeaturesFromJSONTyped(json: any, ignoreDiscriminator: boolean): FeaturesSchemaFeatures { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'name': json['name'], + 'type': !exists(json, 'type') ? undefined : json['type'], + 'description': !exists(json, 'description') ? undefined : json['description'], + 'project': json['project'], + 'enabled': !exists(json, 'enabled') ? undefined : json['enabled'], + 'stale': !exists(json, 'stale') ? undefined : json['stale'], + 'impressionData': !exists(json, 'impressionData') ? undefined : json['impressionData'], + 'createdAt': !exists(json, 'createdAt') ? undefined : (json['createdAt'] === null ? null : new Date(json['createdAt'])), + 'lastSeenAt': !exists(json, 'lastSeenAt') ? undefined : (json['lastSeenAt'] === null ? null : new Date(json['lastSeenAt'])), + 'strategies': !exists(json, 'strategies') ? undefined : ((json['strategies'] as Array).map(FeatureSchemaStrategiesFromJSON)), + 'variants': !exists(json, 'variants') ? undefined : ((json['variants'] as Array).map(FeatureSchemaVariantsFromJSON)), + }; +} + +export function FeaturesSchemaFeaturesToJSON(value?: FeaturesSchemaFeatures | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'name': value.name, + 'type': value.type, + 'description': value.description, + 'project': value.project, + 'enabled': value.enabled, + 'stale': value.stale, + 'impressionData': value.impressionData, + 'createdAt': value.createdAt === undefined ? undefined : (value.createdAt === null ? null : value.createdAt.toISOString().substr(0,10)), + 'lastSeenAt': value.lastSeenAt === undefined ? undefined : (value.lastSeenAt === null ? null : value.lastSeenAt.toISOString().substr(0,10)), + 'strategies': value.strategies === undefined ? undefined : ((value.strategies as Array).map(FeatureSchemaStrategiesToJSON)), + 'variants': value.variants === undefined ? undefined : ((value.variants as Array).map(FeatureSchemaVariantsToJSON)), + }; +} + diff --git a/frontend/src/openapi/models/StrategySchema.ts b/frontend/src/openapi/models/StrategySchema.ts index 2522a14766..494a7a788c 100644 --- a/frontend/src/openapi/models/StrategySchema.ts +++ b/frontend/src/openapi/models/StrategySchema.ts @@ -14,11 +14,11 @@ import { exists, mapValues } from '../runtime'; import { - ConstraintSchema, - ConstraintSchemaFromJSON, - ConstraintSchemaFromJSONTyped, - ConstraintSchemaToJSON, -} from './ConstraintSchema'; + CreateStrategySchemaConstraints, + CreateStrategySchemaConstraintsFromJSON, + CreateStrategySchemaConstraintsFromJSONTyped, + CreateStrategySchemaConstraintsToJSON, +} from './CreateStrategySchemaConstraints'; /** * @@ -40,16 +40,16 @@ export interface StrategySchema { name: string; /** * - * @type {Array} + * @type {Array} * @memberof StrategySchema */ - constraints: Array; + constraints: Array; /** * - * @type {object} + * @type {{ [key: string]: string; }} * @memberof StrategySchema */ - parameters: object; + parameters: { [key: string]: string; }; } export function StrategySchemaFromJSON(json: any): StrategySchema { @@ -64,7 +64,7 @@ export function StrategySchemaFromJSONTyped(json: any, ignoreDiscriminator: bool 'id': json['id'], 'name': json['name'], - 'constraints': ((json['constraints'] as Array).map(ConstraintSchemaFromJSON)), + 'constraints': ((json['constraints'] as Array).map(CreateStrategySchemaConstraintsFromJSON)), 'parameters': json['parameters'], }; } @@ -80,7 +80,7 @@ export function StrategySchemaToJSON(value?: StrategySchema | null): any { 'id': value.id, 'name': value.name, - 'constraints': ((value.constraints as Array).map(ConstraintSchemaToJSON)), + 'constraints': ((value.constraints as Array).map(CreateStrategySchemaConstraintsToJSON)), 'parameters': value.parameters, }; } diff --git a/frontend/src/openapi/models/VariantSchema.ts b/frontend/src/openapi/models/VariantSchema.ts index 2144ae081f..cee84f811a 100644 --- a/frontend/src/openapi/models/VariantSchema.ts +++ b/frontend/src/openapi/models/VariantSchema.ts @@ -14,11 +14,11 @@ import { exists, mapValues } from '../runtime'; import { - OverrideSchema, - OverrideSchemaFromJSON, - OverrideSchemaFromJSONTyped, - OverrideSchemaToJSON, -} from './OverrideSchema'; + FeatureSchemaOverrides, + FeatureSchemaOverridesFromJSON, + FeatureSchemaOverridesFromJSONTyped, + FeatureSchemaOverridesToJSON, +} from './FeatureSchemaOverrides'; /** * @@ -58,10 +58,10 @@ export interface VariantSchema { payload?: object; /** * - * @type {Array} + * @type {Array} * @memberof VariantSchema */ - overrides: Array; + overrides?: Array; } export function VariantSchemaFromJSON(json: any): VariantSchema { @@ -79,7 +79,7 @@ export function VariantSchemaFromJSONTyped(json: any, ignoreDiscriminator: boole 'weightType': json['weightType'], 'stickiness': json['stickiness'], 'payload': !exists(json, 'payload') ? undefined : json['payload'], - 'overrides': ((json['overrides'] as Array).map(OverrideSchemaFromJSON)), + 'overrides': !exists(json, 'overrides') ? undefined : ((json['overrides'] as Array).map(FeatureSchemaOverridesFromJSON)), }; } @@ -97,7 +97,7 @@ export function VariantSchemaToJSON(value?: VariantSchema | null): any { 'weightType': value.weightType, 'stickiness': value.stickiness, 'payload': value.payload, - 'overrides': ((value.overrides as Array).map(OverrideSchemaToJSON)), + 'overrides': value.overrides === undefined ? undefined : ((value.overrides as Array).map(FeatureSchemaOverridesToJSON)), }; } diff --git a/frontend/src/openapi/models/index.ts b/frontend/src/openapi/models/index.ts index 925d0b6d43..1a61215e02 100644 --- a/frontend/src/openapi/models/index.ts +++ b/frontend/src/openapi/models/index.ts @@ -1,9 +1,16 @@ /* tslint:disable */ /* eslint-disable */ +export * from './ChangeProjectSchema'; export * from './ConstraintSchema'; export * from './CreateFeatureSchema'; +export * from './CreateStrategySchema'; +export * from './CreateStrategySchemaConstraints'; export * from './FeatureSchema'; +export * from './FeatureSchemaOverrides'; +export * from './FeatureSchemaStrategies'; +export * from './FeatureSchemaVariants'; export * from './FeaturesSchema'; +export * from './FeaturesSchemaFeatures'; export * from './OverrideSchema'; export * from './StrategySchema'; export * from './VariantSchema'; diff --git a/frontend/src/utils/cleanConstraint.test.ts b/frontend/src/utils/cleanConstraint.test.ts new file mode 100644 index 0000000000..b589c69a59 --- /dev/null +++ b/frontend/src/utils/cleanConstraint.test.ts @@ -0,0 +1,31 @@ +import { cleanConstraint } from 'utils/cleanConstraint'; + +test('cleanConstraint values', () => { + expect( + cleanConstraint({ + contextName: '', + operator: 'IN', + value: '1', + values: ['2'], + }) + ).toEqual({ + contextName: '', + operator: 'IN', + values: ['2'], + }); +}); + +test('cleanConstraint value', () => { + expect( + cleanConstraint({ + contextName: '', + operator: 'NUM_EQ', + value: '1', + values: ['2'], + }) + ).toEqual({ + contextName: '', + operator: 'NUM_EQ', + value: '1', + }); +}); diff --git a/frontend/src/utils/cleanConstraint.ts b/frontend/src/utils/cleanConstraint.ts index 6cb305670b..f6894f7b82 100644 --- a/frontend/src/utils/cleanConstraint.ts +++ b/frontend/src/utils/cleanConstraint.ts @@ -1,31 +1,16 @@ import { singleValueOperators } from 'constants/operators'; import { IConstraint } from 'interfaces/strategy'; import { oneOf } from 'utils/oneOf'; - -const VALUES = 'values'; -const VALUE = 'value'; +import produce from 'immer'; export const cleanConstraint = ( constraint: Readonly ): IConstraint => { - const constraintCopy: IConstraint = { - contextName: '', - operator: 'IN', - }; - - if (oneOf(singleValueOperators, constraint.operator)) { - for (const [key, value] of Object.entries(constraint)) { - if (key !== VALUES) { - constraintCopy[key] = value; - } + return produce(constraint, draft => { + if (oneOf(singleValueOperators, constraint.operator)) { + delete draft.values; + } else { + delete draft.value; } - return constraintCopy; - } else { - for (const [key, value] of Object.entries(constraint)) { - if (key !== VALUE) { - constraintCopy[key] = value; - } - } - return constraintCopy; - } + }); }; diff --git a/frontend/src/utils/getStrategyObject.ts b/frontend/src/utils/getStrategyObject.ts index d33cf6475c..5709fa6640 100644 --- a/frontend/src/utils/getStrategyObject.ts +++ b/frontend/src/utils/getStrategyObject.ts @@ -1,4 +1,8 @@ -import { IStrategy, IParameter } from '../interfaces/strategy'; +import { + IStrategy, + IStrategyParameter, + IFeatureStrategyParameters, +} from 'interfaces/strategy'; import { resolveDefaultParamValue } from 'utils/resolveDefaultParamValue'; export const getStrategyObject = ( @@ -9,9 +13,10 @@ export const getStrategyObject = ( const selectedStrategy = selectableStrategies.find( strategy => strategy.name === name ); - const parameters = {} as IParameter; - selectedStrategy?.parameters.forEach(({ name }: IParameter) => { + const parameters: IFeatureStrategyParameters = {}; + + selectedStrategy?.parameters.forEach(({ name }: IStrategyParameter) => { parameters[name] = resolveDefaultParamValue(name, featureId); }); diff --git a/frontend/src/utils/parseParameter.test.ts b/frontend/src/utils/parseParameter.test.ts new file mode 100644 index 0000000000..ff235b419d --- /dev/null +++ b/frontend/src/utils/parseParameter.test.ts @@ -0,0 +1,36 @@ +import { + parseParameterNumber, + parseParameterString, + parseParameterStrings, +} from 'utils/parseParameter'; + +test('parseParameterNumber', () => { + expect(parseParameterNumber(undefined)).toEqual(0); + expect(parseParameterNumber('')).toEqual(0); + expect(parseParameterNumber(0)).toEqual(0); + expect(parseParameterNumber(1)).toEqual(1); + expect(parseParameterNumber('a')).toEqual(0); + expect(parseParameterNumber('0')).toEqual(0); + expect(parseParameterNumber('1')).toEqual(1); +}); + +test('parseParameterString', () => { + expect(parseParameterString(undefined)).toEqual(''); + expect(parseParameterString('')).toEqual(''); + expect(parseParameterString(0)).toEqual('0'); + expect(parseParameterString(1)).toEqual('1'); + expect(parseParameterString('a')).toEqual('a'); + expect(parseParameterString('0')).toEqual('0'); + expect(parseParameterString('1')).toEqual('1'); + expect(parseParameterString(' a, ,a ')).toEqual('a, ,a'); +}); + +test('parseParameterStrings', () => { + expect(parseParameterStrings(undefined)).toEqual([]); + expect(parseParameterStrings('')).toEqual([]); + expect(parseParameterStrings(0)).toEqual(['0']); + expect(parseParameterStrings(1)).toEqual(['1']); + expect(parseParameterStrings('a')).toEqual(['a']); + expect(parseParameterStrings('a,b,c')).toEqual(['a', 'b', 'c']); + expect(parseParameterStrings(' a,, b c ,,, d,')).toEqual(['a', 'b c', 'd']); +}); diff --git a/frontend/src/utils/parseParameter.ts b/frontend/src/utils/parseParameter.ts new file mode 100644 index 0000000000..7fccc3e35c --- /dev/null +++ b/frontend/src/utils/parseParameter.ts @@ -0,0 +1,23 @@ +import { IFeatureStrategyParameters } from 'interfaces/strategy'; + +export const parseParameterNumber = ( + value: IFeatureStrategyParameters[string] +): number => { + const parsed = Number(parseParameterString(value)); + return Number.isFinite(parsed) ? parsed : 0; +}; + +export const parseParameterString = ( + value: IFeatureStrategyParameters[string] +): string => { + return String(value ?? '').trim(); +}; + +export const parseParameterStrings = ( + value: IFeatureStrategyParameters[string] +): string[] => { + return parseParameterString(value) + .split(',') + .map(s => s.trim()) + .filter(Boolean); +}; diff --git a/frontend/src/utils/resolveDefaultParamValue.ts b/frontend/src/utils/resolveDefaultParamValue.ts index 6d5c50e1ac..21de93815d 100644 --- a/frontend/src/utils/resolveDefaultParamValue.ts +++ b/frontend/src/utils/resolveDefaultParamValue.ts @@ -1,11 +1,11 @@ export const resolveDefaultParamValue = ( name: string, featureToggleName: string -): string | number => { +): string => { switch (name) { case 'percentage': case 'rollout': - return 100; + return '100'; case 'stickiness': return 'default'; case 'groupId':