mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-18 00:19:49 +01:00
fix: backport lifecycle and playground fixes (#7396)
This commit is contained in:
parent
29e0167001
commit
406f906d7f
@ -6,5 +6,5 @@ export const comparisonModerator = (
|
|||||||
): DeepOmit<IFeatureToggle, keyof IFeatureToggle> => {
|
): DeepOmit<IFeatureToggle, keyof IFeatureToggle> => {
|
||||||
const tempData = { ...data };
|
const tempData = { ...data };
|
||||||
|
|
||||||
return deepOmit(tempData, 'lastSeenAt', 'yes', 'no');
|
return deepOmit(tempData, 'lastSeenAt', 'yes', 'no', 'lifecycle');
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
DELETE_FEATURE,
|
DELETE_FEATURE,
|
||||||
UPDATE_FEATURE,
|
UPDATE_FEATURE,
|
||||||
} from 'component/providers/AccessProvider/permissions';
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
|
import { Route, Routes } from 'react-router-dom';
|
||||||
|
|
||||||
const currentTime = '2024-04-25T08:05:00.000Z';
|
const currentTime = '2024-04-25T08:05:00.000Z';
|
||||||
const twoMinutesAgo = '2024-04-25T08:03:00.000Z';
|
const twoMinutesAgo = '2024-04-25T08:03:00.000Z';
|
||||||
@ -22,16 +23,24 @@ const renderOpenTooltip = (
|
|||||||
loading = false,
|
loading = false,
|
||||||
) => {
|
) => {
|
||||||
render(
|
render(
|
||||||
<FeatureLifecycleTooltip
|
<Routes>
|
||||||
stage={stage}
|
<Route
|
||||||
onArchive={onArchive}
|
path={'/projects/:projectId'}
|
||||||
onComplete={onComplete}
|
element={
|
||||||
onUncomplete={onUncomplete}
|
<FeatureLifecycleTooltip
|
||||||
loading={loading}
|
stage={stage}
|
||||||
>
|
onArchive={onArchive}
|
||||||
<span>child</span>
|
onComplete={onComplete}
|
||||||
</FeatureLifecycleTooltip>,
|
onUncomplete={onUncomplete}
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
<span>child</span>
|
||||||
|
</FeatureLifecycleTooltip>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Routes>,
|
||||||
{
|
{
|
||||||
|
route: '/projects/default',
|
||||||
permissions: [
|
permissions: [
|
||||||
{ permission: DELETE_FEATURE },
|
{ permission: DELETE_FEATURE },
|
||||||
{ permission: UPDATE_FEATURE },
|
{ permission: UPDATE_FEATURE },
|
||||||
|
@ -25,6 +25,7 @@ import { isSafeToArchive } from './isSafeToArchive';
|
|||||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||||
import { formatDateYMDHMS } from 'utils/formatDate';
|
import { formatDateYMDHMS } from 'utils/formatDate';
|
||||||
import { formatDistanceToNow, parseISO } from 'date-fns';
|
import { formatDistanceToNow, parseISO } from 'date-fns';
|
||||||
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
|
|
||||||
const TimeLabel = styled('span')(({ theme }) => ({
|
const TimeLabel = styled('span')(({ theme }) => ({
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
@ -264,6 +265,8 @@ const LiveStageDescription: FC<{
|
|||||||
onComplete: () => void;
|
onComplete: () => void;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}> = ({ children, onComplete, loading }) => {
|
}> = ({ children, onComplete, loading }) => {
|
||||||
|
const projectId = useRequiredPathParam('projectId');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BoldTitle>Is this feature complete?</BoldTitle>
|
<BoldTitle>Is this feature complete?</BoldTitle>
|
||||||
@ -282,6 +285,7 @@ const LiveStageDescription: FC<{
|
|||||||
size='small'
|
size='small'
|
||||||
onClick={onComplete}
|
onClick={onComplete}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
projectId={projectId}
|
||||||
>
|
>
|
||||||
Mark completed
|
Mark completed
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
@ -300,6 +304,8 @@ const SafeToArchive: FC<{
|
|||||||
onUncomplete: () => void;
|
onUncomplete: () => void;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}> = ({ onArchive, onUncomplete, loading }) => {
|
}> = ({ onArchive, onUncomplete, loading }) => {
|
||||||
|
const projectId = useRequiredPathParam('projectId');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BoldTitle>Safe to archive</BoldTitle>
|
<BoldTitle>Safe to archive</BoldTitle>
|
||||||
@ -316,6 +322,7 @@ const SafeToArchive: FC<{
|
|||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
gap: 2,
|
gap: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -326,6 +333,7 @@ const SafeToArchive: FC<{
|
|||||||
size='small'
|
size='small'
|
||||||
onClick={onUncomplete}
|
onClick={onUncomplete}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
projectId={projectId}
|
||||||
>
|
>
|
||||||
Revert to live
|
Revert to live
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
@ -336,6 +344,7 @@ const SafeToArchive: FC<{
|
|||||||
size='small'
|
size='small'
|
||||||
sx={{ mb: 2 }}
|
sx={{ mb: 2 }}
|
||||||
onClick={onArchive}
|
onClick={onArchive}
|
||||||
|
projectId={projectId}
|
||||||
>
|
>
|
||||||
Archive feature
|
Archive feature
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
|
24
package.json
24
package.json
@ -82,9 +82,7 @@
|
|||||||
"testTimeout": 10000,
|
"testTimeout": 10000,
|
||||||
"globalSetup": "./scripts/jest-setup.js",
|
"globalSetup": "./scripts/jest-setup.js",
|
||||||
"transform": {
|
"transform": {
|
||||||
"^.+\\.tsx?$": [
|
"^.+\\.tsx?$": ["@swc/jest"]
|
||||||
"@swc/jest"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
||||||
"testPathIgnorePatterns": [
|
"testPathIgnorePatterns": [
|
||||||
@ -93,13 +91,7 @@
|
|||||||
"/frontend/",
|
"/frontend/",
|
||||||
"/website/"
|
"/website/"
|
||||||
],
|
],
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json"],
|
||||||
"ts",
|
|
||||||
"tsx",
|
|
||||||
"js",
|
|
||||||
"jsx",
|
|
||||||
"json"
|
|
||||||
],
|
|
||||||
"coveragePathIgnorePatterns": [
|
"coveragePathIgnorePatterns": [
|
||||||
"/node_modules/",
|
"/node_modules/",
|
||||||
"/dist/",
|
"/dist/",
|
||||||
@ -240,14 +232,8 @@
|
|||||||
"tough-cookie": "4.1.4"
|
"tough-cookie": "4.1.4"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,ts}": [
|
"*.{js,ts}": ["biome check --write --no-errors-on-unmatched"],
|
||||||
"biome check --write --no-errors-on-unmatched"
|
"*.{jsx,tsx}": ["biome check --write --no-errors-on-unmatched"],
|
||||||
],
|
"*.json": ["biome format --write --no-errors-on-unmatched"]
|
||||||
"*.{jsx,tsx}": [
|
|
||||||
"biome check --write --no-errors-on-unmatched"
|
|
||||||
],
|
|
||||||
"*.json": [
|
|
||||||
"biome format --write --no-errors-on-unmatched"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ test('advanced playground evaluation with no toggles', async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('advanced playground evaluation with parent dependency', async () => {
|
test('advanced playground evaluation with unsatisfied parent dependency', async () => {
|
||||||
await createFeatureToggle('test-parent');
|
await createFeatureToggle('test-parent');
|
||||||
await createFeatureToggle('test-child');
|
await createFeatureToggle('test-child');
|
||||||
await enableToggle('test-child');
|
await enableToggle('test-child');
|
||||||
@ -126,6 +126,35 @@ test('advanced playground evaluation with parent dependency', async () => {
|
|||||||
expect(parent.isEnabled).toBe(false);
|
expect(parent.isEnabled).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('advanced playground evaluation with satisfied disabled parent dependency', async () => {
|
||||||
|
await createFeatureToggle('test-parent');
|
||||||
|
await createFeatureToggle('test-child');
|
||||||
|
await enableToggle('test-child');
|
||||||
|
await app.addDependency('test-child', {
|
||||||
|
feature: 'test-parent',
|
||||||
|
enabled: false,
|
||||||
|
variants: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { body: result } = await app.request
|
||||||
|
.post('/api/admin/playground/advanced')
|
||||||
|
.send({
|
||||||
|
environments: ['default'],
|
||||||
|
projects: ['default'],
|
||||||
|
context: { appName: 'test' },
|
||||||
|
})
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const child = result.features[0].environments.default[0];
|
||||||
|
const parent = result.features[1].environments.default[0];
|
||||||
|
|
||||||
|
expect(child.hasUnsatisfiedDependency).toBe(false);
|
||||||
|
expect(child.isEnabled).toBe(true);
|
||||||
|
expect(parent.hasUnsatisfiedDependency).toBe(false);
|
||||||
|
expect(parent.isEnabled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
test('advanced playground evaluation happy path', async () => {
|
test('advanced playground evaluation happy path', async () => {
|
||||||
await createFeatureToggleWithStrategy('test-playground-feature');
|
await createFeatureToggleWithStrategy('test-playground-feature');
|
||||||
await enableToggle('test-playground-feature');
|
await enableToggle('test-playground-feature');
|
||||||
|
@ -77,9 +77,6 @@ export default class UnleashClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parent.enabled !== false) {
|
if (parent.enabled !== false) {
|
||||||
if (!parentToggle.enabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (parent.variants?.length) {
|
if (parent.variants?.length) {
|
||||||
return parent.variants.includes(
|
return parent.variants.includes(
|
||||||
this.getVariant(parent.feature, context).name,
|
this.getVariant(parent.feature, context).name,
|
||||||
@ -91,12 +88,9 @@ export default class UnleashClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return !(
|
||||||
!parentToggle.enabled &&
|
this.isEnabled(parent.feature, context, () => false).result ===
|
||||||
!(
|
true
|
||||||
this.isEnabled(parent.feature, context, () => false)
|
|
||||||
.result === true
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import type { Db } from '../../../lib/db/db';
|
|||||||
import type { IContextFieldDto } from '../../../lib/types/stores/context-field-store';
|
import type { IContextFieldDto } from '../../../lib/types/stores/context-field-store';
|
||||||
import { DEFAULT_ENV } from '../../../lib/util';
|
import { DEFAULT_ENV } from '../../../lib/util';
|
||||||
import type {
|
import type {
|
||||||
|
CreateDependentFeatureSchema,
|
||||||
CreateFeatureSchema,
|
CreateFeatureSchema,
|
||||||
CreateFeatureStrategySchema,
|
CreateFeatureStrategySchema,
|
||||||
ImportTogglesSchema,
|
ImportTogglesSchema,
|
||||||
@ -102,7 +103,10 @@ export interface IUnleashHttpAPI {
|
|||||||
expectedResponseCode?: number,
|
expectedResponseCode?: number,
|
||||||
): supertest.Test;
|
): supertest.Test;
|
||||||
|
|
||||||
addDependency(child: string, parent: string): supertest.Test;
|
addDependency(
|
||||||
|
child: string,
|
||||||
|
parent: string | CreateDependentFeatureSchema,
|
||||||
|
): supertest.Test;
|
||||||
|
|
||||||
addTag(
|
addTag(
|
||||||
feature: string,
|
feature: string,
|
||||||
@ -224,7 +228,7 @@ function httpApis(
|
|||||||
|
|
||||||
addDependency(
|
addDependency(
|
||||||
child: string,
|
child: string,
|
||||||
parent: string,
|
parent: string | CreateDependentFeatureSchema,
|
||||||
project = DEFAULT_PROJECT,
|
project = DEFAULT_PROJECT,
|
||||||
expectedResponseCode: number = 200,
|
expectedResponseCode: number = 200,
|
||||||
): supertest.Test {
|
): supertest.Test {
|
||||||
@ -232,7 +236,7 @@ function httpApis(
|
|||||||
.post(
|
.post(
|
||||||
`/api/admin/projects/${project}/features/${child}/dependencies`,
|
`/api/admin/projects/${project}/features/${child}/dependencies`,
|
||||||
)
|
)
|
||||||
.send({ feature: parent })
|
.send(typeof parent === 'string' ? { feature: parent } : parent)
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.expect(expectedResponseCode);
|
.expect(expectedResponseCode);
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user