Skip to content

Commit

Permalink
refactor: remove variants per environment feature flag (#3102)
Browse files Browse the repository at this point in the history
https://linear.app/unleash/issue/2-428/clean-up-feature-flag-once-were-done-with-the-migration

Cleans up the variants per environment feature flag due to GA.
  • Loading branch information
nunogois committed Feb 14, 2023
1 parent 0a32647 commit 8729f08
Show file tree
Hide file tree
Showing 19 changed files with 63 additions and 321 deletions.
78 changes: 36 additions & 42 deletions frontend/cypress/integration/feature/feature.spec.ts
Expand Up @@ -232,97 +232,91 @@ describe('feature', () => {
cy.wait('@addStrategyToFeature');
});

it('can add two variant to the feature', () => {
it('can add two variants to the development environment', () => {
cy.visit(`/projects/default/features/${featureToggleName}/variants`);

cy.intercept(
'PATCH',
`/api/admin/projects/default/features/${featureToggleName}/variants`,
`/api/admin/projects/default/features/${featureToggleName}/environments/development/variants`,
req => {
if (req.body.length === 1) {
expect(req.body[0].op).to.equal('add');
expect(req.body[0].path).to.match(/\//);
expect(req.body[0].value.name).to.equal(variant1);
} else if (req.body.length === 2) {
expect(req.body[0].op).to.equal('replace');
expect(req.body[0].path).to.match(/weight/);
expect(req.body[0].value).to.equal(500);
expect(req.body[1].op).to.equal('add');
expect(req.body[1].path).to.match(/\//);
expect(req.body[1].value.name).to.equal(variant2);
}
expect(req.body[0].op).to.equal('add');
expect(req.body[0].path).to.equal('/0');
expect(req.body[0].value.name).to.equal(variant1);
expect(req.body[0].value.weight).to.equal(500);
expect(req.body[1].op).to.equal('add');
expect(req.body[1].path).to.equal('/1');
expect(req.body[1].value.name).to.equal(variant2);
expect(req.body[1].value.weight).to.equal(500);
}
).as('variantCreation');

cy.get('[data-testid=ADD_VARIANT_BUTTON]').click();
cy.get('[data-testid=ADD_VARIANT_BUTTON]').first().click();
cy.wait(1000);
cy.get('[data-testid=VARIANT_NAME_INPUT]').type(variant1);
cy.get('[data-testid=DIALOGUE_CONFIRM_ID]').click();
cy.wait('@variantCreation');
cy.get('[data-testid=ADD_VARIANT_BUTTON]').click();
cy.wait(1000);
cy.get('[data-testid=VARIANT_NAME_INPUT]').type(variant2);
cy.get('[data-testid=MODAL_ADD_VARIANT_BUTTON]').click();
cy.get('[data-testid=VARIANT_NAME_INPUT]').last().type(variant2);
cy.get('[data-testid=DIALOGUE_CONFIRM_ID]').click();
cy.wait('@variantCreation');
});

it('can set weight to fixed value for one of the variants', () => {
cy.visit(`/projects/default/features/${featureToggleName}/variants`);

cy.get(`[data-testid=VARIANT_EDIT_BUTTON_${variant1}]`).click();
cy.get('[data-testid=EDIT_VARIANTS_BUTTON]').click();
cy.wait(1000);
cy.get('[data-testid=VARIANT_NAME_INPUT]')
.last()
.children()
.find('input')
.should('have.attr', 'disabled');
cy.get('[data-testid=VARIANT_WEIGHT_CHECK]').find('input').check();
cy.get('[data-testid=VARIANT_WEIGHT_INPUT]').clear().type('15');
cy.get('[data-testid=VARIANT_WEIGHT_CHECK]')
.last()
.find('input')
.check();
cy.get('[data-testid=VARIANT_WEIGHT_INPUT]').last().clear().type('15');

cy.intercept(
'PATCH',
`/api/admin/projects/default/features/${featureToggleName}/variants`,
`/api/admin/projects/default/features/${featureToggleName}/environments/development/variants`,
req => {
expect(req.body[0].op).to.equal('replace');
expect(req.body[0].path).to.match(/weight/);
expect(req.body[0].value).to.equal(850);
expect(req.body[0].path).to.equal('/1/weightType');
expect(req.body[0].value).to.equal('fix');
expect(req.body[1].op).to.equal('replace');
expect(req.body[1].path).to.match(/weightType/);
expect(req.body[1].value).to.equal('fix');
expect(req.body[1].path).to.equal('/1/weight');
expect(req.body[1].value).to.equal(150);
expect(req.body[2].op).to.equal('replace');
expect(req.body[2].path).to.match(/weight/);
expect(req.body[2].value).to.equal(150);
expect(req.body[2].path).to.equal('/0/weight');
expect(req.body[2].value).to.equal(850);
}
).as('variantUpdate');

cy.get('[data-testid=DIALOGUE_CONFIRM_ID]').click();
cy.wait('@variantUpdate');
cy.get(`[data-testid=VARIANT_WEIGHT_${variant1}]`).should(
cy.get(`[data-testid=VARIANT_WEIGHT_${variant2}]`).should(
'have.text',
'15 %'
);
});

it('can delete variant', () => {
const variantName = 'to-be-deleted';

cy.visit(`/projects/default/features/${featureToggleName}/variants`);
cy.get('[data-testid=ADD_VARIANT_BUTTON]').click();
cy.get('[data-testid=EDIT_VARIANTS_BUTTON]').click();
cy.wait(1000);
cy.get('[data-testid=VARIANT_NAME_INPUT]').type(variantName);
cy.get('[data-testid=DIALOGUE_CONFIRM_ID]').click();
cy.get(`[data-testid=VARIANT_DELETE_BUTTON_${variant2}]`).click();

cy.intercept(
'PATCH',
`/api/admin/projects/default/features/${featureToggleName}/variants`,
`/api/admin/projects/default/features/${featureToggleName}/environments/development/variants`,
req => {
const patch = req.body.find(
(patch: Record<string, string>) => patch.op === 'remove'
);
expect(patch.path).to.match(/\//);
expect(req.body[0].op).to.equal('remove');
expect(req.body[0].path).to.equal('/1');
expect(req.body[1].op).to.equal('replace');
expect(req.body[1].path).to.equal('/0/weight');
expect(req.body[1].value).to.equal(1000);
}
).as('delete');

cy.get(`[data-testid=VARIANT_DELETE_BUTTON_${variantName}]`).click();
cy.get('[data-testid=DIALOGUE_CONFIRM_ID]').click();
cy.wait('@delete');
});
Expand Down
6 changes: 5 additions & 1 deletion frontend/cypress/integration/import/import.spec.ts
Expand Up @@ -119,7 +119,11 @@ describe('imports', () => {
// cy.contains('Import completed');

cy.visit(`/projects/default/features/${randomFeatureName}`);
cy.contains('enabled in development');
cy.get(
"[data-testid='feature-toggle-status'] input[type='checkbox']:checked"
)
.closest('div')
.contains('development');
cy.contains('50%');
});
});
@@ -1,6 +1,5 @@
import FeatureOverviewMetaData from './FeatureOverviewMetaData/FeatureOverviewMetaData';
import FeatureOverviewEnvironments from './FeatureOverviewEnvironments/FeatureOverviewEnvironments';
import FeatureOverviewEnvSwitches from './FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitches';
import { Routes, Route, useNavigate } from 'react-router-dom';
import { FeatureStrategyCreate } from 'component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate';
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
Expand All @@ -10,8 +9,6 @@ import {
} from 'component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { usePageTitle } from 'hooks/usePageTitle';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { FeatureOverviewSidePanel } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanel';
import { useHiddenEnvironments } from 'hooks/useHiddenEnvironments';
import { styled } from '@mui/material';
Expand All @@ -34,7 +31,6 @@ const StyledMainContent = styled('div')(({ theme }) => ({
}));

const FeatureOverview = () => {
const { uiConfig } = useUiConfig();
const navigate = useNavigate();
const projectId = useRequiredPathParam('projectId');
const featureId = useRequiredPathParam('featureId');
Expand All @@ -48,20 +44,9 @@ const FeatureOverview = () => {
<StyledContainer>
<div>
<FeatureOverviewMetaData />
<ConditionallyRender
condition={Boolean(uiConfig.flags.variantsPerEnvironment)}
show={
<FeatureOverviewSidePanel
hiddenEnvironments={hiddenEnvironments}
setHiddenEnvironments={setHiddenEnvironments}
/>
}
elseShow={
<FeatureOverviewEnvSwitches
hiddenEnvironments={hiddenEnvironments}
setHiddenEnvironments={setHiddenEnvironments}
/>
}
<FeatureOverviewSidePanel
hiddenEnvironments={hiddenEnvironments}
setHiddenEnvironments={setHiddenEnvironments}
/>
</div>
<StyledMainContent>
Expand Down
Expand Up @@ -145,17 +145,6 @@ const FeatureOverviewMetaData = () => {
/>
</StyledBody>
</StyledPaddingContainerTop>
<ConditionallyRender
condition={
tags.length > 0 &&
!Boolean(uiConfig.flags.variantsPerEnvironment)
}
show={
<StyledPaddingContainerBottom>
<FeatureOverviewTags projectId={projectId} />
</StyledPaddingContainerBottom>
}
/>
</StyledContainer>
);
};
Expand Down
Expand Up @@ -59,7 +59,7 @@ export const FeatureOverviewSidePanelEnvironmentSwitches = ({
environment => environment.enabled && environment.variants?.length
);
return (
<StyledContainer>
<StyledContainer data-testid="feature-toggle-status">
{header}
{feature.environments.map(environment => {
const strategiesLabel =
Expand Down
Expand Up @@ -284,6 +284,7 @@ export const EnvironmentVariantsModal = ({
</StyledName>
</div>
<PermissionButton
data-testid="MODAL_ADD_VARIANT_BUTTON"
onClick={() =>
setVariantsEdit(variantsEdit => [
...variantsEdit,
Expand Down Expand Up @@ -400,6 +401,7 @@ export const EnvironmentVariantsModal = ({

<StyledButtonContainer>
<Button
data-testid="DIALOGUE_CONFIRM_ID"
type="submit"
variant="contained"
color="primary"
Expand Down
Expand Up @@ -304,6 +304,7 @@ export const VariantForm = ({
>
<div>
<IconButton
data-testid={`VARIANT_DELETE_BUTTON_${variant.name}`}
onClick={() => removeVariant(variant.id)}
disabled={isProtectedVariant(variant)}
>
Expand All @@ -318,6 +319,7 @@ export const VariantForm = ({
This will be used to identify the variant in your code
</StyledSubLabel>
<StyledInput
data-testid="VARIANT_NAME_INPUT"
autoFocus
label="Variant name"
error={Boolean(errors.name)}
Expand All @@ -336,6 +338,7 @@ export const VariantForm = ({
label="Custom percentage"
control={
<Switch
data-testid="VARIANT_WEIGHT_CHECK"
checked={customPercentage}
onChange={e =>
setCustomPercentage(
Expand All @@ -346,6 +349,7 @@ export const VariantForm = ({
}
/>
<StyledWeightInput
data-testid="VARIANT_WEIGHT_INPUT"
type="number"
label="Variant weight"
error={Boolean(errors.percentage)}
Expand Down
Expand Up @@ -318,6 +318,7 @@ export const FeatureEnvironmentVariants = () => {
)}
show={
<PermissionIconButton
data-testid="EDIT_VARIANTS_BUTTON"
onClick={() =>
editVariants(environment)
}
Expand All @@ -332,6 +333,7 @@ export const FeatureEnvironmentVariants = () => {
}
elseShow={
<PermissionButton
data-testid="ADD_VARIANT_BUTTON"
onClick={() =>
editVariants(environment)
}
Expand Down
@@ -1,18 +1,10 @@
import { FeatureVariantsList } from './FeatureVariantsList/FeatureVariantsList';
import { usePageTitle } from 'hooks/usePageTitle';
import { FeatureEnvironmentVariants } from './FeatureEnvironmentVariants/FeatureEnvironmentVariants';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';

const FeatureVariants = () => {
usePageTitle('Variants');

const { uiConfig } = useUiConfig();

if (uiConfig.flags.variantsPerEnvironment) {
return <FeatureEnvironmentVariants />;
}

return <FeatureVariantsList />;
return <FeatureEnvironmentVariants />;
};

export default FeatureVariants;
21 changes: 5 additions & 16 deletions frontend/src/hooks/api/getters/useFeature/useFeature.ts
Expand Up @@ -4,7 +4,6 @@ import { emptyFeature } from './emptyFeature';
import handleErrorResponses from '../httpErrorResponseHandler';
import { formatApiPath } from 'utils/formatPath';
import { IFeatureToggle } from 'interfaces/featureToggle';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';

export interface IUseFeatureOutput {
feature: IFeatureToggle;
Expand All @@ -26,14 +25,9 @@ export const useFeature = (
): IUseFeatureOutput => {
const path = formatFeatureApiPath(projectId, featureId);

const { uiConfig } = useUiConfig();
const {
flags: { variantsPerEnvironment },
} = uiConfig;

const { data, error, mutate } = useSWR<IFeatureResponse>(
['useFeature', path, variantsPerEnvironment],
() => featureFetcher(path, variantsPerEnvironment),
['useFeature', path],
() => featureFetcher(path),
options
);

Expand All @@ -51,14 +45,9 @@ export const useFeature = (
};

export const featureFetcher = async (
path: string,
variantsPerEnvironment?: boolean
path: string
): Promise<IFeatureResponse> => {
const variantEnvironments = variantsPerEnvironment
? '?variantEnvironments=true'
: '';

const res = await fetch(path + variantEnvironments);
const res = await fetch(path);

if (res.status === 404) {
return { status: 404 };
Expand All @@ -79,6 +68,6 @@ export const formatFeatureApiPath = (
featureId: string
): string => {
return formatApiPath(
`api/admin/projects/${projectId}/features/${featureId}`
`api/admin/projects/${projectId}/features/${featureId}?variantEnvironments=true`
);
};
1 change: 0 additions & 1 deletion frontend/src/interfaces/uiConfig.ts
Expand Up @@ -38,7 +38,6 @@ export interface IFlags {
UG?: boolean;
ENABLE_DARK_MODE_SUPPORT?: boolean;
embedProxyFrontend?: boolean;
variantsPerEnvironment?: boolean;
networkView?: boolean;
maintenance?: boolean;
maintenanceMode?: boolean;
Expand Down
2 changes: 0 additions & 2 deletions src/lib/__snapshots__/create-config.test.ts.snap
Expand Up @@ -84,7 +84,6 @@ exports[`should create default config 1`] = `
"responseTimeWithAppName": false,
"showProjectApiAccess": false,
"strictSchemaValidation": false,
"variantsPerEnvironment": false,
},
},
"flagResolver": FlagResolver {
Expand All @@ -106,7 +105,6 @@ exports[`should create default config 1`] = `
"responseTimeWithAppName": false,
"showProjectApiAccess": false,
"strictSchemaValidation": false,
"variantsPerEnvironment": false,
},
"externalResolver": {
"isEnabled": [Function],
Expand Down
7 changes: 0 additions & 7 deletions src/lib/services/environment-service.ts
Expand Up @@ -97,13 +97,6 @@ export default class EnvironmentService {
environment,
projectId,
);

if (!this.flagResolver.isEnabled('variantsPerEnvironment')) {
await this.featureEnvironmentStore.clonePreviousVariants(
environment,
projectId,
);
}
} catch (e) {
if (e.code === UNIQUE_CONSTRAINT_VIOLATION) {
throw new NameExistsError(
Expand Down

0 comments on commit 8729f08

Please sign in to comment.