mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-04 13:48:56 +02:00
chore!: removing userId strategy for new installations of Unleash (#9800)
This removes a strategy that was already deprecated, but only for new installations. I tested starting with an installation with this strategy being used and then updating, and I was still able to edit the strategy, so this should not impact current users. On a fresh install the strategy is no longer available. --------- Co-authored-by: Nuno Góis <github@nunogois.com>
This commit is contained in:
parent
8050f25add
commit
5019f4fcbc
4
frontend/cypress/global.d.ts
vendored
4
frontend/cypress/global.d.ts
vendored
@ -54,10 +54,6 @@ declare namespace Cypress {
|
|||||||
deleteSegment_UI(segmentName: string, id: string): Chainable;
|
deleteSegment_UI(segmentName: string, id: string): Chainable;
|
||||||
|
|
||||||
// STRATEGY
|
// STRATEGY
|
||||||
addUserIdStrategyToFeature_UI(
|
|
||||||
featureName: string,
|
|
||||||
projectName: string,
|
|
||||||
): Chainable;
|
|
||||||
addFlexibleRolloutStrategyToFeature_UI(
|
addFlexibleRolloutStrategyToFeature_UI(
|
||||||
options: AddFlexibleRolloutStrategyOptions,
|
options: AddFlexibleRolloutStrategyOptions,
|
||||||
): Chainable;
|
): Chainable;
|
||||||
|
@ -257,57 +257,6 @@ export const deleteFeatureStrategy_UI = (
|
|||||||
return cy.wait('@deleteUserStrategy');
|
return cy.wait('@deleteUserStrategy');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addUserIdStrategyToFeature_UI = (
|
|
||||||
featureToggleName: string,
|
|
||||||
projectName: string,
|
|
||||||
): Chainable<any> => {
|
|
||||||
const project = projectName || 'default';
|
|
||||||
cy.visit(
|
|
||||||
`/projects/${project}/features/${featureToggleName}/strategies/create?environmentId=development&strategyName=userWithId`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (ENTERPRISE) {
|
|
||||||
cy.get('[data-testid=ADD_CONSTRAINT_ID]').click();
|
|
||||||
cy.get('[data-testid=CONSTRAINT_AUTOCOMPLETE_ID]')
|
|
||||||
.type('{downArrow}'.repeat(1))
|
|
||||||
.type('{enter}');
|
|
||||||
cy.get('[data-testid=DIALOGUE_CONFIRM_ID]').click();
|
|
||||||
}
|
|
||||||
|
|
||||||
cy.get('[data-testid=STRATEGY_INPUT_LIST]')
|
|
||||||
.type('user1')
|
|
||||||
.type('{enter}')
|
|
||||||
.type('user2')
|
|
||||||
.type('{enter}');
|
|
||||||
cy.get('[data-testid=ADD_TO_STRATEGY_INPUT_LIST]').click();
|
|
||||||
|
|
||||||
cy.intercept(
|
|
||||||
'POST',
|
|
||||||
`/api/admin/projects/default/features/${featureToggleName}/environments/*/strategies`,
|
|
||||||
(req) => {
|
|
||||||
expect(req.body.name).to.equal('userWithId');
|
|
||||||
|
|
||||||
expect(req.body.parameters.userIds.length).to.equal(11);
|
|
||||||
|
|
||||||
if (ENTERPRISE) {
|
|
||||||
expect(req.body.constraints.length).to.equal(1);
|
|
||||||
} else {
|
|
||||||
expect(req.body.constraints.length).to.equal(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
req.continue((res) => {
|
|
||||||
strategyId = res.body.id;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
).as('addStrategyToFeature');
|
|
||||||
|
|
||||||
// this one needs to wait until the dropdown selector of stickiness is set, that's why waitForAnimations: true
|
|
||||||
cy.get(`[data-testid=STRATEGY_FORM_SUBMIT_ID]`)
|
|
||||||
.first()
|
|
||||||
.click({ waitForAnimations: true });
|
|
||||||
return cy.wait('@addStrategyToFeature');
|
|
||||||
};
|
|
||||||
|
|
||||||
export const logout_UI = (): Chainable<any> => {
|
export const logout_UI = (): Chainable<any> => {
|
||||||
return cy.visit('/logout');
|
return cy.visit('/logout');
|
||||||
};
|
};
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
deleteSegment_UI,
|
deleteSegment_UI,
|
||||||
deleteFeatureStrategy_UI,
|
deleteFeatureStrategy_UI,
|
||||||
addFlexibleRolloutStrategyToFeature_UI,
|
addFlexibleRolloutStrategyToFeature_UI,
|
||||||
addUserIdStrategyToFeature_UI,
|
|
||||||
updateFlexibleRolloutStrategy_UI,
|
updateFlexibleRolloutStrategy_UI,
|
||||||
do_login,
|
do_login,
|
||||||
} from './UI.ts';
|
} from './UI.ts';
|
||||||
@ -45,10 +44,6 @@ Cypress.Commands.add('updateUserPassword_API', updateUserPassword_API);
|
|||||||
Cypress.Commands.add('createFeature_UI', createFeature_UI);
|
Cypress.Commands.add('createFeature_UI', createFeature_UI);
|
||||||
Cypress.Commands.add('deleteFeatureStrategy_UI', deleteFeatureStrategy_UI);
|
Cypress.Commands.add('deleteFeatureStrategy_UI', deleteFeatureStrategy_UI);
|
||||||
Cypress.Commands.add('createFeature_API', createFeature_API);
|
Cypress.Commands.add('createFeature_API', createFeature_API);
|
||||||
Cypress.Commands.add(
|
|
||||||
'addUserIdStrategyToFeature_UI',
|
|
||||||
addUserIdStrategyToFeature_UI,
|
|
||||||
);
|
|
||||||
Cypress.Commands.add(
|
Cypress.Commands.add(
|
||||||
'addFlexibleRolloutStrategyToFeature_UI',
|
'addFlexibleRolloutStrategyToFeature_UI',
|
||||||
addFlexibleRolloutStrategyToFeature_UI,
|
addFlexibleRolloutStrategyToFeature_UI,
|
||||||
|
@ -98,17 +98,20 @@ const setupOtherRoutes = (feature: string) => {
|
|||||||
deprecated: false,
|
deprecated: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'UserIDs',
|
displayName: 'Gradual rollout',
|
||||||
name: 'userWithId',
|
name: 'flexibleRollout',
|
||||||
editable: false,
|
editable: false,
|
||||||
description:
|
description:
|
||||||
'Enable the feature for a specific set of userIds.',
|
'The gradual rollout strategy allows you to gradually roll out a feature to a percentage of users.',
|
||||||
parameters: [
|
parameters: [
|
||||||
{
|
{
|
||||||
name: 'userIds',
|
name: 'rollout',
|
||||||
type: 'list',
|
},
|
||||||
description: '',
|
{
|
||||||
required: false,
|
name: 'stickiness',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'groupId',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
deprecated: false,
|
deprecated: false,
|
||||||
@ -214,12 +217,10 @@ const UnleashUiSetup: FC<{
|
|||||||
</SWRConfig>
|
</SWRConfig>
|
||||||
);
|
);
|
||||||
|
|
||||||
const strategiesAreDisplayed = async (
|
const strategiesAreDisplayed = async (strategies: string[]) => {
|
||||||
firstStrategy: string,
|
for (const strategy of strategies) {
|
||||||
secondStrategy: string,
|
await screen.findByText(strategy);
|
||||||
) => {
|
}
|
||||||
await screen.findByText(firstStrategy);
|
|
||||||
await screen.findByText(secondStrategy);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDeleteButtons = async () => {
|
const getDeleteButtons = async () => {
|
||||||
@ -299,7 +300,7 @@ test('open mode + non-project member can perform basic change request actions',
|
|||||||
const featureName = 'test';
|
const featureName = 'test';
|
||||||
featureEnvironments(featureName, [
|
featureEnvironments(featureName, [
|
||||||
{ name: 'development', strategies: [] },
|
{ name: 'development', strategies: [] },
|
||||||
{ name: 'production', strategies: ['userWithId'] },
|
{ name: 'production', strategies: ['flexibleRollout'] },
|
||||||
{ name: 'custom', strategies: ['default'] },
|
{ name: 'custom', strategies: ['default'] },
|
||||||
]);
|
]);
|
||||||
userIsMemberOfProjects([]);
|
userIsMemberOfProjects([]);
|
||||||
@ -318,7 +319,7 @@ test('open mode + non-project member can perform basic change request actions',
|
|||||||
);
|
);
|
||||||
await openEnvironments(['development', 'production', 'custom']);
|
await openEnvironments(['development', 'production', 'custom']);
|
||||||
|
|
||||||
await strategiesAreDisplayed('UserIDs', 'Standard');
|
await strategiesAreDisplayed(['Gradual rollout', 'Standard']);
|
||||||
await deleteButtonsActiveInChangeRequestEnv();
|
await deleteButtonsActiveInChangeRequestEnv();
|
||||||
await copyButtonsActiveInOtherEnv();
|
await copyButtonsActiveInOtherEnv();
|
||||||
});
|
});
|
||||||
@ -328,7 +329,7 @@ test('protected mode + project member can perform basic change request actions',
|
|||||||
const featureName = 'test';
|
const featureName = 'test';
|
||||||
featureEnvironments(featureName, [
|
featureEnvironments(featureName, [
|
||||||
{ name: 'development', strategies: [] },
|
{ name: 'development', strategies: [] },
|
||||||
{ name: 'production', strategies: ['userWithId'] },
|
{ name: 'production', strategies: ['flexibleRollout'] },
|
||||||
{ name: 'custom', strategies: ['default'] },
|
{ name: 'custom', strategies: ['default'] },
|
||||||
]);
|
]);
|
||||||
userIsMemberOfProjects([project]);
|
userIsMemberOfProjects([project]);
|
||||||
@ -348,7 +349,7 @@ test('protected mode + project member can perform basic change request actions',
|
|||||||
|
|
||||||
await openEnvironments(['development', 'production', 'custom']);
|
await openEnvironments(['development', 'production', 'custom']);
|
||||||
|
|
||||||
await strategiesAreDisplayed('UserIDs', 'Standard');
|
await strategiesAreDisplayed(['Gradual rollout', 'Standard']);
|
||||||
await deleteButtonsActiveInChangeRequestEnv();
|
await deleteButtonsActiveInChangeRequestEnv();
|
||||||
await copyButtonsActiveInOtherEnv();
|
await copyButtonsActiveInOtherEnv();
|
||||||
});
|
});
|
||||||
@ -358,7 +359,7 @@ test.skip('protected mode + non-project member cannot perform basic change reque
|
|||||||
const featureName = 'test';
|
const featureName = 'test';
|
||||||
featureEnvironments(featureName, [
|
featureEnvironments(featureName, [
|
||||||
{ name: 'development', strategies: [] },
|
{ name: 'development', strategies: [] },
|
||||||
{ name: 'production', strategies: ['userWithId'] },
|
{ name: 'production', strategies: ['flexibleRollout'] },
|
||||||
{ name: 'custom', strategies: ['default'] },
|
{ name: 'custom', strategies: ['default'] },
|
||||||
]);
|
]);
|
||||||
userIsMemberOfProjects([]);
|
userIsMemberOfProjects([]);
|
||||||
@ -378,7 +379,7 @@ test.skip('protected mode + non-project member cannot perform basic change reque
|
|||||||
|
|
||||||
await openEnvironments(['development', 'production', 'custom']);
|
await openEnvironments(['development', 'production', 'custom']);
|
||||||
|
|
||||||
await strategiesAreDisplayed('UserIDs', 'Standard');
|
await strategiesAreDisplayed(['Gradual rollout', 'Standard']);
|
||||||
await deleteButtonsInactiveInChangeRequestEnv();
|
await deleteButtonsInactiveInChangeRequestEnv();
|
||||||
await copyButtonsActiveInOtherEnv();
|
await copyButtonsActiveInOtherEnv();
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import type { IFeatureStrategy, IStrategy } from 'interfaces/strategy';
|
import type { IFeatureStrategy, IStrategy } from 'interfaces/strategy';
|
||||||
import DefaultStrategy from 'component/feature/StrategyTypes/DefaultStrategy/DefaultStrategy';
|
import DefaultStrategy from 'component/feature/StrategyTypes/DefaultStrategy/DefaultStrategy';
|
||||||
import FlexibleStrategy from 'component/feature/StrategyTypes/FlexibleStrategy/FlexibleStrategy';
|
import FlexibleStrategy from 'component/feature/StrategyTypes/FlexibleStrategy/FlexibleStrategy';
|
||||||
import UserWithIdStrategy from 'component/feature/StrategyTypes/UserWithIdStrategy/UserWithId';
|
|
||||||
import GeneralStrategy from 'component/feature/StrategyTypes/GeneralStrategy/GeneralStrategy';
|
import GeneralStrategy from 'component/feature/StrategyTypes/GeneralStrategy/GeneralStrategy';
|
||||||
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
|
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
@ -52,15 +51,6 @@ export const FeatureStrategyType = ({
|
|||||||
errors={errors}
|
errors={errors}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'userWithId':
|
|
||||||
return (
|
|
||||||
<UserWithIdStrategy
|
|
||||||
parameters={strategy.parameters ?? {}}
|
|
||||||
updateParameter={updateParameter}
|
|
||||||
editable={hasAccess}
|
|
||||||
errors={errors}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<GeneralStrategy
|
<GeneralStrategy
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
import type { IFeatureStrategyParameters } from 'interfaces/strategy';
|
|
||||||
import StrategyInputList from '../StrategyInputList/StrategyInputList.tsx';
|
|
||||||
import { parseParameterStrings } from 'utils/parseParameter';
|
|
||||||
import type { IFormErrors } from 'hooks/useFormErrors';
|
|
||||||
|
|
||||||
interface IUserWithIdStrategyProps {
|
|
||||||
parameters: IFeatureStrategyParameters;
|
|
||||||
updateParameter: (field: string, value: string) => void;
|
|
||||||
editable: boolean;
|
|
||||||
errors: IFormErrors;
|
|
||||||
}
|
|
||||||
|
|
||||||
const UserWithIdStrategy = ({
|
|
||||||
editable,
|
|
||||||
parameters,
|
|
||||||
updateParameter,
|
|
||||||
errors,
|
|
||||||
}: IUserWithIdStrategyProps) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<StrategyInputList
|
|
||||||
name='userIds'
|
|
||||||
list={parseParameterStrings(parameters.userIds)}
|
|
||||||
disabled={!editable}
|
|
||||||
setConfig={updateParameter}
|
|
||||||
errors={errors}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default UserWithIdStrategy;
|
|
@ -3,7 +3,6 @@ import type { IReleasePlanMilestoneStrategy } from 'interfaces/releasePlans';
|
|||||||
import type { IStrategy } from 'interfaces/strategy';
|
import type { IStrategy } from 'interfaces/strategy';
|
||||||
import { MilestoneStrategyTypeFlexible } from './MilestoneStrategyTypeFlexible.tsx';
|
import { MilestoneStrategyTypeFlexible } from './MilestoneStrategyTypeFlexible.tsx';
|
||||||
import GeneralStrategy from 'component/feature/StrategyTypes/GeneralStrategy/GeneralStrategy';
|
import GeneralStrategy from 'component/feature/StrategyTypes/GeneralStrategy/GeneralStrategy';
|
||||||
import UserWithIdStrategy from 'component/feature/StrategyTypes/UserWithIdStrategy/UserWithId';
|
|
||||||
import DefaultStrategy from 'component/feature/StrategyTypes/DefaultStrategy/DefaultStrategy';
|
import DefaultStrategy from 'component/feature/StrategyTypes/DefaultStrategy/DefaultStrategy';
|
||||||
|
|
||||||
interface IMilestoneStrategyTypeProps {
|
interface IMilestoneStrategyTypeProps {
|
||||||
@ -36,15 +35,6 @@ export const MilestoneStrategyType = ({
|
|||||||
editable={true}
|
editable={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'userWithId':
|
|
||||||
return (
|
|
||||||
<UserWithIdStrategy
|
|
||||||
editable={true}
|
|
||||||
parameters={strategy.parameters ?? {}}
|
|
||||||
updateParameter={updateParameter}
|
|
||||||
errors={errors}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<GeneralStrategy
|
<GeneralStrategy
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import type { FC, SVGProps } from 'react';
|
import type { FC, SVGProps } from 'react';
|
||||||
import { SvgIcon, useTheme } from '@mui/material';
|
import { SvgIcon, useTheme } from '@mui/material';
|
||||||
import LocationOnIcon from '@mui/icons-material/LocationOn';
|
import LocationOnIcon from '@mui/icons-material/LocationOn';
|
||||||
import PeopleIcon from '@mui/icons-material/People';
|
|
||||||
import LanguageIcon from '@mui/icons-material/Language';
|
import LanguageIcon from '@mui/icons-material/Language';
|
||||||
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
||||||
import CodeIcon from '@mui/icons-material/Code';
|
import CodeIcon from '@mui/icons-material/Code';
|
||||||
@ -28,8 +27,6 @@ export const getFeatureStrategyIcon = (strategyName?: string) => {
|
|||||||
return LanguageIcon;
|
return LanguageIcon;
|
||||||
case 'flexibleRollout':
|
case 'flexibleRollout':
|
||||||
return RolloutSvgIcon;
|
return RolloutSvgIcon;
|
||||||
case 'userWithId':
|
|
||||||
return PeopleIcon;
|
|
||||||
case 'applicationHostname':
|
case 'applicationHostname':
|
||||||
return LocationOnIcon;
|
return LocationOnIcon;
|
||||||
case 'releasePlanTemplate':
|
case 'releasePlanTemplate':
|
||||||
@ -47,7 +44,6 @@ export const BuiltInStrategies = [
|
|||||||
'gradualRolloutSessionId',
|
'gradualRolloutSessionId',
|
||||||
'gradualRolloutUserId',
|
'gradualRolloutUserId',
|
||||||
'remoteAddress',
|
'remoteAddress',
|
||||||
'userWithId',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const GetFeatureStrategyIcon: FC<{ strategyName: string }> = ({
|
export const GetFeatureStrategyIcon: FC<{ strategyName: string }> = ({
|
||||||
@ -66,5 +62,4 @@ export const formattedStrategyNames: Record<string, string> = {
|
|||||||
gradualRolloutSessionId: 'Sessions',
|
gradualRolloutSessionId: 'Sessions',
|
||||||
gradualRolloutUserId: 'Users',
|
gradualRolloutUserId: 'Users',
|
||||||
remoteAddress: 'IPs',
|
remoteAddress: 'IPs',
|
||||||
userWithId: 'UserIDs',
|
|
||||||
};
|
};
|
||||||
|
@ -239,11 +239,3 @@ exports[`Should format specialised text for events when strategy removed 1`] = `
|
|||||||
"url": "unleashUrl/projects/my-other-project/features/new-feature",
|
"url": "unleashUrl/projects/my-other-project/features/new-feature",
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Should format specialised text for events when userIds changed 1`] = `
|
|
||||||
{
|
|
||||||
"label": "Flag strategy updated",
|
|
||||||
"text": "*user@company.com* updated *[new-feature](unleashUrl/projects/my-other-project/features/new-feature)* in project *[my-other-project](unleashUrl/projects/my-other-project)* by updating strategy *userWithId* in *production* userIds from empty set of userIds to [a,b]; constraints from empty set of constraints to [appName is one of (x,y)]",
|
|
||||||
"url": "unleashUrl/projects/my-other-project/features/new-feature",
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
@ -352,46 +352,6 @@ const testCases: [string, IEvent][] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
[
|
|
||||||
'when userIds changed',
|
|
||||||
{
|
|
||||||
id: 920,
|
|
||||||
type: FEATURE_STRATEGY_UPDATE,
|
|
||||||
createdBy: 'user@company.com',
|
|
||||||
createdByUserId: SYSTEM_USER_ID,
|
|
||||||
createdAt: new Date('2022-06-01T10:03:11.549Z'),
|
|
||||||
data: {
|
|
||||||
name: 'userWithId',
|
|
||||||
constraints: [
|
|
||||||
{
|
|
||||||
values: ['x', 'y'],
|
|
||||||
inverted: false,
|
|
||||||
operator: IN,
|
|
||||||
contextName: 'appName',
|
|
||||||
caseInsensitive: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
parameters: {
|
|
||||||
userIds: 'a,b',
|
|
||||||
},
|
|
||||||
sortOrder: 9999,
|
|
||||||
id: '9a995d94-5944-4897-a82f-0f7e65c2fb3f',
|
|
||||||
},
|
|
||||||
preData: {
|
|
||||||
name: 'userWithId',
|
|
||||||
constraints: [],
|
|
||||||
parameters: {
|
|
||||||
userIds: '',
|
|
||||||
},
|
|
||||||
sortOrder: 9999,
|
|
||||||
id: '9a995d94-5944-4897-a82f-0f7e65c2fb3f',
|
|
||||||
},
|
|
||||||
tags: [],
|
|
||||||
featureName: 'new-feature',
|
|
||||||
project: 'my-other-project',
|
|
||||||
environment: 'production',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
'when IPs changed',
|
'when IPs changed',
|
||||||
{
|
{
|
||||||
|
@ -135,8 +135,6 @@ export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
|||||||
return this.flexibleRolloutStrategyChangeText(event);
|
return this.flexibleRolloutStrategyChangeText(event);
|
||||||
case 'default':
|
case 'default':
|
||||||
return this.defaultStrategyChangeText(event);
|
return this.defaultStrategyChangeText(event);
|
||||||
case 'userWithId':
|
|
||||||
return this.userWithIdStrategyChangeText(event);
|
|
||||||
case 'remoteAddress':
|
case 'remoteAddress':
|
||||||
return this.remoteAddressStrategyChangeText(event);
|
return this.remoteAddressStrategyChangeText(event);
|
||||||
case 'applicationHostname':
|
case 'applicationHostname':
|
||||||
@ -162,10 +160,6 @@ export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
|||||||
return this.listOfValuesStrategyChangeText(event, 'IPs');
|
return this.listOfValuesStrategyChangeText(event, 'IPs');
|
||||||
}
|
}
|
||||||
|
|
||||||
private userWithIdStrategyChangeText(event: IEvent) {
|
|
||||||
return this.listOfValuesStrategyChangeText(event, 'userIds');
|
|
||||||
}
|
|
||||||
|
|
||||||
private listOfValuesStrategyChangeText(
|
private listOfValuesStrategyChangeText(
|
||||||
event: IEvent,
|
event: IEvent,
|
||||||
propertyName: string,
|
propertyName: string,
|
||||||
|
@ -63,9 +63,9 @@ exports[`should match snapshot from /api/client/features 1`] = `
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
"meta": {
|
"meta": {
|
||||||
"etag": ""61824cd0:20"",
|
"etag": ""61824cd0:19"",
|
||||||
"queryHash": "61824cd0",
|
"queryHash": "61824cd0",
|
||||||
"revisionId": 20,
|
"revisionId": 19,
|
||||||
},
|
},
|
||||||
"query": {
|
"query": {
|
||||||
"environment": "default",
|
"environment": "default",
|
||||||
|
@ -354,7 +354,10 @@ class EventStore implements IEventStore {
|
|||||||
.select(EVENT_COLUMNS)
|
.select(EVENT_COLUMNS)
|
||||||
.from(TABLE)
|
.from(TABLE)
|
||||||
.limit(100)
|
.limit(100)
|
||||||
.orderBy('created_at', 'desc');
|
.orderBy([
|
||||||
|
{ column: 'created_at', order: 'desc' },
|
||||||
|
{ column: 'id', order: 'desc' },
|
||||||
|
]);
|
||||||
if (query) {
|
if (query) {
|
||||||
qB = qB.where(query);
|
qB = qB.where(query);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import DefaultStrategy from './default-strategy.js';
|
|||||||
import GradualRolloutRandomStrategy from './gradual-rollout-random.js';
|
import GradualRolloutRandomStrategy from './gradual-rollout-random.js';
|
||||||
import GradualRolloutUserIdStrategy from './gradual-rollout-user-id.js';
|
import GradualRolloutUserIdStrategy from './gradual-rollout-user-id.js';
|
||||||
import GradualRolloutSessionIdStrategy from './gradual-rollout-session-id.js';
|
import GradualRolloutSessionIdStrategy from './gradual-rollout-session-id.js';
|
||||||
import UserWithIdStrategy from './user-with-id-strategy.js';
|
|
||||||
import RemoteAddressStrategy from './remote-address-strategy.js';
|
import RemoteAddressStrategy from './remote-address-strategy.js';
|
||||||
import FlexibleRolloutStrategy from './flexible-rollout-strategy.js';
|
import FlexibleRolloutStrategy from './flexible-rollout-strategy.js';
|
||||||
import type { Strategy } from './strategy.js';
|
import type { Strategy } from './strategy.js';
|
||||||
@ -18,7 +17,6 @@ export const defaultStrategies: Array<Strategy> = [
|
|||||||
new GradualRolloutRandomStrategy(),
|
new GradualRolloutRandomStrategy(),
|
||||||
new GradualRolloutUserIdStrategy(),
|
new GradualRolloutUserIdStrategy(),
|
||||||
new GradualRolloutSessionIdStrategy(),
|
new GradualRolloutSessionIdStrategy(),
|
||||||
new UserWithIdStrategy(),
|
|
||||||
new RemoteAddressStrategy(),
|
new RemoteAddressStrategy(),
|
||||||
new FlexibleRolloutStrategy(),
|
new FlexibleRolloutStrategy(),
|
||||||
new UnknownStrategy(),
|
new UnknownStrategy(),
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import { Strategy } from './strategy.js';
|
|
||||||
import type { Context } from '../context.js';
|
|
||||||
|
|
||||||
export default class UserWithIdStrategy extends Strategy {
|
|
||||||
constructor() {
|
|
||||||
super('userWithId');
|
|
||||||
}
|
|
||||||
|
|
||||||
isEnabled(parameters: { userIds?: string }, context: Context): boolean {
|
|
||||||
const userIdList = parameters.userIds
|
|
||||||
? parameters.userIds.split(/\s*,\s*/)
|
|
||||||
: [];
|
|
||||||
return (
|
|
||||||
context.userId !== undefined && userIdList.includes(context.userId)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -432,13 +432,6 @@ describe('offline client', () => {
|
|||||||
stickiness: 'userId',
|
stickiness: 'userId',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'userWithId',
|
|
||||||
constraints: [],
|
|
||||||
parameters: {
|
|
||||||
userIds: 'uoea,ueoa',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'remoteAddress',
|
name: 'remoteAddress',
|
||||||
constraints: [],
|
constraints: [],
|
||||||
|
@ -48,7 +48,6 @@ test('clientApplicationSchema go-sdk request', () => {
|
|||||||
"gradualRolloutSessionId",
|
"gradualRolloutSessionId",
|
||||||
"gradualRolloutUserId",
|
"gradualRolloutUserId",
|
||||||
"remoteAddress",
|
"remoteAddress",
|
||||||
"userWithId",
|
|
||||||
"flexibleRollout"
|
"flexibleRollout"
|
||||||
],
|
],
|
||||||
"started": "2022-06-24T09:59:12.822607943+02:00",
|
"started": "2022-06-24T09:59:12.822607943+02:00",
|
||||||
@ -75,7 +74,6 @@ test('clientApplicationSchema node-sdk request', () => {
|
|||||||
"gradualRolloutRandom",
|
"gradualRolloutRandom",
|
||||||
"gradualRolloutUserId",
|
"gradualRolloutUserId",
|
||||||
"gradualRolloutSessionId",
|
"gradualRolloutSessionId",
|
||||||
"userWithId",
|
|
||||||
"remoteAddress",
|
"remoteAddress",
|
||||||
"flexibleRollout"
|
"flexibleRollout"
|
||||||
],
|
],
|
||||||
|
@ -95,14 +95,6 @@ const playgroundStrategies = (): Arbitrary<PlaygroundStrategySchema[]> =>
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
||||||
playgroundStrategy(
|
|
||||||
'userWithId',
|
|
||||||
fc.record({
|
|
||||||
userIds: fc
|
|
||||||
.uniqueArray(fc.emailAddress())
|
|
||||||
.map((ids) => ids.join(',')),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
playgroundStrategy(
|
playgroundStrategy(
|
||||||
'remoteAddress',
|
'remoteAddress',
|
||||||
fc.record({
|
fc.record({
|
||||||
|
@ -6,7 +6,6 @@ exports.up = function (db, cb) {
|
|||||||
ALTER TABLE strategies ADD COLUMN sort_order integer DEFAULT 9999;
|
ALTER TABLE strategies ADD COLUMN sort_order integer DEFAULT 9999;
|
||||||
UPDATE strategies SET sort_order = 0 WHERE name = 'default';
|
UPDATE strategies SET sort_order = 0 WHERE name = 'default';
|
||||||
UPDATE strategies SET sort_order = 1 WHERE name = 'flexibleRollout';
|
UPDATE strategies SET sort_order = 1 WHERE name = 'flexibleRollout';
|
||||||
UPDATE strategies SET sort_order = 2 WHERE name = 'userWithId';
|
|
||||||
UPDATE strategies SET sort_order = 3 WHERE name = 'remoteAddress';
|
UPDATE strategies SET sort_order = 3 WHERE name = 'remoteAddress';
|
||||||
UPDATE strategies SET sort_order = 4 WHERE name = 'applicationHostname';
|
UPDATE strategies SET sort_order = 4 WHERE name = 'applicationHostname';
|
||||||
`,
|
`,
|
||||||
|
@ -6,7 +6,6 @@ exports.up = function (db, cb) {
|
|||||||
ALTER TABLE strategies ADD COLUMN display_name text;
|
ALTER TABLE strategies ADD COLUMN display_name text;
|
||||||
UPDATE strategies SET display_name = 'Standard', description = 'The standard strategy is strictly on / off for your entire userbase.' WHERE name = 'default';
|
UPDATE strategies SET display_name = 'Standard', description = 'The standard strategy is strictly on / off for your entire userbase.' WHERE name = 'default';
|
||||||
UPDATE strategies SET display_name = 'Gradual rollout', description = 'Roll out to a percentage of your userbase, and ensure that the experience is the same for the user on each visit.' WHERE name = 'flexibleRollout';
|
UPDATE strategies SET display_name = 'Gradual rollout', description = 'Roll out to a percentage of your userbase, and ensure that the experience is the same for the user on each visit.' WHERE name = 'flexibleRollout';
|
||||||
UPDATE strategies SET display_name = 'UserIDs', description = 'Enable the feature for a specific set of userIds.' WHERE name = 'userWithId';
|
|
||||||
UPDATE strategies SET display_name = 'IPs', description = 'Enable the feature for a specific set of IP addresses.' WHERE name = 'remoteAddress';
|
UPDATE strategies SET display_name = 'IPs', description = 'Enable the feature for a specific set of IP addresses.' WHERE name = 'remoteAddress';
|
||||||
UPDATE strategies SET display_name = 'Hosts', description = 'Enable the feature for a specific set of hostnames.' WHERE name = 'applicationHostname';
|
UPDATE strategies SET display_name = 'Hosts', description = 'Enable the feature for a specific set of hostnames.' WHERE name = 'applicationHostname';
|
||||||
`,
|
`,
|
||||||
|
@ -9,13 +9,9 @@ exports.up = function (db, callback) {
|
|||||||
and deprecated
|
and deprecated
|
||||||
and not exists (select * from feature_strategies where strategy_name = name limit 1);
|
and not exists (select * from feature_strategies where strategy_name = name limit 1);
|
||||||
|
|
||||||
-- deprecate strategies on v5
|
|
||||||
update strategies set deprecated = true where name in ('userWithId');
|
|
||||||
|
|
||||||
-- update strategy descriptions and sort order
|
-- update strategy descriptions and sort order
|
||||||
update strategies set sort_order = 1, description = 'This strategy turns on / off for your entire userbase. Prefer using "Gradual rollout" strategy (100%=on, 0%=off).' WHERE name = 'default';
|
update strategies set sort_order = 1, description = 'This strategy turns on / off for your entire userbase. Prefer using "Gradual rollout" strategy (100%=on, 0%=off).' WHERE name = 'default';
|
||||||
update strategies set sort_order = 0 WHERE name = 'flexibleRollout';
|
update strategies set sort_order = 0 WHERE name = 'flexibleRollout';
|
||||||
update strategies set description = 'Enable the feature for a specific set of userIds. Prefer using "Gradual rollout" strategy with user id constraints.' WHERE name = 'userWithId';
|
|
||||||
`,
|
`,
|
||||||
callback,
|
callback,
|
||||||
);
|
);
|
||||||
|
@ -4,18 +4,6 @@
|
|||||||
"description": "Default on/off strategy.",
|
"description": "Default on/off strategy.",
|
||||||
"parameters": []
|
"parameters": []
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "userWithId",
|
|
||||||
"description": "Active for users with a userId defined in the userIds-list",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "userIds",
|
|
||||||
"type": "list",
|
|
||||||
"description": "",
|
|
||||||
"required": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "applicationHostname",
|
"name": "applicationHostname",
|
||||||
"description": "Active for client instances with a hostName in the hostNames-list.",
|
"description": "Active for client instances with a hostName in the hostNames-list.",
|
||||||
|
@ -107,14 +107,6 @@ export const strategies = (): Arbitrary<FeatureStrategySchema[]> =>
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
||||||
strategy(
|
|
||||||
'userWithId',
|
|
||||||
fc.record({
|
|
||||||
userIds: fc
|
|
||||||
.uniqueArray(fc.emailAddress())
|
|
||||||
.map((ids) => ids.join(',')),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
strategy(
|
strategy(
|
||||||
'remoteAddress',
|
'remoteAddress',
|
||||||
fc.record({
|
fc.record({
|
||||||
|
@ -152,26 +152,26 @@ describe.each([
|
|||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
if (etagVariant.feature_enabled) {
|
if (etagVariant.feature_enabled) {
|
||||||
expect(res.headers.etag).toBe(`"61824cd0:17:${etagVariant.name}"`);
|
expect(res.headers.etag).toBe(`"61824cd0:16:${etagVariant.name}"`);
|
||||||
expect(res.body.meta.etag).toBe(
|
expect(res.body.meta.etag).toBe(
|
||||||
`"61824cd0:17:${etagVariant.name}"`,
|
`"61824cd0:16:${etagVariant.name}"`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
expect(res.headers.etag).toBe('"61824cd0:17"');
|
expect(res.headers.etag).toBe('"61824cd0:16"');
|
||||||
expect(res.body.meta.etag).toBe('"61824cd0:17"');
|
expect(res.body.meta.etag).toBe('"61824cd0:16"');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test(`returns ${etagVariant.feature_enabled ? 200 : 304} for pre-calculated hash${etagVariant.feature_enabled ? ' because hash changed' : ''}`, async () => {
|
test(`returns ${etagVariant.feature_enabled ? 200 : 304} for pre-calculated hash${etagVariant.feature_enabled ? ' because hash changed' : ''}`, async () => {
|
||||||
const res = await app.request
|
const res = await app.request
|
||||||
.get('/api/client/features')
|
.get('/api/client/features')
|
||||||
.set('if-none-match', '"61824cd0:17"')
|
.set('if-none-match', '"61824cd0:16"')
|
||||||
.expect(etagVariant.feature_enabled ? 200 : 304);
|
.expect(etagVariant.feature_enabled ? 200 : 304);
|
||||||
|
|
||||||
if (etagVariant.feature_enabled) {
|
if (etagVariant.feature_enabled) {
|
||||||
expect(res.headers.etag).toBe(`"61824cd0:17:${etagVariant.name}"`);
|
expect(res.headers.etag).toBe(`"61824cd0:16:${etagVariant.name}"`);
|
||||||
expect(res.body.meta.etag).toBe(
|
expect(res.body.meta.etag).toBe(
|
||||||
`"61824cd0:17:${etagVariant.name}"`,
|
`"61824cd0:16:${etagVariant.name}"`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -193,13 +193,13 @@ describe.each([
|
|||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
if (etagVariant.feature_enabled) {
|
if (etagVariant.feature_enabled) {
|
||||||
expect(res.headers.etag).toBe(`"61824cd0:17:${etagVariant.name}"`);
|
expect(res.headers.etag).toBe(`"61824cd0:16:${etagVariant.name}"`);
|
||||||
expect(res.body.meta.etag).toBe(
|
expect(res.body.meta.etag).toBe(
|
||||||
`"61824cd0:17:${etagVariant.name}"`,
|
`"61824cd0:16:${etagVariant.name}"`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
expect(res.headers.etag).toBe('"61824cd0:17"');
|
expect(res.headers.etag).toBe('"61824cd0:16"');
|
||||||
expect(res.body.meta.etag).toBe('"61824cd0:17"');
|
expect(res.body.meta.etag).toBe('"61824cd0:16"');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -180,7 +180,6 @@ This endpoint gives insight into details about application seen per feature flag
|
|||||||
"abTest",
|
"abTest",
|
||||||
"default",
|
"default",
|
||||||
"betaUser",
|
"betaUser",
|
||||||
"userWithId",
|
|
||||||
"byHostName",
|
"byHostName",
|
||||||
"gradualRolloutWithSessionId",
|
"gradualRolloutWithSessionId",
|
||||||
"gradualRollout",
|
"gradualRollout",
|
||||||
|
@ -21,18 +21,6 @@ Used to fetch all defined strategies and their defined parameters.
|
|||||||
"description": "Default on/off strategy.",
|
"description": "Default on/off strategy.",
|
||||||
"parameters": []
|
"parameters": []
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "userWithId",
|
|
||||||
"description": "Active for userId specified in the comma seperated 'userIds' parameter.",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "userIds",
|
|
||||||
"type": "list",
|
|
||||||
"description": "List of unique userIds the feature should be active for.",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "gradualRollout",
|
"name": "gradualRollout",
|
||||||
"description": "Gradual rollout to logged in users",
|
"description": "Gradual rollout to logged in users",
|
||||||
|
@ -8,6 +8,10 @@ Predefined strategy types are a legacy implementation. Please use the [default s
|
|||||||
|
|
||||||
## UserIDs
|
## UserIDs
|
||||||
|
|
||||||
|
:::warning
|
||||||
|
The `userWithId` strategy was removed in Unleash v7.0.0 for new installations. Instead use a gradual rollout strategy with a user ID stickiness and constraints.
|
||||||
|
:::
|
||||||
|
|
||||||
The `userWithId` strategy is active for users with a `userId` defined in the `userIds` list.
|
The `userWithId` strategy is active for users with a `userId` defined in the `userIds` list.
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
|
Loading…
Reference in New Issue
Block a user