1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-08-27 13:49:10 +02:00

chore: Patch 4.22.6 (#3603)

<!-- Thanks for creating a PR! To make it easier for reviewers and
everyone else to understand what your changes relate to, please add some
relevant content to the headings below. Feel free to ignore or delete
sections that you don't think are relevant. Thank you! ❤️ -->
Includes:
- Stickiness options from context fields. 
- Docs update about project mode and project stickiness
## About the changes
<!-- Describe the changes introduced. What are they and why are they
being introduced? Feel free to also add screenshots or steps to view the
changes if they're visual. -->

<!-- Does it close an issue? Multiple? -->
Closes #

<!-- (For internal contributors): Does it relate to an issue on public
roadmap? -->
<!--
Relates to [roadmap](https://github.com/orgs/Unleash/projects/10) item:
#
-->

### Important files
<!-- PRs can contain a lot of changes, but not all changes are equally
important. Where should a reviewer start looking to get an overview of
the changes? Are any files particularly important? -->


## Discussion points
<!-- Anything about the PR you'd like to discuss before it gets merged?
Got any questions or doubts? -->

---------

Signed-off-by: andreas-unleash <andreas@getunleash.ai>
This commit is contained in:
andreas-unleash 2023-04-25 13:23:48 +03:00 committed by GitHub
parent 3698f1d2ca
commit 03d3ee76d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 159 additions and 29 deletions

View File

@ -11,7 +11,6 @@ jobs:
- groups/groups.spec.ts
- projects/access.spec.ts
- projects/overview.spec.ts
- projects/settings.spec.ts
- projects/notifications.spec.ts
- segments/segments.spec.ts
- import/import.spec.ts

View File

@ -1,12 +1,11 @@
import Select from 'component/common/select';
import { SelectChangeEvent, useTheme } from '@mui/material';
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
const builtInStickinessOptions = [
{ key: 'default', label: 'default' },
{ key: 'userId', label: 'userId' },
{ key: 'sessionId', label: 'sessionId' },
{ key: 'random', label: 'random' },
];
type OptionType = { key: string; label: string };
const DEFAULT_RANDOM_OPTION = 'random';
const DEFAULT_STICKINESS_OPTION = 'default';
interface IStickinessSelectProps {
label: string;
@ -25,20 +24,27 @@ export const StickinessSelect = ({
const { context } = useUnleashContext();
const theme = useTheme();
const resolveStickinessOptions = () =>
builtInStickinessOptions.concat(
context
.filter(contextDefinition => contextDefinition.stickiness)
.filter(
contextDefinition =>
!builtInStickinessOptions.find(
builtInStickinessOption =>
builtInStickinessOption.key ===
contextDefinition.name
)
)
.map(c => ({ key: c.name, label: c.name }))
);
const resolveStickinessOptions = () => {
const options = context
.filter(field => field.stickiness)
.map(c => ({ key: c.name, label: c.name })) as OptionType[];
if (
!options.find(option => option.key === 'default') &&
!context.find(field => field.name === DEFAULT_STICKINESS_OPTION)
) {
options.push({ key: 'default', label: 'default' });
}
if (
!options.find(option => option.key === 'random') &&
!context.find(field => field.name === DEFAULT_RANDOM_OPTION)
) {
options.push({ key: 'random', label: 'random' });
}
return options;
};
const stickinessOptions = resolveStickinessOptions();
return (

View File

@ -227,6 +227,16 @@ class FeatureToggleService {
'You can not change the featureName for an activation strategy.',
);
}
if (
strategy.parameters &&
'stickiness' in strategy.parameters &&
strategy.parameters.stickiness === ''
) {
throw new InvalidOperationError(
'You can not have an empty string for stickiness.',
);
}
}
async validateProjectCanAccessSegments(
@ -411,6 +421,14 @@ class FeatureToggleService {
);
}
if (
strategyConfig.parameters &&
'stickiness' in strategyConfig.parameters &&
strategyConfig.parameters.stickiness === ''
) {
strategyConfig.parameters.stickiness = 'default';
}
try {
const newFeatureStrategy =
await this.featureStrategiesStore.createStrategyFeatureEnv({

View File

@ -0,0 +1,28 @@
'use strict';
exports.up = function (db, callback) {
db.runSql(
`
INSERT INTO context_fields(name, description, sort_order, stickiness) VALUES('sessionId', 'Allows you to constrain on sessionId', 4, true);
UPDATE context_fields
SET stickiness = true
WHERE name LIKE 'userId' AND stickiness is null;
`,
callback,
);
};
exports.down = function (db, callback) {
db.runSql(
`
DELETE FROM context_fields
WHERE name LIKE 'sessionId';
UPDATE context_fields
SET stickiness = null
WHERE name LIKE 'userId';
`,
callback,
);
};

View File

@ -17,8 +17,11 @@ let app: IUnleashTest;
let db: ITestDb;
const defaultStrategy = {
name: 'default',
parameters: {},
name: 'flexibleRollout',
parameters: {
rollout: '100',
stickiness: '',
},
constraints: [],
};
@ -844,3 +847,77 @@ test('Can add and remove tags at the same time', async () => {
expect(res.body.tags).toHaveLength(1);
});
});
test('Should return "default" for stickiness when creating a flexibleRollout strategy with "" for stickiness', async () => {
const username = 'toggle-feature';
const feature = {
name: 'test-featureA',
description: 'the #1 feature',
};
const projectId = 'default';
await app.services.featureToggleServiceV2.createFeatureToggle(
projectId,
feature,
username,
);
await app.services.featureToggleServiceV2.createStrategy(
defaultStrategy,
{ projectId, featureName: feature.name, environment: DEFAULT_ENV },
username,
);
await app.request
.get(
`/api/admin/projects/${projectId}/features/${feature.name}/environments/${DEFAULT_ENV}`,
)
.expect((res) => {
const toggle = res.body;
expect(toggle.strategies).toHaveLength(1);
expect(toggle.strategies[0].parameters.stickiness).toBe('default');
});
await app.request
.get(`/api/admin/features/${feature.name}`)
.expect((res) => {
const toggle = res.body;
expect(toggle.strategies).toHaveLength(1);
expect(toggle.strategies[0].parameters.stickiness).toBe('default');
});
});
test('Should throw error when updating a flexibleRollout strategy with "" for stickiness', async () => {
const username = 'toggle-feature';
const feature = {
name: 'test-featureB',
description: 'the #1 feature',
};
const projectId = 'default';
await app.services.featureToggleServiceV2.createFeatureToggle(
projectId,
feature,
username,
);
await app.services.featureToggleServiceV2.createStrategy(
defaultStrategy,
{ projectId, featureName: feature.name, environment: DEFAULT_ENV },
username,
);
const featureToggle =
await app.services.featureToggleServiceV2.getFeatureToggle(
feature.name,
);
await app.request
.patch(
`/api/admin/projects/${projectId}/features/${feature.name}/environments/${DEFAULT_ENV}/strategies/${featureToggle.environments[0].strategies[0].id}`,
)
.send(defaultStrategy)
.expect((res) => {
const result = res.body;
expect(res.status).toBe(400);
expect(result.error).toBe('Request validation failed');
});
});

View File

@ -43,13 +43,15 @@ The UI shows the currently available projects. To create a new project, use the
The configuration of a new Project is now available. the following input is available to create the new Project.
![A project creation form. The form fields are labeled "project ID", "name", and "description". The "Create" button is highlighted.](/img/projects_save_new_project.png)
![A project creation form. The "Create" button is highlighted.](/img/projects_save_new_project_v2.png)
| Item | Description |
| ------------ | ---------------------------------- |
| Project Id | Id for this Project |
| Project name | The name of the Project. |
| Description | A short description of the project |
| Item | Description |
|--------------------|---------------------------------------------------------------------------------------------|
| Project Id | Id for this Project |
| Project name | The name of the Project. |
| Description | A short description of the project |
| Mode | The project [collaboration mode](/reference/project-collaboration-mode.md) |
| Default Stickiness | The default stickiness for the project. This setting controls the default stickiness value for variants and for the gradual rollout strategy. |
## Deleting an existing project {#deleting-an-existing-project}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB