From 7233143ace148db0e79bc34afa2d6dd61152d55f Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 20 Sep 2023 16:14:36 +0000
Subject: [PATCH 01/14] chore(deps): update dependency jest to v29.7.0 (#4799)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [jest](https://jestjs.io/)
([source](https://togithub.com/jestjs/jest)) | [`29.6.4` ->
`29.7.0`](https://renovatebot.com/diffs/npm/jest/29.6.4/29.7.0) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/jest/29.7.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/jest/29.7.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/jest/29.6.4/29.7.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/jest/29.6.4/29.7.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
---
### Release Notes
jestjs/jest (jest)
###
[`v29.7.0`](https://togithub.com/jestjs/jest/blob/HEAD/CHANGELOG.md#2970)
[Compare
Source](https://togithub.com/jestjs/jest/compare/v29.6.4...v29.7.0)
##### Features
- `[create-jest]` Add `npm init` / `yarn create` initialiser for Jest
projects ([#14465](https://togithub.com/jestjs/jest/pull/14453))
- `[jest-validate]` Allow deprecation warnings for unknown options
([#14499](https://togithub.com/jestjs/jest/pull/14499))
##### Fixes
- `[jest-resolver]` Replace unmatched capture groups in
`moduleNameMapper` with empty string instead of `undefined`
([#14507](https://togithub.com/jestjs/jest/pull/14507))
- `[jest-snapshot]` Allow for strings as well as template literals in
inline snapshots
([#14465](https://togithub.com/jestjs/jest/pull/14465))
- `[@jest/test-sequencer]` Calculate test runtime if `perStats.duration`
is missing
([#14473](https://togithub.com/jestjs/jest/pull/14473))
##### Performance
- `[@jest/create-cache-key-function]` Cache access of `NODE_ENV` and
`BABEL_ENV`
([#14455](https://togithub.com/jestjs/jest/pull/14455))
##### Chore & Maintenance
- `[jest-cli]` Move internal config initialisation logic to the
`create-jest` package
([#14465](https://togithub.com/jestjs/jest/pull/14453))
---
### Configuration
đ
**Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).
đŚ **Automerge**: Enabled.
âť **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
đ **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] If you want to rebase/retry this PR, check
this box
---
This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/Unleash/unleash).
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
package.json | 2 +-
yarn.lock | 16 ++++++++--------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/package.json b/package.json
index 693b7e428028..38848e3fc186 100644
--- a/package.json
+++ b/package.json
@@ -208,7 +208,7 @@
"fast-check": "3.13.0",
"fetch-mock": "9.11.0",
"husky": "8.0.3",
- "jest": "29.6.4",
+ "jest": "29.7.0",
"jest-junit": "^16.0.0",
"lint-staged": "13.2.3",
"nock": "13.3.3",
diff --git a/yarn.lock b/yarn.lock
index d6e3ec40ab95..7e2f50ea8f9a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -761,7 +761,7 @@
jest-util "^29.7.0"
slash "^3.0.0"
-"@jest/core@^29.6.4", "@jest/core@^29.7.0":
+"@jest/core@^29.7.0":
version "29.7.0"
resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f"
integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==
@@ -4761,7 +4761,7 @@ jest-circus@^29.7.0:
slash "^3.0.0"
stack-utils "^2.0.3"
-jest-cli@^29.6.4:
+jest-cli@^29.7.0:
version "29.7.0"
resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995"
integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==
@@ -5136,15 +5136,15 @@ jest-worker@^29.7.0:
merge-stream "^2.0.0"
supports-color "^8.0.0"
-jest@29.6.4:
- version "29.6.4"
- resolved "https://registry.yarnpkg.com/jest/-/jest-29.6.4.tgz#7c48e67a445ba264b778253b5d78d4ebc9d0a622"
- integrity sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==
+jest@29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613"
+ integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==
dependencies:
- "@jest/core" "^29.6.4"
+ "@jest/core" "^29.7.0"
"@jest/types" "^29.6.3"
import-local "^3.0.2"
- jest-cli "^29.6.4"
+ jest-cli "^29.7.0"
joi@^17.3.0, joi@^17.7.0:
version "17.10.1"
From 5e6ed0baac9df0922372ccb153721b5c15632b37 Mon Sep 17 00:00:00 2001
From: Jaanus Sellin
Date: Thu, 21 Sep 2023 11:22:29 +0300
Subject: [PATCH 02/14] feat: private projects handle in playground (#4791)
---
.../playground/advanced-playground.test.ts | 1 +
.../features/playground/playground-service.ts | 40 +++++++++++++++++--
src/lib/features/playground/playground.ts | 5 ++-
.../client-metrics/instance-service.ts | 5 ++-
src/lib/services/index.ts | 1 +
.../e2e/services/playground-service.test.ts | 1 +
6 files changed, 46 insertions(+), 7 deletions(-)
diff --git a/src/lib/features/playground/advanced-playground.test.ts b/src/lib/features/playground/advanced-playground.test.ts
index bf53d177f9fb..c2e6c8c52fcd 100644
--- a/src/lib/features/playground/advanced-playground.test.ts
+++ b/src/lib/features/playground/advanced-playground.test.ts
@@ -19,6 +19,7 @@ beforeAll(async () => {
advancedPlayground: true,
strictSchemaValidation: true,
strategyVariant: true,
+ privateProjects: true,
},
},
},
diff --git a/src/lib/features/playground/playground-service.ts b/src/lib/features/playground/playground-service.ts
index ef4495c1c025..4ffcf8d81377 100644
--- a/src/lib/features/playground/playground-service.ts
+++ b/src/lib/features/playground/playground-service.ts
@@ -4,7 +4,7 @@ import { IUnleashServices } from 'lib/types/services';
import { ALL } from '../../types/models/api-token';
import { PlaygroundFeatureSchema } from 'lib/openapi/spec/playground-feature-schema';
import { Logger } from '../../logger';
-import { ISegment, IUnleashConfig } from 'lib/types';
+import { IFlagResolver, ISegment, IUnleashConfig } from 'lib/types';
import { offlineUnleashClient } from './offline-unleash-client';
import { FeatureInterface } from 'lib/features/playground/feature-evaluator/feature';
import {
@@ -16,10 +16,11 @@ import { FeatureConfigurationClient } from '../../types/stores/feature-strategie
import { generateObjectCombinations } from './generateObjectCombinations';
import groupBy from 'lodash.groupby';
import { omitKeys } from '../../util';
-import { AdvancedPlaygroundFeatureSchema } from '../../openapi/spec/advanced-playground-feature-schema';
+import { AdvancedPlaygroundFeatureSchema } from '../../openapi';
import { AdvancedPlaygroundEnvironmentFeatureSchema } from '../../openapi/spec/advanced-playground-environment-feature-schema';
import { validateQueryComplexity } from './validateQueryComplexity';
import { playgroundStrategyEvaluation } from 'lib/openapi';
+import { IPrivateProjectChecker } from '../private-project/privateProjectCheckerType';
type EvaluationInput = {
features: FeatureConfigurationClient[];
@@ -66,16 +67,28 @@ export class PlaygroundService {
private readonly segmentService: ISegmentService;
+ private flagResolver: IFlagResolver;
+
+ private privateProjectChecker: IPrivateProjectChecker;
+
constructor(
config: IUnleashConfig,
{
featureToggleServiceV2,
segmentService,
- }: Pick,
+ privateProjectChecker,
+ }: Pick<
+ IUnleashServices,
+ | 'featureToggleServiceV2'
+ | 'segmentService'
+ | 'privateProjectChecker'
+ >,
) {
this.logger = config.getLogger('services/playground-service.ts');
+ this.flagResolver = config.flagResolver;
this.featureToggleService = featureToggleServiceV2;
this.segmentService = segmentService;
+ this.privateProjectChecker = privateProjectChecker;
}
async evaluateAdvancedQuery(
@@ -83,10 +96,29 @@ export class PlaygroundService {
environments: string[],
context: SdkContextSchema,
limit: number,
+ userId: number,
): Promise {
const segments = await this.segmentService.getActive();
+
+ let filteredProjects: typeof projects;
+ if (this.flagResolver.isEnabled('privateProjects')) {
+ const accessibleProjects =
+ await this.privateProjectChecker.getUserAccessibleProjects(
+ userId,
+ );
+ filteredProjects =
+ projects === ALL
+ ? accessibleProjects
+ : projects.filter((project) =>
+ accessibleProjects.includes(project),
+ );
+ console.log(accessibleProjects);
+ }
+
const environmentFeatures = await Promise.all(
- environments.map((env) => this.resolveFeatures(projects, env)),
+ environments.map((env) =>
+ this.resolveFeatures(filteredProjects, env),
+ ),
);
const contexts = generateObjectCombinations(context);
diff --git a/src/lib/features/playground/playground.ts b/src/lib/features/playground/playground.ts
index ef165a989012..5822f4ea437a 100644
--- a/src/lib/features/playground/playground.ts
+++ b/src/lib/features/playground/playground.ts
@@ -20,6 +20,7 @@ import {
advancedPlaygroundViewModel,
playgroundViewModel,
} from './playground-view-model';
+import { IAuthRequest } from '../../routes/unleash-types';
export default class PlaygroundController extends Controller {
private openApiService: OpenApiService;
@@ -112,9 +113,10 @@ export default class PlaygroundController extends Controller {
}
async evaluateAdvancedContext(
- req: Request,
+ req: IAuthRequest,
res: Response,
): Promise {
+ const { user } = req;
// used for runtime control, do not remove
const { payload } = this.flagResolver.getVariant('advancedPlayground');
const limit =
@@ -127,6 +129,7 @@ export default class PlaygroundController extends Controller {
req.body.environments,
req.body.context,
limit,
+ user.id,
);
const response: AdvancedPlaygroundResponseSchema =
diff --git a/src/lib/services/client-metrics/instance-service.ts b/src/lib/services/client-metrics/instance-service.ts
index 8cb503d5a778..19cca5a26b9d 100644
--- a/src/lib/services/client-metrics/instance-service.ts
+++ b/src/lib/services/client-metrics/instance-service.ts
@@ -20,6 +20,7 @@ import { clientMetricsSchema } from './schema';
import { PartialSome } from '../../types/partial';
import { IPrivateProjectChecker } from '../../features/private-project/privateProjectCheckerType';
import { IFlagResolver } from '../../types';
+import { ALL_PROJECTS } from '../../util';
export default class ClientInstanceService {
apps = {};
@@ -178,7 +179,7 @@ export default class ClientInstanceService {
): Promise {
const applications =
await this.clientApplicationsStore.getAppsForStrategy(query);
- if (this.flagResolver.isEnabled('privateProjects') && userId) {
+ if (this.flagResolver.isEnabled('privateProjects')) {
const accessibleProjects =
await this.privateProjectChecker.getUserAccessibleProjects(
userId,
@@ -188,7 +189,7 @@ export default class ClientInstanceService {
...application,
usage: application.usage?.filter(
(usageItem) =>
- usageItem.project === '*' ||
+ usageItem.project === ALL_PROJECTS ||
accessibleProjects.includes(usageItem.project),
),
};
diff --git a/src/lib/services/index.ts b/src/lib/services/index.ts
index 433f008bb9e5..a3db29e6cfc9 100644
--- a/src/lib/services/index.ts
+++ b/src/lib/services/index.ts
@@ -251,6 +251,7 @@ export const createServices = (
const playgroundService = new PlaygroundService(config, {
featureToggleServiceV2,
segmentService,
+ privateProjectChecker,
});
const configurationRevisionService = new ConfigurationRevisionService(
diff --git a/src/test/e2e/services/playground-service.test.ts b/src/test/e2e/services/playground-service.test.ts
index 058e713b6f22..89b1dc07589a 100644
--- a/src/test/e2e/services/playground-service.test.ts
+++ b/src/test/e2e/services/playground-service.test.ts
@@ -63,6 +63,7 @@ beforeAll(async () => {
service = new PlaygroundService(config, {
featureToggleServiceV2: featureToggleService,
segmentService,
+ privateProjectChecker,
});
});
From b1234fb89c545f6d7b71707ca66a613efa4a49ca Mon Sep 17 00:00:00 2001
From: Simon Hornby
Date: Thu, 21 Sep 2023 11:12:20 +0200
Subject: [PATCH 03/14] fix: force permissions export to only be enterprise in
ui (#4760)
---
frontend/src/component/admin/users/UsersList/UsersList.tsx | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/frontend/src/component/admin/users/UsersList/UsersList.tsx b/frontend/src/component/admin/users/UsersList/UsersList.tsx
index 027b107e81c7..74bb5ad1eb1f 100644
--- a/frontend/src/component/admin/users/UsersList/UsersList.tsx
+++ b/frontend/src/component/admin/users/UsersList/UsersList.tsx
@@ -34,6 +34,7 @@ import { RoleCell } from 'component/common/Table/cells/RoleCell/RoleCell';
import { useSearch } from 'hooks/useSearch';
import { Download } from '@mui/icons-material';
import { useUiFlag } from 'hooks/useUiFlag';
+import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
const UsersList = () => {
const navigate = useNavigate();
@@ -44,6 +45,7 @@ const UsersList = () => {
const [pwDialog, setPwDialog] = useState<{ open: boolean; user?: IUser }>({
open: false,
});
+ const { isEnterprise } = useUiConfig();
const [delDialog, setDelDialog] = useState(false);
const [showConfirm, setShowConfirm] = useState(false);
const [emailSent, setEmailSent] = useState(false);
@@ -271,7 +273,10 @@ const UsersList = () => {
(
<>
Date: Thu, 21 Sep 2023 11:46:39 +0200
Subject: [PATCH 04/14] docs: add info on how to troubleshoot and better errors
(#4803)
This PR adds some troubleshooting information to the website readme and
also makes it so that we get more readable errors in the build logs when
something goes wrong.
---
.github/workflows/build_doc_prs.yaml | 7 +++-
website/README.md | 58 ++++++++++++++++++++++++++++
2 files changed, 64 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/build_doc_prs.yaml b/.github/workflows/build_doc_prs.yaml
index 46971c60c676..122eedc5e68c 100644
--- a/.github/workflows/build_doc_prs.yaml
+++ b/.github/workflows/build_doc_prs.yaml
@@ -17,4 +17,9 @@ jobs:
UNLEASH_PROXY_URL: ${{ secrets.UNLEASH_PROXY_URL_DEVELOPMENT }}
run: |
# Build the site
- cd website && yarn && yarn build
+ cd website && yarn
+ # give better error messages when the build fails (refer to website/readme.md#troubleshooting)
+ echo "Removing references to chalk in node_modules/@docusaurus/core/lib/client/serverEntry.js"
+ sed -i 's/chalk\(\w\|\.\)\+//g' node_modules/@docusaurus/core/lib/client/serverEntry.js
+ echo "Chalk removed"
+ yarn build
diff --git a/website/README.md b/website/README.md
index 7eee45919dbc..7d8ca67f7593 100644
--- a/website/README.md
+++ b/website/README.md
@@ -39,3 +39,61 @@ GIT_USER= USE_SSH=true yarn deploy
```
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
+
+## Troubleshooting
+
+### `TypeError: source_default(...).bold is not a function`
+
+If you get an error like this, it's probably due to a formatting issue within one of the markdown files. It could be
+
+- unescaped angle brackets (markdown will try to parse `` (when it's not quoted) as HTML, which breaks the build)
+- incorrectly formatted titles or missing pieces of files
+- a lot of other stuff.
+
+```console
+Component Figure was not imported, exported, or provided by MDXProvider as global scope
+
+TypeError: source_default(...).bold is not a function
+[ERROR] Unable to build website for locale en.
+```
+
+This error is very hard to debug, but there is a trick that appears to work (as shared in [this discussion on docusaurus' repo](https://github.com/facebook/docusaurus/issues/7686#issuecomment-1486771382)):
+
+In `node_modules/@docusaurus/core/lib/client/serverEntry.js`, remove all references to `chalk`. You can use a regex replace for that, by replacing `chalk(\w|\.)+` with the empty string.
+
+Depending on your editor, that regex might need more escapes. For instance, here's a command to run with `evil-ex` in Emacs:
+
+```
+%s/chalk\(\w\|\.\)+//g
+```
+
+For macOS `sed`, it'd be:
+
+```shell
+sed -i '' 's/chalk\(\w\|\.\)\+//g' node_modules/@docusaurus/core/lib/client/serverEntry.js
+```
+
+For GNU `sed`:
+
+```shell
+sed -i 's/chalk\(\w\|\.\)\+//g' node_modules/@docusaurus/core/lib/client/serverEntry.js
+```
+
+That might turn your error into something like this:
+
+```console
+[ERROR] Docusaurus server-side rendering could not render static page with path /reference/api/unleash/change-requests.
+[ERROR] Docusaurus server-side rendering could not render static page with path /reference/api/unleash/feature-types.
+[ERROR] Docusaurus server-side rendering could not render static page with path /reference/api/unleash/frontend-api.
+[ERROR] Docusaurus server-side rendering could not render static page with path /reference/api/unleash/maintenance.
+[ERROR] Docusaurus server-side rendering could not render static page with path /reference/api/unleash/notifications.
+[ERROR] Docusaurus server-side rendering could not render static page with path /reference/api/unleash/personal-access-tokens.
+[ERROR] Docusaurus server-side rendering could not render static page with path /reference/api/unleash/segments.
+[ERROR] Docusaurus server-side rendering could not render static page with path /reference/api/unleash/service-accounts.
+[ERROR] Docusaurus server-side rendering could not render static page with path /reference/api/unleash/telemetry.
+[ERROR] Docusaurus server-side rendering could not render static page with path /reference/api/unleash/unstable.
+Component Figure was not imported, exported, or provided by MDXProvider as global scope
+
+Error: Unexpected: cant find current sidebar in context
+[ERROR] Unable to build website for locale en.
+```
From 6884f9cdc9048fad87a7b42c82ccb6be87fcf7ab Mon Sep 17 00:00:00 2001
From: Fredrik Strand Oseberg
Date: Thu, 21 Sep 2023 14:28:45 +0200
Subject: [PATCH 05/14] feat: strategy variants on strategy overview (#4776)
Refactors the breakdown of feature variants per strategy on the
environment overview level:
---
.../TooltipResolver/TooltipResolver.tsx | 1 -
.../StrategyItem/StrategyItem.tsx | 4 +
.../SplitPreviewSlider/SplitPreviewSlider.tsx | 198 +++++++++++++++---
.../StrategyTypes/StrategyVariants.tsx | 4 +-
4 files changed, 170 insertions(+), 37 deletions(-)
diff --git a/frontend/src/component/common/TooltipResolver/TooltipResolver.tsx b/frontend/src/component/common/TooltipResolver/TooltipResolver.tsx
index 8aea828f5cef..d4860ac7ff2b 100644
--- a/frontend/src/component/common/TooltipResolver/TooltipResolver.tsx
+++ b/frontend/src/component/common/TooltipResolver/TooltipResolver.tsx
@@ -18,7 +18,6 @@ export const TooltipResolver = ({
if (!title && !titleComponent) {
return children;
}
-
if (variant === 'custom') {
return (
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx
index d71a619f6d2b..1cfef9da7415 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx
@@ -12,6 +12,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
import { CopyStrategyIconMenu } from './CopyStrategyIconMenu/CopyStrategyIconMenu';
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
import MenuStrategyRemove from './MenuStrategyRemove/MenuStrategyRemove';
+import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
interface IStrategyItemProps {
environmentId: string;
@@ -86,6 +87,9 @@ export const StrategyItem: FC = ({
}
>
+ {strategy.variants ? (
+
+ ) : null}
);
};
diff --git a/frontend/src/component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider.tsx b/frontend/src/component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider.tsx
index 6ddd8425a041..7d761579e88d 100644
--- a/frontend/src/component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider.tsx
+++ b/frontend/src/component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider.tsx
@@ -1,10 +1,9 @@
import { Box, Typography, styled } from '@mui/material';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
+import { IFeatureVariant } from 'interfaces/featureToggle';
-type SplitPreviewSliderProps = {
- values: number[];
-};
-
-const StyledContainer = styled(Box)(({ theme }) => ({
+const StyledContainer = styled(Box)(() => ({
display: 'flex',
width: '100%',
position: 'relative',
@@ -18,55 +17,188 @@ const StyledTrack = styled(Box)(({ theme }) => ({
overflow: 'hidden',
}));
-const StyledSegment = styled(Box)(({ theme }) => ({
+const StyledSegment = styled(Box)(() => ({
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
+ width: '100%',
}));
-const StyledSegmentTrack = styled(Box)(({ theme }) => ({
- height: theme.spacing(3),
+const StyledSegmentTrack = styled(Box, {
+ shouldForwardProp: prop => prop !== 'index',
+})<{ index: number }>(({ theme, index }) => ({
+ height: theme.spacing(1.8),
width: '100%',
position: 'relative',
+ background: theme.palette.variants[index % theme.palette.variants.length],
}));
-const SplitPreviewSlider = ({ values }: SplitPreviewSliderProps) => {
- if (values.length < 2) {
+const StyledHeaderContainer = styled(Box)(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ marginBottom: theme.spacing(1),
+}));
+
+const StyledTypography = styled(Typography)(({ theme }) => ({
+ marginY: theme.spacing(1),
+}));
+
+const StyledVariantBoxContainer = styled(Box)(() => ({
+ display: 'flex',
+ alignItems: 'center',
+ marginLeft: 'auto',
+}));
+
+const StyledVariantBox = styled(Box, {
+ shouldForwardProp: prop => prop !== 'index',
+})<{ index: number }>(({ theme, index }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ marginRight: theme.spacing(2),
+ '& div': {
+ width: theme.spacing(1.6),
+ height: theme.spacing(1.6),
+ borderRadius: '50%',
+ marginRight: theme.spacing(1),
+ background:
+ theme.palette.variants[index % theme.palette.variants.length],
+ },
+}));
+
+const StyledTypographySubtitle = styled(Typography)(({ theme }) => ({
+ marginTop: theme.spacing(1),
+}));
+
+interface ISplitPreviewSliderProps {
+ variants: IFeatureVariant[];
+}
+
+const SplitPreviewSlider = ({ variants }: ISplitPreviewSliderProps) => {
+ if (variants.length < 2) {
return null;
}
return (
({ marginTop: theme.spacing(2) })}>
- ({ marginY: theme.spacing(1) })}
- >
- Split preview
-
+
- {values.map((value, index) => (
-
- ({
- background:
- theme.palette.variants[
- index % theme.palette.variants.length
- ],
- })}
- />
- ({ marginTop: theme.spacing(1) })}
+
+ {variants.map((variant, index) => {
+ const value = variant.weight / 10;
+ return (
+ e.preventDefault()}
+ titleComponent={
+
+ }
>
- {value}%
-
-
- ))}
+
+ {' '}
+
+
+
+ {value}%
+
+
+
+
+ );
+ })}
);
};
+const SplitPreviewHeader = ({ variants }: ISplitPreviewSliderProps) => {
+ return (
+
+
+ Feature variants ({variants.length})
+
+
+ {variants.map((variant, index) => (
+
+
+
+ {variant.name}
+
+
+ ))}
+
+
+ );
+};
+
+interface ISplitPreviewTooltip {
+ variant: IFeatureVariant;
+ index: number;
+}
+
+const StyledTooltipContainer = styled(Box)(() => ({
+ display: 'flex',
+ flexDirection: 'column',
+}));
+
+const StyledVariantContainer = styled(Box)(() => ({
+ display: 'flex',
+ alignItems: 'center',
+ minWidth: '250px',
+}));
+
+const StyledPayloadContainer = styled(Box)(({ theme }) => ({
+ marginTop: theme.spacing(1),
+ display: 'flex',
+ flexDirection: 'column',
+}));
+
+const StyledPayloadLabel = styled(Typography)(({ theme }) => ({
+ marginBottom: theme.spacing(1),
+}));
+
+const SplitPreviewTooltip = ({ variant, index }: ISplitPreviewTooltip) => {
+ return (
+
+
+
+
+
+
+
+ {variant.weight / 10}% - {variant.name}
+
+
+
+ {variant.payload ? (
+
+
+ Payload
+
+
+ {variant.payload.value}}
+ elseShow={
+
+ {variant.payload.value}
+
+ }
+ />
+
+ ) : null}
+
+ );
+};
+
export default SplitPreviewSlider;
diff --git a/frontend/src/component/feature/StrategyTypes/StrategyVariants.tsx b/frontend/src/component/feature/StrategyTypes/StrategyVariants.tsx
index 7d1cb785a83b..30177b6d7977 100644
--- a/frontend/src/component/feature/StrategyTypes/StrategyVariants.tsx
+++ b/frontend/src/component/feature/StrategyTypes/StrategyVariants.tsx
@@ -157,9 +157,7 @@ export const StrategyVariants: FC<{
>
Add variant
- variant.weight / 10)}
- />
+
>
);
};
From ea9901c96833fafafdbfdba005c08bb24bcffe29 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nuno=20G=C3=B3is?=
Date: Thu, 21 Sep 2023 13:55:58 +0100
Subject: [PATCH 06/14] chore: GA (remove flag) for Slack App integration
(#4765)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
https://linear.app/unleash/issue/2-1405/remove-slackappaddon-feature-flag-and-make-this-ga
GA's the new Slack App integration by removing the feature flag đ
---
.../__snapshots__/create-config.test.ts.snap | 2 --
src/lib/addons/addon-schema.ts | 2 +-
src/lib/addons/index.ts | 20 +++----------------
src/lib/addons/slack-definition.ts | 2 ++
src/lib/services/addon-service.ts | 7 +------
src/lib/types/experimental.ts | 5 -----
src/server-dev.ts | 1 -
src/test/e2e/api/admin/addon.e2e.test.ts | 1 -
8 files changed, 7 insertions(+), 33 deletions(-)
diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap
index 59a28fd6e8a2..0e40bd6a3044 100644
--- a/src/lib/__snapshots__/create-config.test.ts.snap
+++ b/src/lib/__snapshots__/create-config.test.ts.snap
@@ -103,7 +103,6 @@ exports[`should create default config 1`] = `
"privateProjects": false,
"proPlanAutoCharge": false,
"responseTimeWithAppNameKillSwitch": false,
- "slackAppAddon": false,
"strictSchemaValidation": false,
"variantTypeNumber": false,
},
@@ -142,7 +141,6 @@ exports[`should create default config 1`] = `
"privateProjects": false,
"proPlanAutoCharge": false,
"responseTimeWithAppNameKillSwitch": false,
- "slackAppAddon": false,
"strictSchemaValidation": false,
"variantTypeNumber": false,
},
diff --git a/src/lib/addons/addon-schema.ts b/src/lib/addons/addon-schema.ts
index dd7b4dcbe6b5..1e3e0b212e1f 100644
--- a/src/lib/addons/addon-schema.ts
+++ b/src/lib/addons/addon-schema.ts
@@ -9,7 +9,7 @@ export const addonDefinitionSchema = joi.object().keys({
documentationUrl: joi.string().uri({ scheme: [/https?/] }),
description: joi.string().allow(''),
howTo: joi.string().optional().allow(''),
- deprecated: joi.boolean().optional().default(false),
+ deprecated: joi.string().optional().allow(''),
parameters: joi
.array()
.optional()
diff --git a/src/lib/addons/index.ts b/src/lib/addons/index.ts
index c6376dcd8ae5..39b6d137ac10 100644
--- a/src/lib/addons/index.ts
+++ b/src/lib/addons/index.ts
@@ -5,7 +5,6 @@ import DatadogAddon from './datadog';
import Addon from './addon';
import { LogProvider } from '../logger';
import SlackAppAddon from './slack-app';
-import { IFlagResolver } from '../types';
export interface IAddonProviders {
[key: string]: Addon;
@@ -14,28 +13,15 @@ export interface IAddonProviders {
export const getAddons: (args: {
getLogger: LogProvider;
unleashUrl: string;
- flagResolver: IFlagResolver;
-}) => IAddonProviders = ({ getLogger, unleashUrl, flagResolver }) => {
- const slackAppAddonEnabled = flagResolver.isEnabled('slackAppAddon');
-
- const slackAddon = new SlackAddon({ getLogger, unleashUrl });
-
- if (slackAppAddonEnabled) {
- slackAddon.definition.deprecated =
- 'This integration is deprecated. Please try the new Slack App integration instead.';
- }
-
+}) => IAddonProviders = ({ getLogger, unleashUrl }) => {
const addons: Addon[] = [
new Webhook({ getLogger }),
- slackAddon,
+ new SlackAddon({ getLogger, unleashUrl }),
+ new SlackAppAddon({ getLogger, unleashUrl }),
new TeamsAddon({ getLogger, unleashUrl }),
new DatadogAddon({ getLogger, unleashUrl, flagResolver }),
];
- if (slackAppAddonEnabled) {
- addons.push(new SlackAppAddon({ getLogger, unleashUrl }));
- }
-
return addons.reduce((map, addon) => {
// eslint-disable-next-line no-param-reassign
map[addon.name] = addon;
diff --git a/src/lib/addons/slack-definition.ts b/src/lib/addons/slack-definition.ts
index ebbec92969c8..e2407b75506f 100644
--- a/src/lib/addons/slack-definition.ts
+++ b/src/lib/addons/slack-definition.ts
@@ -22,6 +22,8 @@ const slackDefinition: IAddonDefinition = {
displayName: 'Slack',
description: 'Allows Unleash to post updates to Slack.',
documentationUrl: 'https://docs.getunleash.io/docs/addons/slack',
+ deprecated:
+ 'This integration is deprecated. Please try the new Slack App integration instead.',
alerts: [
{
type: 'warning',
diff --git a/src/lib/services/addon-service.ts b/src/lib/services/addon-service.ts
index 3105d321a910..3b4599c0c76d 100644
--- a/src/lib/services/addon-service.ts
+++ b/src/lib/services/addon-service.ts
@@ -49,11 +49,7 @@ export default class AddonService {
IUnleashStores,
'addonStore' | 'eventStore' | 'featureToggleStore'
>,
- {
- getLogger,
- server,
- flagResolver,
- }: Pick,
+ { getLogger, server }: Pick,
tagTypeService: TagTypeService,
addons?: IAddonProviders,
) {
@@ -68,7 +64,6 @@ export default class AddonService {
getAddons({
getLogger,
unleashUrl: server.unleashUrl,
- flagResolver,
});
this.sensitiveParams = this.loadSensitiveParams(this.addonProviders);
if (addonStore) {
diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts
index d7dd19770f4a..907989dd49ac 100644
--- a/src/lib/types/experimental.ts
+++ b/src/lib/types/experimental.ts
@@ -20,7 +20,6 @@ export type IFlagKey =
| 'disableBulkToggle'
| 'disableNotifications'
| 'advancedPlayground'
- | 'slackAppAddon'
| 'filterInvalidClientMetrics'
| 'lastSeenByEnvironment'
| 'customRootRolesKillSwitch'
@@ -99,10 +98,6 @@ const flags: IFlags = {
process.env.DISABLE_NOTIFICATIONS,
false,
),
- slackAppAddon: parseEnvVarBoolean(
- process.env.UNLEASH_SLACK_APP_ADDON,
- false,
- ),
filterInvalidClientMetrics: parseEnvVarBoolean(
process.env.FILTER_INVALID_CLIENT_METRICS,
false,
diff --git a/src/server-dev.ts b/src/server-dev.ts
index 41e7ed217748..730e3ac25b68 100644
--- a/src/server-dev.ts
+++ b/src/server-dev.ts
@@ -37,7 +37,6 @@ process.nextTick(async () => {
embedProxyFrontend: true,
anonymiseEventLog: false,
responseTimeWithAppNameKillSwitch: false,
- slackAppAddon: true,
lastSeenByEnvironment: true,
featureNamingPattern: true,
doraMetrics: true,
diff --git a/src/test/e2e/api/admin/addon.e2e.test.ts b/src/test/e2e/api/admin/addon.e2e.test.ts
index 5ead1b23dda4..595de3cf217b 100644
--- a/src/test/e2e/api/admin/addon.e2e.test.ts
+++ b/src/test/e2e/api/admin/addon.e2e.test.ts
@@ -13,7 +13,6 @@ beforeAll(async () => {
experimental: {
flags: {
strictSchemaValidation: true,
- slackAppAddon: true,
},
},
});
From 7eae1f83825adb038e3ebbf6c6de5b0a92ed3aaf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nuno=20G=C3=B3is?=
Date: Thu, 21 Sep 2023 14:07:24 +0100
Subject: [PATCH 07/14] fix: datadog addon needs flagResolver (#4806)
Fixes what this breaks: https://github.com/Unleash/unleash/pull/4765 -
The Datadog integration needs a `flagResolver`.
---
src/lib/addons/index.ts | 4 +++-
src/lib/services/addon-service.ts | 7 ++++++-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/lib/addons/index.ts b/src/lib/addons/index.ts
index 39b6d137ac10..9f88f33faa79 100644
--- a/src/lib/addons/index.ts
+++ b/src/lib/addons/index.ts
@@ -5,6 +5,7 @@ import DatadogAddon from './datadog';
import Addon from './addon';
import { LogProvider } from '../logger';
import SlackAppAddon from './slack-app';
+import { IFlagResolver } from '../types';
export interface IAddonProviders {
[key: string]: Addon;
@@ -13,7 +14,8 @@ export interface IAddonProviders {
export const getAddons: (args: {
getLogger: LogProvider;
unleashUrl: string;
-}) => IAddonProviders = ({ getLogger, unleashUrl }) => {
+ flagResolver: IFlagResolver;
+}) => IAddonProviders = ({ getLogger, unleashUrl, flagResolver }) => {
const addons: Addon[] = [
new Webhook({ getLogger }),
new SlackAddon({ getLogger, unleashUrl }),
diff --git a/src/lib/services/addon-service.ts b/src/lib/services/addon-service.ts
index 3b4599c0c76d..3105d321a910 100644
--- a/src/lib/services/addon-service.ts
+++ b/src/lib/services/addon-service.ts
@@ -49,7 +49,11 @@ export default class AddonService {
IUnleashStores,
'addonStore' | 'eventStore' | 'featureToggleStore'
>,
- { getLogger, server }: Pick,
+ {
+ getLogger,
+ server,
+ flagResolver,
+ }: Pick,
tagTypeService: TagTypeService,
addons?: IAddonProviders,
) {
@@ -64,6 +68,7 @@ export default class AddonService {
getAddons({
getLogger,
unleashUrl: server.unleashUrl,
+ flagResolver,
});
this.sensitiveParams = this.loadSensitiveParams(this.addonProviders);
if (addonStore) {
From fc8ddbd6ffddf8b4a4a752fc4fc0e28c6fda9e5c Mon Sep 17 00:00:00 2001
From: Mateusz Kwasniewski
Date: Thu, 21 Sep 2023 15:48:17 +0200
Subject: [PATCH 08/14] fix: string-width issue when running docker container
(#4808)
---
docker/yarn.lock | 487 ++++++++++++++++++++++++++++++-----------------
1 file changed, 314 insertions(+), 173 deletions(-)
diff --git a/docker/yarn.lock b/docker/yarn.lock
index 7328750aee98..4a195abd7185 100644
--- a/docker/yarn.lock
+++ b/docker/yarn.lock
@@ -35,11 +35,11 @@
z-schema "^5.0.1"
"@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0":
- version "7.22.6"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438"
- integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==
+ version "7.22.15"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8"
+ integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==
dependencies:
- regenerator-runtime "^0.13.11"
+ regenerator-runtime "^0.14.0"
"@colors/colors@1.5.0":
version "1.5.0"
@@ -160,15 +160,61 @@
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
+"@slack/logger@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-3.0.0.tgz#b736d4e1c112c22a10ffab0c2d364620aedcb714"
+ integrity sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==
+ dependencies:
+ "@types/node" ">=12.0.0"
+
+"@slack/types@^2.8.0":
+ version "2.8.0"
+ resolved "https://registry.yarnpkg.com/@slack/types/-/types-2.8.0.tgz#11ea10872262a7e6f86f54e5bcd4f91e3a41fe91"
+ integrity sha512-ghdfZSF0b4NC9ckBA8QnQgC9DJw2ZceDq0BIjjRSv6XAZBXJdWgxIsYz0TYnWSiqsKZGH2ZXbj9jYABZdH3OSQ==
+
+"@slack/web-api@^6.9.0":
+ version "6.9.0"
+ resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-6.9.0.tgz#d829dcfef490dbce8e338912706b6f39dcde3ad2"
+ integrity sha512-RME5/F+jvQmZHkoP+ogrDbixq1Ms1mBmylzuWq4sf3f7GCpMPWoiZ+WqWk+sism3vrlveKWIgO9R4Qg9fiRyoQ==
+ dependencies:
+ "@slack/logger" "^3.0.0"
+ "@slack/types" "^2.8.0"
+ "@types/is-stream" "^1.1.0"
+ "@types/node" ">=12.0.0"
+ axios "^0.27.2"
+ eventemitter3 "^3.1.0"
+ form-data "^2.5.0"
+ is-electron "2.2.2"
+ is-stream "^1.1.0"
+ p-queue "^6.6.1"
+ p-retry "^4.0.0"
+
"@tootallnate/once@2":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
+"@types/is-stream@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@types/is-stream/-/is-stream-1.1.0.tgz#b84d7bb207a210f2af9bed431dc0fbe9c4143be1"
+ integrity sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==
+ dependencies:
+ "@types/node" "*"
+
"@types/json-schema@^7.0.6", "@types/json-schema@^7.0.9":
- version "7.0.12"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
- integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
+ version "7.0.13"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85"
+ integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==
+
+"@types/node@*", "@types/node@>=12.0.0":
+ version "20.6.3"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.3.tgz#5b763b321cd3b80f6b8dde7a37e1a77ff9358dd9"
+ integrity sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA==
+
+"@types/retry@0.12.0":
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
+ integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
"@unleash/express-openapi@^0.3.0":
version "0.3.0"
@@ -200,12 +246,10 @@ agent-base@6, agent-base@^6.0.2:
debug "4"
agentkeepalive@^4.2.1:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255"
- integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923"
+ integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==
dependencies:
- debug "^4.1.0"
- depd "^2.0.0"
humanize-ms "^1.2.1"
aggregate-error@^3.0.0:
@@ -285,7 +329,7 @@ array-flatten@3.0.0:
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-3.0.0.tgz#6428ca2ee52c7b823192ec600fa3ed2f157cd541"
integrity sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==
-asn1@^0.2.4:
+asn1@^0.2.6:
version "0.2.6"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d"
integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==
@@ -297,6 +341,19 @@ async@3.2.3, async@^2.6.4, async@^3.2.4:
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+axios@^0.27.2:
+ version "0.27.2"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
+ integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
+ dependencies:
+ follow-redirects "^1.14.9"
+ form-data "^4.0.0"
+
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@@ -376,10 +433,10 @@ buffer-writer@2.0.0:
resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04"
integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==
-buildcheck@0.0.3:
- version "0.0.3"
- resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.3.tgz#70451897a95d80f7807e68fc412eb2e7e35ff4d5"
- integrity sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA==
+buildcheck@~0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238"
+ integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==
busboy@^1.0.0:
version "1.6.0"
@@ -423,15 +480,15 @@ cacache@^16.1.0:
unique-filename "^2.0.0"
cacache@^17.0.0:
- version "17.1.3"
- resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.3.tgz#c6ac23bec56516a7c0c52020fd48b4909d7c7044"
- integrity sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==
+ version "17.1.4"
+ resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.4.tgz#b3ff381580b47e85c6e64f801101508e26604b35"
+ integrity sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==
dependencies:
"@npmcli/fs" "^3.1.0"
fs-minipass "^3.0.0"
glob "^10.2.2"
lru-cache "^7.7.1"
- minipass "^5.0.0"
+ minipass "^7.0.3"
minipass-collect "^1.0.2"
minipass-flush "^1.0.5"
minipass-pipeline "^1.2.4"
@@ -510,6 +567,13 @@ colors@1.0.x:
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
integrity sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==
+combined-stream@^1.0.6, combined-stream@^1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ dependencies:
+ delayed-stream "~1.0.0"
+
commander@^10.0.0:
version "10.0.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
@@ -520,11 +584,6 @@ commander@^6.1.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
-commander@^9.1.0:
- version "9.5.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30"
- integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==
-
compressible@~2.0.16:
version "2.0.18"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
@@ -634,13 +693,13 @@ cors@^2.8.5:
object-assign "^4"
vary "^1"
-cpu-features@~0.0.4:
- version "0.0.4"
- resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.4.tgz#0023475bb4f4c525869c162e4108099e35bf19d8"
- integrity sha512-fKiZ/zp1mUwQbnzb9IghXtHtDoTMtNeb8oYGx6kX2SYfhnG0HNdBEBIzB9b5KlXu5DQPhfy3mInbBxFcgwAr3A==
+cpu-features@~0.0.8:
+ version "0.0.9"
+ resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.9.tgz#5226b92f0f1c63122b0a3eb84cb8335a4de499fc"
+ integrity sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==
dependencies:
- buildcheck "0.0.3"
- nan "^2.15.0"
+ buildcheck "~0.0.6"
+ nan "^2.17.0"
cross-spawn@^7.0.0:
version "7.0.3"
@@ -683,10 +742,10 @@ db-migrate-base@^2.3.0:
dependencies:
bluebird "^3.1.1"
-db-migrate-pg@1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/db-migrate-pg/-/db-migrate-pg-1.3.0.tgz#b2e81fbf727e62e7b17dc1aa27a4c7d411468786"
- integrity sha512-WBf1CTpQUkkoqb+c2dOe3mvuCM8OMpUtC6zFqRqdvccdqqCsDyqOTMT4islRiCuzn1G1BQdrfVnB8OuVsddWRQ==
+db-migrate-pg@1.4.2:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/db-migrate-pg/-/db-migrate-pg-1.4.2.tgz#af1f40fdbe89405e6ee4b476c1e3e4aeb71144e7"
+ integrity sha512-OqZMM74A74+1w1MpjTGKAelI6bVjjvFWJcdd9z3VADH+NTKocmcX20qFBmnWJrzIyCyBcUiYf4/akJebyB39zQ==
dependencies:
bluebird "^3.1.1"
db-migrate-base "^2.3.0"
@@ -698,10 +757,10 @@ db-migrate-shared@1.2.0, db-migrate-shared@^1.2.0:
resolved "https://registry.yarnpkg.com/db-migrate-shared/-/db-migrate-shared-1.2.0.tgz#6125be1b3a5e661229fc75d50c85f6c3ffadbede"
integrity sha512-65k86bVeHaMxb2L0Gw3y5V+CgZSRwhVQMwDMydmw5MvIpHHwD6SmBciqIwHsZfzJ9yzV/yYhdRefRM6FV5/siw==
-db-migrate@0.11.13:
- version "0.11.13"
- resolved "https://registry.yarnpkg.com/db-migrate/-/db-migrate-0.11.13.tgz#b9e5b242c62e82ef8ed2b29e118698813e953d67"
- integrity sha512-OE/bbDo/mQvLmZrui/2jNAiAECJROSURCOU5xs6qKr3FvtUE2O6b0xBUI6WyAAKdili3LJQHFlpqugiYCGTSBA==
+db-migrate@0.11.14:
+ version "0.11.14"
+ resolved "https://registry.yarnpkg.com/db-migrate/-/db-migrate-0.11.14.tgz#a9fb80e70adc281355f5be3cf6eec141ffd1be5a"
+ integrity sha512-8e+/YsIlM3d69hj+cb6qG6WyubR8nwXfDf9gGLWl4AxHpI6mTomcqhRLNfPrs7Le7AZv2eEsgK8hkXDSQqfIvg==
dependencies:
balanced-match "^1.0.0"
bluebird "^3.1.1"
@@ -733,7 +792,7 @@ debug@3.2.7:
dependencies:
ms "^2.1.1"
-debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.3.3, debug@^4.3.4:
+debug@4, debug@4.3.4, debug@^4.3.3, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -760,7 +819,12 @@ deepmerge@^4.2.2:
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
-depd@2.0.0, depd@^2.0.0, depd@~2.0.0:
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
+depd@2.0.0, depd@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
@@ -889,10 +953,20 @@ event-emitter@^0.3.5:
d "1"
es5-ext "~0.10.14"
+eventemitter3@^3.1.0:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
+ integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==
+
+eventemitter3@^4.0.4:
+ version "4.0.7"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
+ integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
+
express-rate-limit@^6.6.0:
- version "6.7.0"
- resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-6.7.0.tgz#6aa8a1bd63dfe79702267b3af1161a93afc1d3c2"
- integrity sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==
+ version "6.11.2"
+ resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-6.11.2.tgz#6c42035603d3b52e4e2fb59f6ebaa89e628ef980"
+ integrity sha512-a7uwwfNTh1U60ssiIkuLFWHt4hAC5yxlLGU2VP0X4YNlyEDZAqF4tK3GD3NSitVBrCQmQ0++0uOyFOgC2y4DDw==
express-session@^1.17.1:
version "1.17.3"
@@ -946,11 +1020,11 @@ express@^4.18.2:
vary "~1.1.2"
ext@^1.1.2:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52"
- integrity sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f"
+ integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==
dependencies:
- type "^2.5.0"
+ type "^2.7.2"
eyes@0.1.x:
version "0.1.8"
@@ -1002,9 +1076,14 @@ find-up@^4.1.0:
path-exists "^4.0.0"
flatted@^3.2.7:
- version "3.2.7"
- resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
- integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
+ version "3.2.9"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf"
+ integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
+
+follow-redirects@^1.14.9:
+ version "1.15.3"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
+ integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
for-in@^0.1.3:
version "0.1.8"
@@ -1031,6 +1110,24 @@ foreground-child@^3.1.0:
cross-spawn "^7.0.0"
signal-exit "^4.0.1"
+form-data@^2.5.0:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
+ integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.6"
+ mime-types "^2.1.12"
+
+form-data@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+ integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.8"
+ mime-types "^2.1.12"
+
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@@ -1058,11 +1155,11 @@ fs-minipass@^2.0.0, fs-minipass@^2.1.0:
minipass "^3.0.0"
fs-minipass@^3.0.0:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.2.tgz#5b383858efa8c1eb8c33b39e994f7e8555b8b3a3"
- integrity sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54"
+ integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==
dependencies:
- minipass "^5.0.0"
+ minipass "^7.0.3"
fs.realpath@^1.0.0:
version "1.0.0"
@@ -1100,15 +1197,15 @@ getopts@2.3.0:
integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==
glob@^10.2.2:
- version "10.3.1"
- resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.1.tgz#9789cb1b994515bedb811a6deca735b5c37d2bf4"
- integrity sha512-9BKYcEeIs7QwlCYs+Y3GBvqAMISufUS0i2ELd11zpZjxI5V9iyRj0HgzB5/cLf2NY4vcYBTYzJ7GIui7j/4DOw==
+ version "10.3.5"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.5.tgz#4c0e46b5bccd78ac42b06a7eaaeb9ee34062968e"
+ integrity sha512-bYUpUD7XDEHI4Q2O5a7PXGvyw4deKR70kHiDxzQbe925wbZknhOzUt2xBgTkYL6RBcVeXYuD9iNYeqoWbBZQnA==
dependencies:
foreground-child "^3.1.0"
jackspeak "^2.0.3"
minimatch "^9.0.1"
- minipass "^5.0.0 || ^6.0.2"
- path-scurry "^1.10.0"
+ minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
+ path-scurry "^1.10.1"
glob@^7.1.3:
version "7.2.3"
@@ -1301,13 +1398,18 @@ is-buffer@^1.0.2, is-buffer@^1.1.5:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
-is-core-module@^2.11.0:
- version "2.12.1"
- resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd"
- integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==
+is-core-module@^2.13.0:
+ version "2.13.0"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db"
+ integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==
dependencies:
has "^1.0.3"
+is-electron@2.2.2:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.2.2.tgz#3778902a2044d76de98036f5dc58089ac4d80bb9"
+ integrity sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==
+
is-extendable@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
@@ -1340,6 +1442,11 @@ is-promise@^2.2.2:
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1"
integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==
+is-stream@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+ integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==
+
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@@ -1361,18 +1468,18 @@ isstream@0.1.x:
integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==
jackspeak@^2.0.3:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.2.1.tgz#655e8cf025d872c9c03d3eb63e8f0c024fef16a6"
- integrity sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.3.tgz#95e4cbcc03b3eb357bf6bcce14a903fb3d1151e1"
+ integrity sha512-R2bUw+kVZFS/h1AZqBKrSgDmdmjApzgY0AlCPumopFiAlbUxE2gf+SCuBzQ0cP5hHmUmFYF5yw55T97Th5Kstg==
dependencies:
"@isaacs/cliui" "^8.0.2"
optionalDependencies:
"@pkgjs/parseargs" "^0.11.0"
joi@^17.3.0:
- version "17.9.2"
- resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.2.tgz#8b2e4724188369f55451aebd1d0b1d9482470690"
- integrity sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==
+ version "17.10.2"
+ resolved "https://registry.yarnpkg.com/joi/-/joi-17.10.2.tgz#4ecc348aa89ede0b48335aad172e0f5591e55b29"
+ integrity sha512-hcVhjBxRNW/is3nNLdGLIjkgXetkeGc2wyhydhz8KumG23Aerk4HPjU5zaPAMRqXQFc0xNqXTC7+zQjxr0GlKA==
dependencies:
"@hapi/hoek" "^9.0.0"
"@hapi/topo" "^5.0.0"
@@ -1380,10 +1487,10 @@ joi@^17.3.0:
"@sideway/formula" "^3.0.1"
"@sideway/pinpoint" "^2.0.0"
-js-sha256@^0.9.0:
- version "0.9.0"
- resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966"
- integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==
+js-sha256@^0.10.0:
+ version "0.10.1"
+ resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.10.1.tgz#b40104ba1368e823fdd5f41b66b104b15a0da60d"
+ integrity sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw==
js-yaml@^4.1.0:
version "4.1.0"
@@ -1392,10 +1499,10 @@ js-yaml@^4.1.0:
dependencies:
argparse "^2.0.1"
-json-schema-to-ts@2.9.1:
- version "2.9.1"
- resolved "https://registry.yarnpkg.com/json-schema-to-ts/-/json-schema-to-ts-2.9.1.tgz#0e055b787587477abdb7e880c874efad3dba7779"
- integrity sha512-8MNpRGERlCUWYeJwsWkMrJ0MWzBz49dfqpG+n9viiIlP4othaahbiaNQZuBzmPxRLUhOv1QJMCzW5WE8nHFGIQ==
+json-schema-to-ts@2.9.2:
+ version "2.9.2"
+ resolved "https://registry.yarnpkg.com/json-schema-to-ts/-/json-schema-to-ts-2.9.2.tgz#a054bc6410f13c7a2fc51aeabac52292e885b98d"
+ integrity sha512-h9WqLkTVpBbiaPb5OmeUpz/FBLS/kvIJw4oRCPiEisIu2WjMh+aai0QIY2LoOhRFx5r92taGLcerIrzxKBAP6g==
dependencies:
"@babel/runtime" "^7.18.3"
"@types/json-schema" "^7.0.9"
@@ -1464,12 +1571,12 @@ kind-of@^6.0.3:
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
knex@^2.3.0, knex@^2.4.2:
- version "2.4.2"
- resolved "https://registry.yarnpkg.com/knex/-/knex-2.4.2.tgz#a34a289d38406dc19a0447a78eeaf2d16ebedd61"
- integrity sha512-tMI1M7a+xwHhPxjbl/H9K1kHX+VncEYcvCx5K00M16bWvpYPKAZd6QrCu68PtHAdIZNQPWZn0GVhqVBEthGWCg==
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/knex/-/knex-2.5.1.tgz#a6c6b449866cf4229f070c17411f23871ba52ef9"
+ integrity sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA==
dependencies:
colorette "2.0.19"
- commander "^9.1.0"
+ commander "^10.0.0"
debug "4.3.4"
escalade "^3.1.1"
esm "^3.2.25"
@@ -1477,7 +1584,7 @@ knex@^2.3.0, knex@^2.4.2:
getopts "2.3.0"
interpret "^2.2.0"
lodash "^4.17.21"
- pg-connection-string "2.5.0"
+ pg-connection-string "2.6.1"
rechoir "^0.8.0"
resolve-from "^5.0.0"
tarn "^3.0.2"
@@ -1648,7 +1755,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
-mime-types@~2.1.24, mime-types@~2.1.34:
+mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
@@ -1666,9 +1773,9 @@ mime@^3.0.0:
integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==
minimatch@^3.1.1, minimatch@^5.0.0, minimatch@^5.0.1, minimatch@^9.0.1:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.2.tgz#0939d7d6f0898acbd1508abe534d1929368a8fff"
- integrity sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==
+ version "5.1.6"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
+ integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
dependencies:
brace-expansion "^2.0.1"
@@ -1696,11 +1803,11 @@ minipass-fetch@^2.0.3:
encoding "^0.1.13"
minipass-fetch@^3.0.0:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.3.tgz#d9df70085609864331b533c960fd4ffaa78d15ce"
- integrity sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.4.tgz#4d4d9b9f34053af6c6e597a64be8e66e42bf45b7"
+ integrity sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==
dependencies:
- minipass "^5.0.0"
+ minipass "^7.0.3"
minipass-sized "^1.0.3"
minizlib "^2.1.2"
optionalDependencies:
@@ -1735,19 +1842,19 @@ minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6:
yallist "^4.0.0"
minipass@^4.0.2:
- version "4.2.7"
- resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.7.tgz#14c6fc0dcab54d9c4dd64b2b7032fef04efec218"
- integrity sha512-ScVIgqHcXRMyfflqHmEW0bm8z8rb5McHyOY3ewX9JBgZaR77G7nxq9L/mtV96/QbAAwtbCAHVVLzD1kkyfFQEw==
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a"
+ integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==
minipass@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d"
integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==
-"minipass@^5.0.0 || ^6.0.2":
- version "6.0.2"
- resolved "https://registry.yarnpkg.com/minipass/-/minipass-6.0.2.tgz#542844b6c4ce95b202c0995b0a471f1229de4c81"
- integrity sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==
+"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.3.tgz#05ea638da44e475037ed94d1c7efcc76a25e1974"
+ integrity sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==
minizlib@^2.1.1, minizlib@^2.1.2:
version "2.1.2"
@@ -1830,10 +1937,10 @@ mute-stream@~0.0.4:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
-nan@^2.15.0:
- version "2.16.0"
- resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916"
- integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==
+nan@^2.17.0:
+ version "2.18.0"
+ resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554"
+ integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==
negotiator@0.6.3, negotiator@^0.6.3:
version "0.6.3"
@@ -1856,14 +1963,14 @@ node-fs@~0.1.5:
integrity sha512-XqDBlmUKgDGe76+lZ/0sRBF3XW2vVcK07+ZPvdpUTK8jrvtPahUd0aBqJ9+ZjB01ANjZLuvK3O/eoMVmz62rpA==
nodemailer@^6.5.0:
- version "6.9.3"
- resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.3.tgz#e4425b85f05d83c43c5cd81bf84ab968f8ef5cbe"
- integrity sha512-fy9v3NgTzBngrMFkDsKEj0r02U7jm6XfC3b52eoNV+GCrGj+s8pt5OqhiJdWKuw51zCTdiNR/IUD1z33LIIGpg==
+ version "6.9.5"
+ resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.5.tgz#eaeae949c62ec84ef1e9128df89fc146a1017aca"
+ integrity sha512-/dmdWo62XjumuLc5+AYQZeiRj+PRR8y8qKtFCOyuOl1k/hckZd8durUUHs/ucKx6/8kN+wFxqKJlQ/LK/qR5FA==
oauth@0.9.x:
version "0.9.15"
resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1"
- integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE=
+ integrity sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==
object-assign@^4, object-assign@^4.1.1:
version "4.1.1"
@@ -1904,6 +2011,11 @@ owasp-password-strength-test@^1.3.0:
resolved "https://registry.yarnpkg.com/owasp-password-strength-test/-/owasp-password-strength-test-1.3.0.tgz#4f629e42903e8f6d279b230d657ab61e58e44b12"
integrity sha512-33/Z+vyjlFaVZsT7aAFe3SkQZdU6su59XNkYdU5o2Fssz0D9dt6uiFaMm62M7dFQSKogULq8UYvdKnHkeqNB2w==
+p-finally@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+ integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==
+
p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
@@ -1925,6 +2037,29 @@ p-map@^4.0.0:
dependencies:
aggregate-error "^3.0.0"
+p-queue@^6.6.1:
+ version "6.6.2"
+ resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426"
+ integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==
+ dependencies:
+ eventemitter3 "^4.0.4"
+ p-timeout "^3.2.0"
+
+p-retry@^4.0.0:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16"
+ integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==
+ dependencies:
+ "@types/retry" "0.12.0"
+ retry "^0.13.1"
+
+p-timeout@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe"
+ integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==
+ dependencies:
+ p-finally "^1.0.0"
+
p-try@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
@@ -1948,9 +2083,9 @@ parseurl@~1.3.2, parseurl@~1.3.3:
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
passport-oauth2@^1.4.0:
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.6.1.tgz#c5aee8f849ce8bd436c7f81d904a3cd1666f181b"
- integrity sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.7.0.tgz#5c4766c8531ac45ffe9ec2c09de9809e2c841fc4"
+ integrity sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==
dependencies:
base64url "3.x.x"
oauth "0.9.x"
@@ -1961,7 +2096,7 @@ passport-oauth2@^1.4.0:
passport-strategy@1.x.x:
version "1.0.0"
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
- integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=
+ integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==
passport@^0.6.0:
version "0.6.0"
@@ -1992,7 +2127,7 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
-path-scurry@1.6.3, path-scurry@^1.10.0:
+path-scurry@1.6.3, path-scurry@^1.10.1:
version "1.6.3"
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.3.tgz#4eba7183d64ef88b63c7d330bddc3ba279dc6c40"
integrity sha512-RAmB+n30SlN+HnNx6EbcpoDy9nwdpcGPnEKrJnu6GZoDWBdIjo1UQMVtW2ybtC7LC2oKLcMq8y5g8WnKLiod9g==
@@ -2013,23 +2148,23 @@ path-to-regexp@^2.4.0:
pause@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d"
- integrity sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=
+ integrity sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==
pg-cloudflare@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98"
integrity sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==
-pg-connection-string@2.5.0:
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34"
- integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==
-
-pg-connection-string@^2.5.0, pg-connection-string@^2.6.1:
+pg-connection-string@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.1.tgz#78c23c21a35dd116f48e12e23c0965e8d9e2cbfb"
integrity sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==
+pg-connection-string@^2.5.0, pg-connection-string@^2.6.2:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.2.tgz#713d82053de4e2bd166fab70cd4f26ad36aab475"
+ integrity sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==
+
pg-int8@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
@@ -2057,13 +2192,13 @@ pg-types@^2.1.0:
postgres-interval "^1.1.0"
pg@^8.0.3, pg@^8.7.3:
- version "8.11.1"
- resolved "https://registry.yarnpkg.com/pg/-/pg-8.11.1.tgz#297e0eb240306b1e9e4f55af8a3bae76ae4810b1"
- integrity sha512-utdq2obft07MxaDg0zBJI+l/M3mBRfIpEN3iSemsz0G5F2/VXx+XzqF4oxrbIZXQxt2AZzIUzyVg/YM6xOP/WQ==
+ version "8.11.3"
+ resolved "https://registry.yarnpkg.com/pg/-/pg-8.11.3.tgz#d7db6e3fe268fcedd65b8e4599cda0b8b4bf76cb"
+ integrity sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==
dependencies:
buffer-writer "2.0.0"
packet-reader "1.0.0"
- pg-connection-string "^2.6.1"
+ pg-connection-string "^2.6.2"
pg-pool "^3.6.1"
pg-protocol "^1.6.0"
pg-types "^2.1.0"
@@ -2218,10 +2353,10 @@ rechoir@^0.8.0:
dependencies:
resolve "^1.20.0"
-regenerator-runtime@^0.13.11:
- version "0.13.11"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
- integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
+regenerator-runtime@^0.14.0:
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
+ integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
require-directory@^2.1.1:
version "2.1.1"
@@ -2244,11 +2379,11 @@ resolve-from@^5.0.0:
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve@^1.1.6, resolve@^1.20.0:
- version "1.22.2"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f"
- integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==
+ version "1.22.6"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362"
+ integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==
dependencies:
- is-core-module "^2.11.0"
+ is-core-module "^2.13.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
@@ -2265,6 +2400,11 @@ retry@^0.12.0:
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==
+retry@^0.13.1:
+ version "0.13.1"
+ resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
+ integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==
+
revalidator@0.1.x:
version "0.1.8"
resolved "https://registry.yarnpkg.com/revalidator/-/revalidator-0.1.8.tgz#fece61bfa0c1b52a206bd6b18198184bdd523a3b"
@@ -2322,10 +2462,10 @@ sanitize-filename@^1.6.3:
dependencies:
truncate-utf8-bytes "^1.0.0"
-semver@^5.0.3, semver@^5.3.0, semver@^7.3.5, semver@^7.3.8, semver@^7.5.3:
- version "7.5.3"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e"
- integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==
+semver@^5.0.3, semver@^5.3.0, semver@^7.3.5, semver@^7.5.3:
+ version "7.5.4"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
+ integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies:
lru-cache "^6.0.0"
@@ -2419,9 +2559,9 @@ side-channel@^1.0.4:
object-inspect "^1.9.0"
signal-exit@^4.0.1:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.0.2.tgz#ff55bb1d9ff2114c13b400688fa544ac63c36967"
- integrity sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
+ integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
smart-buffer@^4.2.0:
version "4.2.0"
@@ -2451,22 +2591,22 @@ split2@^4.1.0:
integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==
ssh2@1.4.0, ssh2@^1.4.0:
- version "1.9.0"
- resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.9.0.tgz#3ab8330cec2bb6ba3061052fefb25bc19e36f176"
- integrity sha512-rhhIZT0eMPvCBSOG8CpqZZ7gre2vgXaIqmb3Jb83t88rjsxIsFzDanqBJM9Ns8BmP1835A5IbQ199io4EUZwOA==
+ version "1.14.0"
+ resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.14.0.tgz#8f68440e1b768b66942c9e4e4620b2725b3555bb"
+ integrity sha512-AqzD1UCqit8tbOKoj6ztDDi1ffJZ2rV2SwlgrVVrHPkV5vWqGJOVp5pmtj18PunkPJAuKQsnInyKV+/Nb2bUnA==
dependencies:
- asn1 "^0.2.4"
+ asn1 "^0.2.6"
bcrypt-pbkdf "^1.0.2"
optionalDependencies:
- cpu-features "~0.0.4"
- nan "^2.15.0"
+ cpu-features "~0.0.8"
+ nan "^2.17.0"
ssri@^10.0.0:
- version "10.0.4"
- resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.4.tgz#5a20af378be586df139ddb2dfb3bf992cf0daba6"
- integrity sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==
+ version "10.0.5"
+ resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.5.tgz#e49efcd6e36385196cb515d3a2ad6c3f0265ef8c"
+ integrity sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==
dependencies:
- minipass "^5.0.0"
+ minipass "^7.0.3"
ssri@^9.0.0:
version "9.0.1"
@@ -2571,9 +2711,9 @@ swagger-ui-dist@^4.10.3:
integrity sha512-n/gFn+R7G/BXWwl5UZLw6F1YgWOlf3zkwGlsPhTMhNtAAolBGKg0JS5b2RKt5NI6/hSopVaSrki2wTIMUDDy2w==
tar@^6.1.11:
- version "6.1.15"
- resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69"
- integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73"
+ integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
@@ -2620,9 +2760,9 @@ truncate-utf8-bytes@^1.0.0:
utf8-byte-length "^1.0.1"
ts-algebra@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/ts-algebra/-/ts-algebra-1.2.0.tgz#f91c481207a770f0d14d055c376cbee040afdfc9"
- integrity sha512-kMuJJd8B2N/swCvIvn1hIFcIOrLGbWl9m/J6O3kHx9VRaevh00nvgjPiEGaRee7DRaAczMYR2uwWvXU22VFltw==
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/ts-algebra/-/ts-algebra-1.2.2.tgz#b75d301c28cd4126cd344760a47b43e48e2872e0"
+ integrity sha512-kloPhf1hq3JbCPOTYoOWDKxebWjNb2o/LKnNfkWhxVVisFFmMJPPdJeGoGmM+iRLyoXAR61e08Pb+vUXINg8aA==
ts-toolbelt@^9.6.0:
version "9.6.0"
@@ -2666,7 +2806,7 @@ type@^1.0.1:
resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
-type@^2.5.0:
+type@^2.7.2:
version "2.7.2"
resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0"
integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==
@@ -2721,19 +2861,20 @@ universalify@^0.1.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
-unleash-client@3.21.0:
- version "3.21.0"
- resolved "https://registry.yarnpkg.com/unleash-client/-/unleash-client-3.21.0.tgz#a31ab30acb42abfb3a21180aa83e4415a3124ec1"
- integrity sha512-I7eYhRyOia3oBZ9Tu1v+IlNO+XJgsjcMEO2+j+e4A7LTTKZvGoV8WPfDGGxiMPKBPHNUACkERB3YhCQ9jzTGoQ==
+unleash-client@4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/unleash-client/-/unleash-client-4.1.1.tgz#ad3e90853f98885bbb4746af813514e6d1e7dee9"
+ integrity sha512-cliJJ82unQauip8/7TQhJbvuHMgBIrM167672uV5RmeD7buluAHm1x0BmYjqsXMpE3MX06m05EzpRz62H90puQ==
dependencies:
ip "^1.1.8"
make-fetch-happen "^10.2.1"
murmurhash3js "^3.0.1"
- semver "^7.3.8"
+ semver "^7.5.3"
"unleash-server@file:../build":
- version "5.2.0-main"
+ version "5.4.0-main"
dependencies:
+ "@slack/web-api" "^6.9.0"
"@unleash/express-openapi" "^0.3.0"
ajv "^8.11.0"
ajv-formats "^2.1.1"
@@ -2745,8 +2886,8 @@ unleash-client@3.21.0:
cookie-session "^2.0.0-rc.1"
cors "^2.8.5"
date-fns "^2.25.0"
- db-migrate "0.11.13"
- db-migrate-pg "1.3.0"
+ db-migrate "0.11.14"
+ db-migrate-pg "1.4.2"
db-migrate-shared "1.2.0"
deep-object-diff "^1.1.9"
deepmerge "^4.2.2"
@@ -2761,9 +2902,9 @@ unleash-client@3.21.0:
http-errors "^2.0.0"
ip "^1.1.8"
joi "^17.3.0"
- js-sha256 "^0.9.0"
+ js-sha256 "^0.10.0"
js-yaml "^4.1.0"
- json-schema-to-ts "2.9.1"
+ json-schema-to-ts "2.9.2"
json2csv "^5.0.7"
knex "^2.4.2"
lodash.get "^4.4.2"
@@ -2790,7 +2931,7 @@ unleash-client@3.21.0:
stoppable "^1.1.0"
ts-toolbelt "^9.6.0"
type-is "^1.6.18"
- unleash-client "3.21.0"
+ unleash-client "4.1.1"
uuid "^9.0.0"
unpipe@1.0.0, unpipe@~1.0.0:
@@ -2818,17 +2959,17 @@ util-deprecate@~1.0.1:
utils-merge@1.0.1, utils-merge@1.x.x, utils-merge@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
- integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
+ integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
uuid@^9.0.0:
- version "9.0.0"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
- integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
+ integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
validator@^13.7.0:
- version "13.9.0"
- resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855"
- integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==
+ version "13.11.0"
+ resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b"
+ integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==
vary@^1, vary@~1.1.2:
version "1.1.2"
From ac018447f9c254819db3dbe065d75a2baabe62f1 Mon Sep 17 00:00:00 2001
From: Jaanus Sellin
Date: Fri, 22 Sep 2023 11:54:33 +0300
Subject: [PATCH 09/14] feat: optimize private projects for enterprise (#4812)
---
.../__snapshots__/create-config.test.ts.snap | 1 +
src/lib/create-config.test.ts | 16 +++++++++
src/lib/create-config.ts | 6 ++++
.../features/playground/playground-service.ts | 18 +++++-----
.../createPrivateProjectChecker.ts | 9 +++--
.../fakePrivateProjectChecker.ts | 3 +-
.../private-project/privateProjectChecker.ts | 25 ++++++++-----
.../privateProjectCheckerType.ts | 4 ++-
.../private-project/privateProjectStore.ts | 36 ++++++++++++++-----
.../privateProjectStoreType.ts | 4 ++-
.../client-metrics/instance-service.ts | 26 ++++++++------
src/lib/services/feature-toggle-service.ts | 18 +++++++---
src/lib/services/index.ts | 1 +
src/lib/services/project-service.ts | 13 ++++---
src/lib/types/no-auth-user.ts | 3 +-
src/lib/types/option.ts | 1 +
16 files changed, 134 insertions(+), 50 deletions(-)
diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap
index 0e40bd6a3044..771f220e8296 100644
--- a/src/lib/__snapshots__/create-config.test.ts.snap
+++ b/src/lib/__snapshots__/create-config.test.ts.snap
@@ -162,6 +162,7 @@ exports[`should create default config 1`] = `
"keepExisting": false,
},
"inlineSegmentConstraints": true,
+ "isEnterprise": false,
"listen": {
"host": undefined,
"port": 4242,
diff --git a/src/lib/create-config.test.ts b/src/lib/create-config.test.ts
index d7057df9b01b..0d8b137595a7 100644
--- a/src/lib/create-config.test.ts
+++ b/src/lib/create-config.test.ts
@@ -471,3 +471,19 @@ test.each(['demo', '/demo', '/demo/'])(
expect(config.server.baseUriPath).toBe('/demo');
},
);
+
+test('Config with enterpriseVersion set and pro environment should set isEnterprise to false', async () => {
+ let config = createConfig({
+ enterpriseVersion: '5.3.0',
+ ui: { environment: 'pro' },
+ });
+ expect(config.isEnterprise).toBe(false);
+});
+
+test('Config with enterpriseVersion set and not pro environment should set isEnterprise to true', async () => {
+ let config = createConfig({
+ enterpriseVersion: '5.3.0',
+ ui: { environment: 'Enterprise' },
+ });
+ expect(config.isEnterprise).toBe(true);
+});
diff --git a/src/lib/create-config.ts b/src/lib/create-config.ts
index a18015f68636..80ee6cfcdefe 100644
--- a/src/lib/create-config.ts
+++ b/src/lib/create-config.ts
@@ -481,6 +481,11 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
const clientFeatureCaching = loadClientCachingOptions(options);
const prometheusApi = options.prometheusApi || process.env.PROMETHEUS_API;
+
+ const isEnterprise =
+ Boolean(options.enterpriseVersion) &&
+ ui.environment?.toLowerCase() !== 'pro';
+
return {
db,
session,
@@ -513,6 +518,7 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
prometheusApi,
publicFolder: options.publicFolder,
disableScheduler: options.disableScheduler,
+ isEnterprise: isEnterprise,
};
}
diff --git a/src/lib/features/playground/playground-service.ts b/src/lib/features/playground/playground-service.ts
index 4ffcf8d81377..4dbb64f42c47 100644
--- a/src/lib/features/playground/playground-service.ts
+++ b/src/lib/features/playground/playground-service.ts
@@ -102,17 +102,19 @@ export class PlaygroundService {
let filteredProjects: typeof projects;
if (this.flagResolver.isEnabled('privateProjects')) {
- const accessibleProjects =
+ const projectAccess =
await this.privateProjectChecker.getUserAccessibleProjects(
userId,
);
- filteredProjects =
- projects === ALL
- ? accessibleProjects
- : projects.filter((project) =>
- accessibleProjects.includes(project),
- );
- console.log(accessibleProjects);
+ if (projectAccess.mode === 'all') {
+ filteredProjects = projects;
+ } else if (projects === ALL) {
+ filteredProjects = projectAccess.projects;
+ } else {
+ filteredProjects = projects.filter((project) =>
+ projectAccess.projects.includes(project),
+ );
+ }
}
const environmentFeatures = await Promise.all(
diff --git a/src/lib/features/private-project/createPrivateProjectChecker.ts b/src/lib/features/private-project/createPrivateProjectChecker.ts
index 4844025ca906..7000815dcb47 100644
--- a/src/lib/features/private-project/createPrivateProjectChecker.ts
+++ b/src/lib/features/private-project/createPrivateProjectChecker.ts
@@ -10,9 +10,12 @@ export const createPrivateProjectChecker = (
const { getLogger } = config;
const privateProjectStore = new PrivateProjectStore(db, getLogger);
- return new PrivateProjectChecker({
- privateProjectStore: privateProjectStore,
- });
+ return new PrivateProjectChecker(
+ {
+ privateProjectStore: privateProjectStore,
+ },
+ config,
+ );
};
export const createFakePrivateProjectChecker =
diff --git a/src/lib/features/private-project/fakePrivateProjectChecker.ts b/src/lib/features/private-project/fakePrivateProjectChecker.ts
index 2dba73db6593..f53890847366 100644
--- a/src/lib/features/private-project/fakePrivateProjectChecker.ts
+++ b/src/lib/features/private-project/fakePrivateProjectChecker.ts
@@ -1,9 +1,10 @@
import { IPrivateProjectChecker } from './privateProjectCheckerType';
import { Promise } from 'ts-toolbelt/out/Any/Promise';
+import { ProjectAccess } from './privateProjectStore';
export class FakePrivateProjectChecker implements IPrivateProjectChecker {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- async getUserAccessibleProjects(userId: number): Promise {
+ async getUserAccessibleProjects(userId: number): Promise {
throw new Error('Method not implemented.');
}
diff --git a/src/lib/features/private-project/privateProjectChecker.ts b/src/lib/features/private-project/privateProjectChecker.ts
index daa30a1a51a9..2af389a8f7ba 100644
--- a/src/lib/features/private-project/privateProjectChecker.ts
+++ b/src/lib/features/private-project/privateProjectChecker.ts
@@ -1,26 +1,35 @@
-import { IUnleashStores } from '../../types';
+import { IUnleashConfig, IUnleashStores } from '../../types';
import { IPrivateProjectStore } from './privateProjectStoreType';
import { IPrivateProjectChecker } from './privateProjectCheckerType';
+import { ALL_PROJECT_ACCESS, ProjectAccess } from './privateProjectStore';
export class PrivateProjectChecker implements IPrivateProjectChecker {
private privateProjectStore: IPrivateProjectStore;
- constructor({
- privateProjectStore,
- }: Pick) {
+ private isEnterprise: boolean;
+
+ constructor(
+ { privateProjectStore }: Pick,
+ { isEnterprise }: Pick,
+ ) {
this.privateProjectStore = privateProjectStore;
+ this.isEnterprise = isEnterprise;
}
- async getUserAccessibleProjects(userId: number): Promise {
- return this.privateProjectStore.getUserAccessibleProjects(userId);
+ async getUserAccessibleProjects(userId: number): Promise {
+ return this.isEnterprise
+ ? this.privateProjectStore.getUserAccessibleProjects(userId)
+ : Promise.resolve(ALL_PROJECT_ACCESS);
}
async hasAccessToProject(
userId: number,
projectId: string,
): Promise {
- return (await this.getUserAccessibleProjects(userId)).includes(
- projectId,
+ const projectAccess = await this.getUserAccessibleProjects(userId);
+ return (
+ projectAccess.mode === 'all' ||
+ projectAccess.projects.includes(projectId)
);
}
}
diff --git a/src/lib/features/private-project/privateProjectCheckerType.ts b/src/lib/features/private-project/privateProjectCheckerType.ts
index c8b537a90658..8ddb3b9ce4f9 100644
--- a/src/lib/features/private-project/privateProjectCheckerType.ts
+++ b/src/lib/features/private-project/privateProjectCheckerType.ts
@@ -1,4 +1,6 @@
+import { ProjectAccess } from './privateProjectStore';
+
export interface IPrivateProjectChecker {
- getUserAccessibleProjects(userId: number): Promise;
+ getUserAccessibleProjects(userId: number): Promise;
hasAccessToProject(userId: number, projectId: string): Promise;
}
diff --git a/src/lib/features/private-project/privateProjectStore.ts b/src/lib/features/private-project/privateProjectStore.ts
index 0a3be0b48a6c..9666cfe9e3dd 100644
--- a/src/lib/features/private-project/privateProjectStore.ts
+++ b/src/lib/features/private-project/privateProjectStore.ts
@@ -1,8 +1,20 @@
import { Db } from '../../db/db';
import { Logger, LogProvider } from '../../logger';
import { IPrivateProjectStore } from './privateProjectStoreType';
+import { ADMIN_TOKEN_ID } from '../../types';
-const ADMIN_TOKEN_ID = -1;
+export type ProjectAccess =
+ | {
+ mode: 'all';
+ }
+ | {
+ mode: 'limited';
+ projects: string[];
+ };
+
+export const ALL_PROJECT_ACCESS: ProjectAccess = {
+ mode: 'all',
+};
class PrivateProjectStore implements IPrivateProjectStore {
private db: Db;
@@ -16,10 +28,9 @@ class PrivateProjectStore implements IPrivateProjectStore {
destroy(): void {}
- async getUserAccessibleProjects(userId: number): Promise {
+ async getUserAccessibleProjects(userId: number): Promise {
if (userId === ADMIN_TOKEN_ID) {
- const allProjects = await this.db('projects').pluck('id');
- return allProjects;
+ return ALL_PROJECT_ACCESS;
}
const isViewer = await this.db('role_user')
.join('roles', 'role_user.role_id', 'roles.id')
@@ -32,11 +43,10 @@ class PrivateProjectStore implements IPrivateProjectStore {
.first();
if (!isViewer || isViewer.count == 0) {
- const allProjects = await this.db('projects').pluck('id');
- return allProjects;
+ return ALL_PROJECT_ACCESS;
}
- const accessibleProjects = await this.db
+ const accessibleProjects: string[] = await this.db
.from((db) => {
db.distinct()
.select('projects.id as project_id')
@@ -46,7 +56,15 @@ class PrivateProjectStore implements IPrivateProjectStore {
'projects.id',
'project_settings.project',
)
- .where('project_settings.project_mode', '!=', 'private')
+ .where((builder) => {
+ builder
+ .whereNull('project_settings.project')
+ .orWhere(
+ 'project_settings.project_mode',
+ '!=',
+ 'private',
+ );
+ })
.unionAll((queryBuilder) => {
queryBuilder
.select('projects.id as project_id')
@@ -89,7 +107,7 @@ class PrivateProjectStore implements IPrivateProjectStore {
.select('*')
.pluck('project_id');
- return accessibleProjects;
+ return { mode: 'limited', projects: accessibleProjects };
}
}
diff --git a/src/lib/features/private-project/privateProjectStoreType.ts b/src/lib/features/private-project/privateProjectStoreType.ts
index a554aaab5aea..98b213775f47 100644
--- a/src/lib/features/private-project/privateProjectStoreType.ts
+++ b/src/lib/features/private-project/privateProjectStoreType.ts
@@ -1,3 +1,5 @@
+import { ProjectAccess } from './privateProjectStore';
+
export interface IPrivateProjectStore {
- getUserAccessibleProjects(userId: number): Promise;
+ getUserAccessibleProjects(userId: number): Promise;
}
diff --git a/src/lib/services/client-metrics/instance-service.ts b/src/lib/services/client-metrics/instance-service.ts
index 19cca5a26b9d..f819dc486e47 100644
--- a/src/lib/services/client-metrics/instance-service.ts
+++ b/src/lib/services/client-metrics/instance-service.ts
@@ -184,16 +184,22 @@ export default class ClientInstanceService {
await this.privateProjectChecker.getUserAccessibleProjects(
userId,
);
- return applications.map((application) => {
- return {
- ...application,
- usage: application.usage?.filter(
- (usageItem) =>
- usageItem.project === ALL_PROJECTS ||
- accessibleProjects.includes(usageItem.project),
- ),
- };
- });
+ if (accessibleProjects.mode === 'all') {
+ return applications;
+ } else {
+ return applications.map((application) => {
+ return {
+ ...application,
+ usage: application.usage?.filter(
+ (usageItem) =>
+ usageItem.project === ALL_PROJECTS ||
+ accessibleProjects.projects.includes(
+ usageItem.project,
+ ),
+ ),
+ };
+ });
+ }
}
return applications;
}
diff --git a/src/lib/services/feature-toggle-service.ts b/src/lib/services/feature-toggle-service.ts
index 17b35c25960e..6bb6bd3a6265 100644
--- a/src/lib/services/feature-toggle-service.ts
+++ b/src/lib/services/feature-toggle-service.ts
@@ -1031,11 +1031,15 @@ class FeatureToggleService {
});
if (this.flagResolver.isEnabled('privateProjects') && userId) {
- const projects =
+ const projectAccess =
await this.privateProjectChecker.getUserAccessibleProjects(
userId,
);
- return features.filter((f) => projects.includes(f.project));
+ return projectAccess.mode === 'all'
+ ? features
+ : features.filter((f) =>
+ projectAccess.projects.includes(f.project),
+ );
}
return features;
}
@@ -1860,11 +1864,17 @@ class FeatureToggleService {
): Promise {
const features = await this.featureToggleStore.getAll({ archived });
if (this.flagResolver.isEnabled('privateProjects')) {
- const projects =
+ const projectAccess =
await this.privateProjectChecker.getUserAccessibleProjects(
userId,
);
- return features.filter((f) => projects.includes(f.project));
+ if (projectAccess.mode === 'all') {
+ return features;
+ } else {
+ return features.filter((f) =>
+ projectAccess.projects.includes(f.project),
+ );
+ }
}
return features;
}
diff --git a/src/lib/services/index.ts b/src/lib/services/index.ts
index a3db29e6cfc9..03265589bd72 100644
--- a/src/lib/services/index.ts
+++ b/src/lib/services/index.ts
@@ -201,6 +201,7 @@ export const createServices = (
changeRequestAccessReadModel,
config,
);
+
const privateProjectChecker = db
? createPrivateProjectChecker(db, config)
: createFakePrivateProjectChecker();
diff --git a/src/lib/services/project-service.ts b/src/lib/services/project-service.ts
index 41cf1334a3a6..ba2c76677a61 100644
--- a/src/lib/services/project-service.ts
+++ b/src/lib/services/project-service.ts
@@ -164,13 +164,18 @@ export default class ProjectService {
): Promise {
const projects = await this.store.getProjectsWithCounts(query, userId);
if (this.flagResolver.isEnabled('privateProjects') && userId) {
- const accessibleProjects =
+ const projectAccess =
await this.privateProjectChecker.getUserAccessibleProjects(
userId,
);
- return projects.filter((project) =>
- accessibleProjects.includes(project.id),
- );
+
+ if (projectAccess.mode === 'all') {
+ return projects;
+ } else {
+ return projects.filter((project) =>
+ projectAccess.projects.includes(project.id),
+ );
+ }
}
return projects;
}
diff --git a/src/lib/types/no-auth-user.ts b/src/lib/types/no-auth-user.ts
index fbb65e8cde09..7ceb1fc424fb 100644
--- a/src/lib/types/no-auth-user.ts
+++ b/src/lib/types/no-auth-user.ts
@@ -1,5 +1,6 @@
import { ADMIN } from './permissions';
+export const ADMIN_TOKEN_ID = -1;
export default class NoAuthUser {
isAPI: boolean;
@@ -11,7 +12,7 @@ export default class NoAuthUser {
constructor(
username: string = 'unknown',
- id: number = -1,
+ id: number = ADMIN_TOKEN_ID,
permissions: string[] = [ADMIN],
) {
this.isAPI = true;
diff --git a/src/lib/types/option.ts b/src/lib/types/option.ts
index d6d85358adf9..a948c7e80095 100644
--- a/src/lib/types/option.ts
+++ b/src/lib/types/option.ts
@@ -215,4 +215,5 @@ export interface IUnleashConfig {
prometheusApi?: string;
publicFolder?: string;
disableScheduler?: boolean;
+ isEnterprise: boolean;
}
From d28e7e5a693e2c31320eb8b3fb40000ad7d97c82 Mon Sep 17 00:00:00 2001
From: Bolaji Ayodeji
Date: Fri, 22 Sep 2023 10:06:41 +0100
Subject: [PATCH 10/14] docs: update the bulleted list case in
about-the-docs.md (#4811)
## About the changes
While exploring the [About the docs](https://docs.getunleash.io) page
for the first time, I observed the first bulleted list started with
lowercase, which isn't consistent with other sections of the page. This
minor PR capitalizes the first letter of the bullet lists in the
"Documentation structure" section. This ensures the list follows the
existing style across the page and the stable rule of written language
([reference](https://www.purchase.edu/editorial-style-guide/general-style-preferences/punctuation/bulleted-and-numbered-lists/#C)).
---
website/docs/about-the-docs.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/website/docs/about-the-docs.md b/website/docs/about-the-docs.md
index eeb0a804116a..4ced12d81582 100644
--- a/website/docs/about-the-docs.md
+++ b/website/docs/about-the-docs.md
@@ -14,10 +14,10 @@ Have questions that you can't find the answer to in these docs? You can always t
Our documentation is split into four parts, using the [DiĂĄtaxis documentation framework](https://diataxis.fr/):
-- [tutorials and introductory material](#tutorials)
-- [how-to guides](#how-to-guides)
-- [reference documentation](#reference-documentation)
-- [topic guides](#topic-guides)
+- [Tutorials and introductory material](#tutorials)
+- [How-to guides](#how-to-guides)
+- [Reference documentation](#reference-documentation)
+- [Topic guides](#topic-guides)
### Tutorials and introductory material {#tutorials}
From b4742df8be57417585d6b443f5160eb38451275f Mon Sep 17 00:00:00 2001
From: Mateusz Kwasniewski
Date: Fri, 22 Sep 2023 11:26:45 +0200
Subject: [PATCH 11/14] feat: UI stub for adding dependent features (#4814)
---
.../feature/Dependencies/AddDependency.tsx | 60 +++++++++++++++++++
.../FeatureOverview/FeatureOverview.tsx | 14 +++++
.../useDependentFeaturesApi.ts | 35 +++++++++++
frontend/src/interfaces/uiConfig.ts | 1 +
.../dependent.features.e2e.test.ts | 12 ++--
src/server-dev.ts | 1 +
6 files changed, 117 insertions(+), 6 deletions(-)
create mode 100644 frontend/src/component/feature/Dependencies/AddDependency.tsx
create mode 100644 frontend/src/hooks/api/actions/useDependentFeaturesApi/useDependentFeaturesApi.ts
diff --git a/frontend/src/component/feature/Dependencies/AddDependency.tsx b/frontend/src/component/feature/Dependencies/AddDependency.tsx
new file mode 100644
index 000000000000..be903f089bea
--- /dev/null
+++ b/frontend/src/component/feature/Dependencies/AddDependency.tsx
@@ -0,0 +1,60 @@
+import { Box, styled } from '@mui/material';
+import { trim } from '../../common/util';
+import React, { FC, useState } from 'react';
+import Input from '../../common/Input/Input';
+import { CREATE_FEATURE } from '../../providers/AccessProvider/permissions';
+import PermissionButton from '../../common/PermissionButton/PermissionButton';
+import { useDependentFeaturesApi } from 'hooks/api/actions/useDependentFeaturesApi/useDependentFeaturesApi';
+
+const StyledForm = styled('form')({});
+
+const StyledInputDescription = styled('p')(({ theme }) => ({
+ marginBottom: theme.spacing(1),
+}));
+
+const StyledInput = styled(Input)(({ theme }) => ({
+ marginBottom: theme.spacing(2),
+}));
+
+interface IAddDependencyProps {
+ projectId: string;
+ featureId: string;
+}
+export const AddDependency: FC = ({
+ projectId,
+ featureId,
+}) => {
+ const [parent, setParent] = useState('');
+ const { addDependency } = useDependentFeaturesApi();
+
+ return (
+ {
+ addDependency(featureId, { feature: parent });
+ }}
+ >
+
+ What feature do you want to depend on?
+
+
+ setParent(trim(e.target.value))}
+ />
+ {
+ addDependency(featureId, { feature: parent });
+ }}
+ variant={'outlined'}
+ >
+ Add{' '}
+
+
+
+ );
+};
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverview.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverview.tsx
index 98283955d531..0e61d6b7e1e6 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverview.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverview.tsx
@@ -12,6 +12,9 @@ import { usePageTitle } from 'hooks/usePageTitle';
import { FeatureOverviewSidePanel } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanel';
import { useHiddenEnvironments } from 'hooks/useHiddenEnvironments';
import { styled } from '@mui/material';
+import { AddDependency } from '../../Dependencies/AddDependency';
+import { useUiFlag } from 'hooks/useUiFlag';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
const StyledContainer = styled('div')(({ theme }) => ({
display: 'flex',
@@ -39,6 +42,7 @@ const FeatureOverview = () => {
useHiddenEnvironments();
const onSidebarClose = () => navigate(featurePath);
usePageTitle(featureId);
+ const dependentFeatures = useUiFlag('dependentFeatures');
return (
@@ -50,6 +54,16 @@ const FeatureOverview = () => {
/>
+
+ }
+ />
+
diff --git a/frontend/src/hooks/api/actions/useDependentFeaturesApi/useDependentFeaturesApi.ts b/frontend/src/hooks/api/actions/useDependentFeaturesApi/useDependentFeaturesApi.ts
new file mode 100644
index 000000000000..f0241caeb7ff
--- /dev/null
+++ b/frontend/src/hooks/api/actions/useDependentFeaturesApi/useDependentFeaturesApi.ts
@@ -0,0 +1,35 @@
+import useAPI from '../useApi/useApi';
+
+// TODO: generate from orval
+interface IParentFeaturePayload {
+ feature: string;
+}
+export const useDependentFeaturesApi = () => {
+ const { makeRequest, createRequest, errors, loading } = useAPI({
+ propagateErrors: true,
+ });
+
+ const addDependency = async (
+ childFeature: string,
+ parentFeaturePayload: IParentFeaturePayload
+ ) => {
+ const req = createRequest(
+ `/api/admin/projects/default/features/${childFeature}/dependencies`,
+ {
+ method: 'POST',
+ body: JSON.stringify(parentFeaturePayload),
+ }
+ );
+ try {
+ await makeRequest(req.caller, req.id);
+ } catch (e) {
+ throw e;
+ }
+ };
+
+ return {
+ addDependency,
+ errors,
+ loading,
+ };
+};
diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts
index 290af1e5a628..18ea05fffd0c 100644
--- a/frontend/src/interfaces/uiConfig.ts
+++ b/frontend/src/interfaces/uiConfig.ts
@@ -65,6 +65,7 @@ export type UiFlags = {
variantTypeNumber?: boolean;
privateProjects?: boolean;
accessOverview?: boolean;
+ dependentFeatures?: boolean;
[key: string]: boolean | Variant | undefined;
};
diff --git a/src/lib/features/dependent-features/dependent.features.e2e.test.ts b/src/lib/features/dependent-features/dependent.features.e2e.test.ts
index d1b0503ccebb..15defbadb067 100644
--- a/src/lib/features/dependent-features/dependent.features.e2e.test.ts
+++ b/src/lib/features/dependent-features/dependent.features.e2e.test.ts
@@ -32,13 +32,13 @@ afterAll(async () => {
});
const addFeatureDependency = async (
- parentFeature: string,
+ childFeature: string,
payload: CreateDependentFeatureSchema,
expectedCode = 200,
) => {
return app.request
.post(
- `/api/admin/projects/default/features/${parentFeature}/dependencies`,
+ `/api/admin/projects/default/features/${childFeature}/dependencies`,
)
.send(payload)
.expect(expectedCode);
@@ -51,13 +51,13 @@ test('should add feature dependency', async () => {
await app.createFeature(child);
// save explicit enabled and variants
- await addFeatureDependency(parent, {
- feature: child,
+ await addFeatureDependency(child, {
+ feature: parent,
enabled: false,
});
// overwrite with implicit enabled: true and variants
- await addFeatureDependency(parent, {
- feature: child,
+ await addFeatureDependency(child, {
+ feature: parent,
variants: ['variantB'],
});
});
diff --git a/src/server-dev.ts b/src/server-dev.ts
index 730e3ac25b68..b997164923ec 100644
--- a/src/server-dev.ts
+++ b/src/server-dev.ts
@@ -44,6 +44,7 @@ process.nextTick(async () => {
privateProjects: true,
accessOverview: true,
datadogJsonTemplate: true,
+ dependentFeatures: true,
},
},
authentication: {
From b6b0f83e3dcb956e83233362d7f201def75fb007 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nuno=20G=C3=B3is?=
Date: Fri, 22 Sep 2023 11:04:46 +0100
Subject: [PATCH 12/14] feat: add group-deleted event (#4816)
Adds a missing `group-deleted` event.
---
src/lib/services/group-service.ts | 16 +++++++++++++---
src/lib/types/events.ts | 2 ++
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/src/lib/services/group-service.ts b/src/lib/services/group-service.ts
index 589a5707baca..6e2644686e58 100644
--- a/src/lib/services/group-service.ts
+++ b/src/lib/services/group-service.ts
@@ -11,7 +11,7 @@ import { IUnleashConfig, IUnleashStores } from '../types';
import { IGroupStore } from '../types/stores/group-store';
import { Logger } from '../logger';
import BadDataError from '../error/bad-data-error';
-import { GROUP_CREATED, GROUP_UPDATED } from '../types/events';
+import { GROUP_CREATED, GROUP_DELETED, GROUP_UPDATED } from '../types/events';
import { IEventStore } from '../types/stores/event-store';
import NameExistsError from '../error/name-exists-error';
import { IAccountStore } from '../types/stores/account-store';
@@ -170,8 +170,18 @@ export class GroupService {
return [];
}
- async deleteGroup(id: number): Promise {
- return this.groupStore.delete(id);
+ async deleteGroup(id: number, userName?: string): Promise {
+ const group = await this.groupStore.get(id);
+
+ await this.groupStore.delete(id);
+
+ if (userName) {
+ await this.eventStore.store({
+ type: GROUP_DELETED,
+ createdBy: userName,
+ data: group,
+ });
+ }
}
async validateGroup(
diff --git a/src/lib/types/events.ts b/src/lib/types/events.ts
index 68064d5c2659..0ea5d04e20ad 100644
--- a/src/lib/types/events.ts
+++ b/src/lib/types/events.ts
@@ -90,6 +90,7 @@ export const SEGMENT_UPDATED = 'segment-updated' as const;
export const SEGMENT_DELETED = 'segment-deleted' as const;
export const GROUP_CREATED = 'group-created' as const;
export const GROUP_UPDATED = 'group-updated' as const;
+export const GROUP_DELETED = 'group-deleted' as const;
export const SETTING_CREATED = 'setting-created' as const;
export const SETTING_UPDATED = 'setting-updated' as const;
export const SETTING_DELETED = 'setting-deleted' as const;
@@ -213,6 +214,7 @@ export const IEventTypes = [
SEGMENT_DELETED,
GROUP_CREATED,
GROUP_UPDATED,
+ GROUP_DELETED,
SETTING_CREATED,
SETTING_UPDATED,
SETTING_DELETED,
From 91edae3ccf5ba2c8f523ad56ca8e1d4b671da65f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nuno=20G=C3=B3is?=
Date: Fri, 22 Sep 2023 12:57:09 +0100
Subject: [PATCH 13/14] refactor: contract event group deleted after #4816
(#4817)
As mentioned in
https://github.com/Unleash/unleash/pull/4816#discussion_r1334162284
---
src/lib/services/group-service.ts | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/src/lib/services/group-service.ts b/src/lib/services/group-service.ts
index 6e2644686e58..ea4e652252fa 100644
--- a/src/lib/services/group-service.ts
+++ b/src/lib/services/group-service.ts
@@ -170,18 +170,16 @@ export class GroupService {
return [];
}
- async deleteGroup(id: number, userName?: string): Promise {
+ async deleteGroup(id: number, userName: string): Promise {
const group = await this.groupStore.get(id);
await this.groupStore.delete(id);
- if (userName) {
- await this.eventStore.store({
- type: GROUP_DELETED,
- createdBy: userName,
- data: group,
- });
- }
+ await this.eventStore.store({
+ type: GROUP_DELETED,
+ createdBy: userName,
+ data: group,
+ });
}
async validateGroup(
From 4f5f1f347c72dc66bd42bfeb90fd999714b1b607 Mon Sep 17 00:00:00 2001
From: Michael Ferranti <87366358+ardeche07@users.noreply.github.com>
Date: Fri, 22 Sep 2023 14:09:41 +0200
Subject: [PATCH 14/14] docs: Create feature-flag-best-practices.md (#4804)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## About the changes
Closes #
### Important files
## Discussion points
---------
Co-authored-by: Ivar Conradi Ăsthus
---
.../availability-over-consistency.md | 21 ++++++++++
.../democratize-feature-flag-access.md | 31 +++++++++++++++
.../feature-flags/enable-traceability.md | 35 +++++++++++++++++
.../evaluate-flags-close-to-user.md | 25 ++++++++++++
.../feature-flag-best-practices.md | 35 +++++++++++++++++
.../topics/feature-flags/limit-payloads.md | 38 ++++++++++++++++++
.../topics/feature-flags/never-expose-pii.md | 39 +++++++++++++++++++
.../topics/feature-flags/prioritize-ux.md | 31 +++++++++++++++
.../topics/feature-flags/runtime-control.md | 23 +++++++++++
.../feature-flags/scale-horizontally.md | 31 +++++++++++++++
.../short-lived-feature-flags.md | 20 ++++++++++
.../docs/topics/feature-flags/unique-names.md | 15 +++++++
website/sidebars.js | 25 +++++++++++-
13 files changed, 367 insertions(+), 2 deletions(-)
create mode 100644 website/docs/topics/feature-flags/availability-over-consistency.md
create mode 100644 website/docs/topics/feature-flags/democratize-feature-flag-access.md
create mode 100644 website/docs/topics/feature-flags/enable-traceability.md
create mode 100644 website/docs/topics/feature-flags/evaluate-flags-close-to-user.md
create mode 100644 website/docs/topics/feature-flags/feature-flag-best-practices.md
create mode 100644 website/docs/topics/feature-flags/limit-payloads.md
create mode 100644 website/docs/topics/feature-flags/never-expose-pii.md
create mode 100644 website/docs/topics/feature-flags/prioritize-ux.md
create mode 100644 website/docs/topics/feature-flags/runtime-control.md
create mode 100644 website/docs/topics/feature-flags/scale-horizontally.md
create mode 100644 website/docs/topics/feature-flags/short-lived-feature-flags.md
create mode 100644 website/docs/topics/feature-flags/unique-names.md
diff --git a/website/docs/topics/feature-flags/availability-over-consistency.md b/website/docs/topics/feature-flags/availability-over-consistency.md
new file mode 100644
index 000000000000..4005db315845
--- /dev/null
+++ b/website/docs/topics/feature-flags/availability-over-consistency.md
@@ -0,0 +1,21 @@
+---
+title: 6. Design for failure. Favor availability over consistency.
+---
+
+Your feature flag system should not be able to take down your main application under any circumstance, including network disruptions. Follow these patterns to achieve fault tolerance for your feature flag system.
+
+**Zero dependencies**: Your application's availability should have zero dependencies on the availability of your feature flag system. Robust feature flag systems avoid relying on real-time flag evaluations because the unavailability of the feature flag system will cause application downtime, outages, degraded performance, or even a complete failure of your application.
+
+ **Graceful degradation**: If the system goes down, it should not disrupt the user experience or cause unexpected behavior. Feature flagging should gracefully degrade in the absence of the Feature Flag Control service, ensuring that users can continue to use the application without disruption.
+
+**Resilient Architecture Patterns**:
+
+ - **Bootstrapping SDKs with Data**: Feature flagging SDKs used within your application should be designed to work with locally cached data, even when the network connection to the Feature Flag Control service is unavailable. The SDKs can bootstrap with the last known feature flag configuration or default values to ensure uninterrupted functionality.
+
+ - **Local Cache**: Maintaining a local cache of feature flag configuration helps reduce network round trips and dependency on external services. The local cache can be periodically synchronized with the central Feature Flag Control service when it's available. This approach minimizes the impact of network failures or service downtime on your application.
+
+ - **Evaluate Locally**: Whenever possible, the SDKs or application components should be able to evaluate feature flags locally without relying on external services. This ensures that feature flag evaluations continue even when the feature flagging service is temporarily unavailable.
+
+ - **Availability Over Consistency**: As the CAP theorem teaches us, in distributed systems, prioritizing availability over strict consistency can be a crucial design choice. This means that, in the face of network partitions or downtime of external services, your application should favor maintaining its availability rather than enforcing perfectly consistent feature flag configuration caches. Eventually consistent systems can tolerate temporary inconsistencies in flag evaluations without compromising availability. In CAP theorem parlance, a feature flagging system should aim for AP over CP.
+
+By implementing these resilient architecture patterns, your feature flagging system can continue to function effectively even in the presence of downtime or network disruptions in the feature flagging service. This ensures that your main application remains stable, available, and resilient to potential issues in the feature flagging infrastructure, ultimately leading to a better user experience and improved reliability.
diff --git a/website/docs/topics/feature-flags/democratize-feature-flag-access.md b/website/docs/topics/feature-flags/democratize-feature-flag-access.md
new file mode 100644
index 000000000000..354fdb1e0528
--- /dev/null
+++ b/website/docs/topics/feature-flags/democratize-feature-flag-access.md
@@ -0,0 +1,31 @@
+---
+title: 9. Choose open by default. Democratize feature flag access.
+---
+
+Allowing engineers, product owners, and even technical support to have open access to a feature flagging system is essential for effective development, debugging, and decision-making. These groups should have access to the system, along with access to the codebase and visibility into configuration changes:
+
+1. **Debugging and Issue Resolution**:
+
+ - **Code Access**: Engineers should have access to the codebase where feature flags are implemented. This access enables them to quickly diagnose and fix issues related to feature flags when they arise. Without code access, debugging becomes cumbersome, and troubleshooting becomes slower, potentially leading to extended downtimes or performance problems.
+
+2. **Visibility into Configuration**:
+
+ - **Configuration Transparency**: Engineers, product owners, and even technical support should be able to view the feature toggle configuration. This transparency provides insights into which features are currently active, what conditions trigger them, and how they impact the application's behavior. It helps understand the system's state and behavior, which is crucial for making informed decisions.
+
+ - **Change History**: Access to a history of changes made to feature flags, including who made the changes and when, is invaluable. This audit trail allows teams to track changes to the system's behavior over time. It aids in accountability and can be instrumental in troubleshooting when unexpected behavior arises after a change.
+
+ - **Correlating Changes with Metrics**: Engineers and product owners often need to correlate feature flag changes with production application metrics. This correlation helps them understand how feature flags affect user behavior, performance, and system health. It's essential for making data-driven decisions about feature rollouts, optimizations, or rollbacks.
+
+3. **Collaboration**:
+
+ - **Efficient Communication**: Open access fosters efficient communication between engineers and the rest of the organization. When it's open by default, everyone can see the feature flagging system and its changes, and have more productive discussions about feature releases, experiments, and their impact on the user experience.
+
+4. **Empowering Product Decisions**:
+
+ - **Product Owner Involvement**: Product owners play a critical role in defining feature flags' behavior and rollout strategies based on user needs and business goals. Allowing them to access the feature flagging system empowers them to make real-time decisions about feature releases, rollbacks, or adjustments without depending solely on engineering resources.
+
+5. **Security and Compliance**:
+
+ - **Security Audits**: Users of a feature flag system should be part of corporate access control groups such as SSO. Sometimes, additional controls are necessary, such as feature flag approvals using the four-eyes principle.
+
+Access control and visibility into feature flag changes are essential for security and compliance purposes. It helps track and audit who has made changes to the system, which can be crucial in maintaining data integrity and adhering to regulatory requirements.
diff --git a/website/docs/topics/feature-flags/enable-traceability.md b/website/docs/topics/feature-flags/enable-traceability.md
new file mode 100644
index 000000000000..44c7c71d4089
--- /dev/null
+++ b/website/docs/topics/feature-flags/enable-traceability.md
@@ -0,0 +1,35 @@
+---
+title: 11. Enable traceability. Make it easy to understand flag evaluation.
+---
+
+Developer experience is a critical factor to consider when implementing a feature flag solution. A positive developer experience enhances the efficiency of the development process and contributes to the overall success and effectiveness of feature flagging. One crucial aspect of developer experience is ensuring the testability of the SDK and providing tools for developers to understand how and why feature flags are evaluated. This is important because:
+
+1. **Ease of Testing and Debugging:**
+
+ - **Faster Development Cycles:** A feature flagging solution with a testable SDK allows developers to quickly test and iterate on new features. They can easily turn flags on or off, simulate different conditions, and observe the results without needing extensive code changes or redeployments.
+
+ - **Rapid Issue Resolution:** When issues or unexpected behavior arise, a testable SDK enables developers to pinpoint the problem more efficiently. They can examine the flag configurations, log feature flag decisions, and troubleshoot issues more precisely.
+
+2. **Visibility into Flag Behaviour:**
+
+ - **Understanding User Experience:** Developers need tools to see and understand how feature flags affect the user experience. This visibility helps them gauge the impact of flag changes and make informed decisions about when to roll out features to different user segments. Debugging a feature flag with multiple inputs simultaneously makes it easy for developers to compare the results and quickly figure out how a feature flag evaluates in different scenarios with multiple input values.
+
+ - **Enhanced Collaboration:** Feature flagging often involves cross-functional teams, including developers, product managers, and QA testers. Providing tools with a clear view of flag behavior fosters effective collaboration and communication among team members.
+
+3. **Transparency and Confidence:**
+
+ - **Confidence in Flag Decisions:** A transparent feature flagging solution empowers developers to make data-driven decisions. They can see why a particular flag evaluates to a certain value, which is crucial for making informed choices about feature rollouts and experimentation.
+
+ - **Reduced Risk:** When developers clearly understand of why flags evaluate the way they do, they are less likely to make unintentional mistakes that could lead to unexpected issues in production.
+
+4. **Effective Monitoring and Metrics:**
+
+ - **Tracking Performance:** A testable SDK should provide developers with the ability to monitor the performance of feature flags in real time. This includes tracking metrics related to flag evaluations, user engagement, and the impact of flag changes.
+
+ - **Data-Driven Decisions:** Developers can use this data to evaluate the success of new features, conduct A/B tests, and make informed decisions about optimizations.
+
+ - **Usage metrics:** A feature flag system should provide insight on an aggregated level about the usage of feature flags. This is helpful for developers so that they can easily assess that everything works as expected.
+
+5. **Documentation and Training:**
+
+ - **Onboarding and Training:** The entire feature flag solution, including API, UI, and the SDKs, requires clear and comprehensive documentation, along with easy-to-understand examples, in order to simplify the onboarding process for new developers. It also supports the ongoing training of new team members, ensuring that everyone can effectively use the feature flagging solution.
diff --git a/website/docs/topics/feature-flags/evaluate-flags-close-to-user.md b/website/docs/topics/feature-flags/evaluate-flags-close-to-user.md
new file mode 100644
index 000000000000..c48b3b025106
--- /dev/null
+++ b/website/docs/topics/feature-flags/evaluate-flags-close-to-user.md
@@ -0,0 +1,25 @@
+---
+title: 3. Evaluate flags as close to the user as possible. Reduce latency.
+---
+
+Feature flags should be evaluated as close to your users as possible, and the evaluation should always happen server side as discussed in Principle 2. In addition to security and privacy benefits, performing evaluation as close as possible to your users has multiple benefits:
+
+1. **Performance Efficiency**:
+
+ a. **Reduced Latency**: Network roundtrips introduce latency, which can slow down your application's response time. Local evaluation eliminates the need for these roundtrips, resulting in faster feature flag decisions. Users will experience a more responsive application, which can be critical for maintaining a positive user experience.
+
+ b. **Offline Functionality**: Applications often need to function offline or in low-connectivity environments. Local evaluation ensures that feature flags can still be used and decisions can be made without relying on a network connection. This is especially important for mobile apps or services in remote locations.
+
+2. **Cost Savings**:
+
+ a. **Reduced Bandwidth Costs**: Local evaluation reduces the amount of data transferred between your application and the feature flag service. This can lead to significant cost savings, particularly if you have a large user base or high traffic volume.
+
+3. **Offline Development and Testing**:
+
+ a. **Development and Testing**: Local evaluation is crucial for local development and testing environments where a network connection to the feature flag service might not be readily available. Developers can work on feature flag-related code without needing constant access to the service, streamlining the development process.
+
+4. **Resilience**:
+
+ a. **Service Outages**: If the feature flag service experiences downtime or outages, local evaluation allows your application to continue functioning without interruptions. This is important for maintaining service reliability and ensuring your application remains available even when the service is down.
+
+In summary, this principle emphasizes the importance of optimizing performance while protecting end-user privacy by evaluating feature flags as close to the end user as possible. Done right, this also leads to a highly available feature flag system that scales with your applications.
diff --git a/website/docs/topics/feature-flags/feature-flag-best-practices.md b/website/docs/topics/feature-flags/feature-flag-best-practices.md
new file mode 100644
index 000000000000..a3c88473e4c6
--- /dev/null
+++ b/website/docs/topics/feature-flags/feature-flag-best-practices.md
@@ -0,0 +1,35 @@
+---
+title: 11 Principles for building and scaling feature flag systems
+---
+
+Feature flags, sometimes called feature toggles or feature switches, are a software development technique that allows engineering teams to decouple the release of new functionality from software deployments. With feature flags, developers can turn specific features or code segments on or off at runtime, without the need for a code deployment or rollback. Organizations who adopt feature flags see improvements in all key operational metrics for DevOps: Lead time to changes, mean-time-to-recovery, deployment frequency, and change failure rate.
+
+There are 11 principles for building a large-scale feature flag system. These principles have their roots in distributed systems architecture and pay particular attention to security, privacy, and scale that is required by most enterprise systems. If you follow these principles, your feature flag system is less likely to break under load and will be easier to evolve and maintain.
+
+These principles are:
+
+1. [Enable run-time control. Control flags dynamically, not using config files.](./runtime-control.md)
+2. [Never expose PII. Follow the principle of least privilege.](./never-expose-pii.md)
+3. [Evaluate flags as close to the user as possible. Reduce latency.](./evaluate-flags-close-to-user.md)
+4. [Scale Horizontally. Decouple reading and writing flags.](./scale-horizontally.md)
+5. [Limit payloads. Feature flag payload should be as small as possible.](./limit-payloads.md)
+6. [Design for failure. Favor availability over consistency.](./availability-over-consistency.md)
+7. [Make feature flags short-lived. Do not confuse flags with application configuration.](./short-lived-feature-flags.md)
+8. [Use unique names across all applications. Enforce naming conventions.](./unique-names.md)
+9. [Choose open by default. Democratize feature flag access.](./democratize-feature-flag-access.md)
+10. [Do no harm. Prioritize consistent user experience.](./prioritize-ux.md)
+11. [Enable traceability. Make it easy to understand flag evaluation](./enable-traceability.md)
+
+## Background
+
+Feature flags have become a central part of the DevOps toolbox along with Git, CI/CD and microservices. You can write modern software without all of these things, but it sure is a lot harder, and a lot less fun.
+
+And just like the wrong Git repo design can cause interminable headaches, getting the details wrong when first building a feature flag system can be very costly.
+
+This set of principles for building a large-scale feature management platform is the result of thousands of hours of work building and scaling Unleash, an open-source feature management solution used by thousands of organizations.
+
+Before Unleash was a community and a company, it was an internal project, started by [one dev](https://github.com/ivarconr), for one company. As the community behind Unleash grew, patterns and anti-patterns of large-scale feature flag systems emerged. Our community quickly discovered that these are important principles for anyone who wanted to avoid spending weekends debugging the production system that is supposed to make debugging in production easier.
+
+âLarge scaleâ means the ability to support millions of flags served to end-users with minimal latency or impact on application uptime or performance. That is the type of system most large enterprises are building today and the type of feature flag system that this guide focuses on.
+
+Our motivation for writing these principles is to share what weâve learned building a large-scale feature flag solution with other architects and engineers solving similar challenges. Unleash is open-source, and so are these principles. Have something to contribute? [Open a PR](https://github.com/Unleash/unleash/pulls) or [discussion](https://github.com/orgs/Unleash/discussions) on our Github.
diff --git a/website/docs/topics/feature-flags/limit-payloads.md b/website/docs/topics/feature-flags/limit-payloads.md
new file mode 100644
index 000000000000..e45b966b9ecd
--- /dev/null
+++ b/website/docs/topics/feature-flags/limit-payloads.md
@@ -0,0 +1,38 @@
+---
+title: 5. Limit payloads. Feature flag payload should be as small as possible.
+---
+
+Minimizing the size of feature flag payloads is a critical aspect of maintaining the efficiency and performance of a feature flag system. The configuration of your feature flags can vary in size depending on the complexity of your targeting rules. For instance, if you have a targeting engine that determines whether a feature flag should be active or inactive based on individual user IDs, you might be tempted to include all these user IDs within the configuration payload. While this approach may work fine for a small user base, it can become unwieldy when dealing with a large number of users.
+
+If you find yourself facing this challenge, your instinct might be to store this extensive user information directly in the feature flagging system. However, this can also run into scaling problems. A more efficient approach is to categorize these users into logical groupings at a different layer and then use these group identifiers when you evaluate flags within your feature flagging system. For example, you can group users based on their subscription plan or geographical location. Find a suitable parameter for grouping users, and employ those group parameters as targeting rules in your feature flagging solution.
+
+Imposing limitations on payloads is crucial for scaling a feature flag system:
+
+1. **Reduced Network Load**:
+
+ - Large payloads, especially for feature flag evaluations, can lead to increased network traffic between the application and the feature flagging service. This can overwhelm the network and cause bottlenecks, leading to slow response times and degraded system performance. Limiting payloads helps reduce the amount of data transferred over the network, alleviating this burden. Even small numbers become large when multiplied by millions.
+
+2. **Faster Evaluation**:
+
+ - Smaller payloads reduce latency which means quicker transmission and evaluation. Speed is essential when evaluating feature flags, especially for real-time decisions that impact user experiences. Limiting payloads ensures evaluations occur faster, allowing your application to respond promptly to feature flag changes.
+
+3. **Improved Memory Efficiency**:
+
+ - Feature flagging systems often store flag configurations in memory for quick access during runtime. Larger payloads consume more memory, potentially causing memory exhaustion and system crashes. By limiting payloads, you ensure that the system remains memory-efficient, reducing the risk of resource-related issues.
+
+4. **Scalability**:
+
+ - Scalability is a critical concern for modern applications, especially those experiencing rapid growth. Feature flagging solutions need to scale horizontally to accommodate increased workloads. Smaller payloads require fewer resources for processing, making it easier to scale your system horizontally.
+
+5. **Lower Infrastructure Costs**:
+
+ - When payloads are limited, the infrastructure required to support the feature flagging system can be smaller and less costly. This saves on infrastructure expenses and simplifies the management and maintenance of the system.
+
+
+6. **Reliability**:
+
+ - A feature flagging system that consistently delivers small, manageable payloads is more likely to be reliable. It reduces the risk of network failures, timeouts, and other issues when handling large data transfers. Reliability is paramount for mission-critical applications.
+
+7. **Ease of Monitoring and Debugging**:
+
+ - Smaller payloads are easier to monitor and debug. When issues arise, it's simpler to trace problems and identify their root causes when dealing with smaller, more manageable data sets.
diff --git a/website/docs/topics/feature-flags/never-expose-pii.md b/website/docs/topics/feature-flags/never-expose-pii.md
new file mode 100644
index 000000000000..d16e5791aa7e
--- /dev/null
+++ b/website/docs/topics/feature-flags/never-expose-pii.md
@@ -0,0 +1,39 @@
+---
+title: 2. Never expose PII. Follow the principle of least privilege.
+---
+
+To keep things simple, you may be tempted to evaluate the feature flags in your Feature Flag Control Service. Donât. Your Feature Flag Control Service should only handle the configuration for your feature flags and pass this configuration down to SDKs connecting from your applications.
+
+The primary rationale behind this practice is that feature flags often require contextual data for accurate evaluation. This may include user IDs, email addresses, or geographical locations that influence whether a flag should be toggled on or off. Safeguarding this sensitive information from external exposure is paramount. This information may include Personally Identifiable Information (PII), which must remain confined within the boundaries of your application, following the data security principle of least privilege (PoLP).
+
+![feature-flag-server-side-evaluation](https://github.com/Unleash/unleash/assets/87366358/6ca24aea-26fb-481d-92ff-a9af2729cb83)
+
+For client-side applications where the code resides on the user's machine, such as in the browser or on mobile devices, youâll want to take a different approach. You canât evaluate on the client side because it raises significant security concerns by exposing potentially sensitive information such as API keys, flag data, and flag configurations. Placing these critical elements on the client side increases the risk of unauthorized access, tampering, or data breaches.
+
+Instead of performing client-side evaluation, a more secure and maintainable approach is to conduct feature flag evaluation within a self-hosted environment. Doing so can safeguard sensitive elements like API keys and flag configurations from potential client-side exposure. This strategy involves a server-side evaluation of feature flags, where the server makes decisions based on user and application parameters and then securely passes down the evaluated results to the frontend without any configuration leaking.
+
+![feature-flag-architecture-client-side](https://github.com/Unleash/unleash/assets/87366358/28240019-ea3c-4ae6-a6da-60ad9079dc54)
+
+Hereâs how you can architect your solution to minimize PII or configuration leakage:
+
+1. **Server-Side Components**:
+
+In Principle 1, we proposed a set of architectural principles and components to set up a Feature Flag Control Service. The same architecture patterns apply here, with additional suggestions for achieving local evaluation. Refer to Principle 1 for patterns to set up a feature flagging service.
+
+**Feature Flag Evaluation Service**: If you need to use feature flags on the client side, where code is delivered to users' devices, youâll need an evaluation server that can evaluate feature flags and pass evaluated results down to the SDK in the client application.
+
+2. **SDKs**:
+
+SDKs will make it more comfortable to work with feature flags. Depending on the context of your infrastructure, you need different types of SDKs to talk to your feature flagging service. For the server side, youâll need SDKs that can talk directly to the feature flagging service and fetch the configuration.
+
+The server-side SDKs should implement logic to evaluate feature flags based on the configuration received from the Feature Flag Control Service and the application-specific context. Local evaluation ensures that decisions are made quickly without relying on network roundtrips.
+
+For client-side feature flags, youâll need a different type of SDK. These SDKs will send the context to the Feature Flag Evaluation Service and receive the evaluated results. These results should be stored in memory and used when doing a feature flag lookup in the client-side application. By keeping the evaluated results for a specific context in memory in the client-side application, you avoid network roundtrips every time your application needs to check the status of a feature flag. It achieves the same level of performance as a server-side SDK, but the content stored in memory is different and limited to evaluated results on the client.
+
+The benefits of this approach include:
+
+ **Privacy Protection**:
+
+ a. **Data Minimization**: By evaluating feature flags in this way, you minimize the amount of data that needs to be sent to the Feature Flag Control Service. This can be crucial for protecting user privacy, as less user-specific data is transmitted over the network.
+
+ b. **Reduced Data Exposure**: Sensitive information about your users or application's behavior is less likely to be exposed to potential security threats. Data breaches or leaks can be mitigated by limiting the transmission of sensitive data.
diff --git a/website/docs/topics/feature-flags/prioritize-ux.md b/website/docs/topics/feature-flags/prioritize-ux.md
new file mode 100644
index 000000000000..5e8ce855fa8b
--- /dev/null
+++ b/website/docs/topics/feature-flags/prioritize-ux.md
@@ -0,0 +1,31 @@
+---
+title: 10. Do no harm. Prioritize consistent user experience.
+---
+
+Feature flagging solutions are indispensable tools in modern software development, enabling teams to manage feature releases and experiment with new functionality. However, one aspect that is absolutely non-negotiable in any feature flag solution is the need to ensure a consistent user experience. This isn't a luxury; it's a fundamental requirement. Feature flagging solutions must prioritize consistency and guarantee the same user experience every time, especially with percentage-based gradual rollouts.
+
+**Why Consistency is Paramount:**
+
+1. **User Trust**: Consistency breeds trust. When users interact with an application, they form expectations about how it behaves. Any sudden deviations can erode trust and lead to a sense of unreliability.
+
+2. **Reduced Friction**: Consistency reduces friction. Users shouldn't have to relearn how to use an app every time they open it. A consistent experience reduces the cognitive load on users, enabling them to engage effortlessly.
+
+3. **Quality Assurance**: Maintaining a consistent experience makes quality assurance more manageable. It's easier to test and monitor when you have a reliable benchmark for the user experience.
+
+4. **Support and Feedback**: Inconsistent experiences lead to confused users, increased support requests, and muddied user feedback. Consistency ensures that user issues are easier to identify and address.
+
+5. **Brand Integrity**: A consistent experience reflects positively on your brand. It demonstrates professionalism and commitment to user satisfaction, enhancing your brand's reputation.
+
+**Strategies for Consistency in Percentage-Based Gradual Rollouts:**
+
+1. **User Hashing**: Assign users to consistent groups using a secure hashing algorithm based on unique identifiers like user IDs or emails. This ensures that the same user consistently falls into the same group.
+
+2. **Segmentation Control**: Provide controls within the feature flagging tool to allow developers to segment users logically. For instance, segment by location, subscription type, or any relevant criteria to ensure similar user experiences.
+
+3. **Fallback Mechanisms**: Include fallback mechanisms in your architecture. If a user encounters issues or inconsistencies, the system can automatically switch them to a stable version or feature state.
+
+4. **Logging and Monitoring**: Implement robust logging and monitoring. Continuously track which users are in which groups and what version of the feature they are experiencing. Monitor for anomalies or deviations and consider building automated processes to disable features that may be misbehaving.
+
+5. **Transparent Communication**: Clearly communicate the gradual rollout to users. Use in-app notifications, tooltips, or changelogs to inform users about changes, ensuring they know what to expect.
+
+In summary, consistency is a cornerstone of effective feature flagging solutions. When designing an architecture for percentage-based gradual rollouts, prioritize mechanisms that guarantee the same user gets the same experience every time. This isn't just about good software practice; it's about respecting your users and upholding their trust in your application. By implementing these strategies, you can create a feature flagging solution that empowers your development process and delights your users with a dependable and consistent experience.
diff --git a/website/docs/topics/feature-flags/runtime-control.md b/website/docs/topics/feature-flags/runtime-control.md
new file mode 100644
index 000000000000..e95747a81c8e
--- /dev/null
+++ b/website/docs/topics/feature-flags/runtime-control.md
@@ -0,0 +1,23 @@
+---
+title: 1. Enable run-time control. Control flags dynamically, not using config files.
+---
+
+A scalable feature management system evaluates flags at runtime. Flags are dynamic, not static. If you need to restart your application to turn on a flag, you are using configuration, not feature flags.
+
+A large-scale feature flag system that enables runtime control should have at minimum the following components:
+
+**1. Feature Flag Control Service**: Use a centralized feature flag service that acts as the control plane for your feature flags. This service will handle flag configuration. The scope of this service should reflect the boundaries of your organization.
+
+Independent business units or product lines should potentially have their own instances, while business units or product lines that work closely together should most likely use the same instance in order to facilitate collaboration. This will always be a contextual decision based on your organization and how you organize the work, but keep in mind that youâd like to keep the management of the flags as simple as possible to avoid the complexity of cross-instance synchronization of feature flag configuration.
+
+**2. Database or Data Store**: Use a robust and scalable database or data store to store feature flag configurations. Popular choices include SQL or NoSQL databases or key-value stores. Ensure that this store is highly available and reliable.
+
+**3. API Layer**: Develop an API layer that exposes endpoints for your application to interact with the Feature Flag Control Service. This API should allow your application to request feature flag configurations.
+
+**4. Feature Flag SDK**: Build or integrate a feature flag SDK into your application. This SDK should provide an easy-to-use interface for fetching flag configurations and evaluating feature flags at runtime. When evaluating feature flags in your application, the call to the SDK should query the local cache, and the SDK should ask the central service for updates in the background.
+
+Build SDK bindings for each relevant language in your organization. Make sure that the SDKs uphold a standard contract governed by a set of feature flag client specifications that documents what functionality each SDK should support.
+
+![feature-flag-scalable-architecture](https://github.com/Unleash/unleash/assets/87366358/e0ac76e0-992c-49ef-80be-70d2971ed937)
+
+**5. Continuously Updated**: Implement update mechanisms in your application so that changes to feature flag configurations are reflected without requiring application restarts or redeployments. The SDK should handle subscriptions or polling to the feature flag service for updates.
diff --git a/website/docs/topics/feature-flags/scale-horizontally.md b/website/docs/topics/feature-flags/scale-horizontally.md
new file mode 100644
index 000000000000..7262f2dde896
--- /dev/null
+++ b/website/docs/topics/feature-flags/scale-horizontally.md
@@ -0,0 +1,31 @@
+---
+title: 4. Scale Horizontally. Decouple reading and writing flags.
+---
+
+Separating the reading and writing of feature flags into distinct APIs is a critical architectural decision for building a scalable and efficient feature flag system, particularly when considering horizontal scaling. This separation provides several benefits:
+
+![feature-flag-horizontal-scaling](https://github.com/Unleash/unleash/assets/87366358/92135dea-917a-45c8-87e4-515b2a48daa9)
+
+1. **Horizontal Scaling**:
+
+ - By separating read and write APIs, you can horizontally scale each component independently. This enables you to add more servers or containers to handle increased traffic for reading feature flags, writing updates, or both, depending on the demand.
+
+2. **Caching Efficiency**:
+
+ - Feature flag systems often rely on caching to improve response times for flag evaluations. Separating read and write APIs allows you to optimize caching strategies independently. For example, you can cache read operations more aggressively to minimize latency during flag evaluations while still ensuring that write operations maintain consistency across the system.
+
+3. **Granular Access Control**:
+
+ - Separation of read and write APIs simplifies access control and permissions management. You can apply different security measures and access controls to the two APIs. This helps ensure that only authorized users or systems can modify feature flags, reducing the risk of accidental or unauthorized changes.
+
+4. **Better Monitoring and Troubleshooting**:
+
+ - Monitoring and troubleshooting become more straightforward when read and write operations are separated. It's easier to track and analyze the performance of each API independently. When issues arise, you can isolate the source of the problem more quickly and apply targeted fixes or optimizations.
+
+5. **Flexibility and Maintenance**:
+
+ - Separation of concerns makes your system more flexible and maintainable. Changes or updates to one API won't directly impact the other, reducing the risk of unintended consequences. This separation allows development teams to work on each API separately, facilitating parallel development and deployment cycles.
+
+6. **Load Balancing**:
+
+ - Load balancing strategies can be tailored to the specific needs of the read and write APIs. You can distribute traffic and resources accordingly to optimize performance and ensure that neither API becomes a bottleneck under heavy loads.
diff --git a/website/docs/topics/feature-flags/short-lived-feature-flags.md b/website/docs/topics/feature-flags/short-lived-feature-flags.md
new file mode 100644
index 000000000000..ea0ee43a37bc
--- /dev/null
+++ b/website/docs/topics/feature-flags/short-lived-feature-flags.md
@@ -0,0 +1,20 @@
+---
+title: 7. Make feature flags short-lived. Do not confuse flags with application configuration.
+---
+
+Feature flags have a lifecycle shorter than an application lifecycle. The most common use case for feature flags is to protect new functionality. That means that when the roll-out of new functionality is complete, the feature flag should be removed from the code and archived. If there were old code paths that the new functionality replaces, those should also be cleaned up and removed.
+
+Feature flags should not be used for static application configuration. Application configuration is expected to be consistent, long-lived, and read when launching an application. Using feature flags to configure an application can lead to inconsistencies between different instances of the same application. Feature flags, on the other hand, are designed to be short-lived, dynamic, and changed at runtime. They are expected to be read and updated at runtime and favor availability over consistency.
+
+To succeed with feature flags in a large organization, you should:
+
+ - **Use flag expiration dates**: By setting expiration dates for your feature flags, you make it easier to keep track of old feature flags that are no longer needed. A proper feature flag solution will inform you about potentially expired flags.
+
+ - **Treat feature flags like technical debt.**: You must plan to clean up old feature branches in sprint or project planning, the same way you plan to clean up technical debt in your code. Feature flags add complexity to your code. Youâll need to know what code paths the feature flag enables, and while the feature flag lives, the context of it needs to be maintained and known within the organization. If you donât clean up feature flags, eventually, you may lose the context surrounding it if enough time passes and/or personnel changes happen. As time passes, you will find it hard to remove flags, or to operate them effectively.
+
+- **Archive old flags**: Feature flags that are no longer in use should be archived after their usage has been removed from the codebase. The archive serves as an important audit log of feature flags that are no longer in use, and allows you to revive them if you need to install an older version of your application.
+
+There are valid exceptions to short-lived feature flags. In general, you should try to limit the amount of long-lived feature flags. Some examples include:
+
+* Kill-switches - these work like an inverted feature flag and are used to gracefully disable part of a system with known weak spots.
+* Internal flags used to enable additional debugging, tracing, and metrics at runtime, which are too costly to run all the time. These can be enabled by software engineers while debugging issues.
diff --git a/website/docs/topics/feature-flags/unique-names.md b/website/docs/topics/feature-flags/unique-names.md
new file mode 100644
index 000000000000..36dcd9fdfe1f
--- /dev/null
+++ b/website/docs/topics/feature-flags/unique-names.md
@@ -0,0 +1,15 @@
+---
+title: 8. Use unique names across all applications. Enforce naming conventions.
+---
+
+All flags served by the same Feature Flag Control service should have unique names across the entire cluster to avoid inconsistencies and errors.
+
+* **Avoid zombies:** Uniqueness should be controlled using a global list of feature flag names. This prevents the reuse of old flag names to protect new features. Using old names can lead to accidental exposure of old features, still protected with the same feature flag name.
+* **Naming convention enforcement: **Ideally, unique names are enforced at creation time. In a large organization, it is impossible for all developers to know all flags used. Enforcing a naming convention makes naming easier, ensures consistency, and provides an easy way to check for uniqueness.
+
+Unique naming has the following advantages:
+
+* **Flexibility over time: **Large enterprise systems are not static. Over time, monoliths are split into microservices, microservices are merged into larger microservices, and applications change responsibility. This means that the way flags are grouped will change over time, and a unique name for the entire organization ensures that you keep the option to reorganize your flags to match the changing needs of your organization.
+* **Prevent conflicts**: If two applications use the same Feature Flag name it can be impossible to know which flag is controlling which applications. This can lead to accidentally flipping the wrong flag, even if they are separated into different namespaces (projects, workspaces etc).
+* **Easier to manage: **It's easier to know what a flag is used for and where it is being used when it has a unique name. E.g. It will be easier to search across multiple code bases to find references for a feature flag when it has a unique identifier across the entire organization.
+* **Enables collaboration:** When a feature flag has a unique name in the organization, it simplifies collaboration across teams, products and applications. It ensures that we all talk about the same feature.
diff --git a/website/sidebars.js b/website/sidebars.js
index 3a38a2c21f08..2db720b72c2f 100644
--- a/website/sidebars.js
+++ b/website/sidebars.js
@@ -28,7 +28,7 @@ module.exports = {
'tutorials/quickstart',
],
},
- {
+ {
label: 'Topic guides',
collapsed: false,
type: 'category',
@@ -36,7 +36,7 @@ module.exports = {
type: 'generated-index',
title: 'Topic guides',
description:
- 'Discussions, explanations, and explorations regarding topics related to Unleash.',
+ 'Discussions, explanations, and explorations regarding topics related to feature flags and Unleash.',
slug: '/topics',
},
items: [
@@ -45,6 +45,27 @@ module.exports = {
'topics/managing-constraints',
'topics/proxy-hosting',
'topics/data-collection',
+ {
+ type: 'category',
+ label: '11 Principles for building and scaling feature flag systems',
+ link: {
+ type: 'doc',
+ id: 'topics/feature-flags/feature-flag-best-practices',
+ },
+ items: [
+ 'topics/feature-flags/runtime-control',
+ 'topics/feature-flags/never-expose-pii',
+ 'topics/feature-flags/evaluate-flags-close-to-user',
+ 'topics/feature-flags/scale-horizontally',
+ 'topics/feature-flags/limit-payloads',
+ 'topics/feature-flags/availability-over-consistency',
+ 'topics/feature-flags/short-lived-feature-flags',
+ 'topics/feature-flags/unique-names',
+ 'topics/feature-flags/democratize-feature-flag-access',
+ 'topics/feature-flags/prioritize-ux',
+ 'topics/feature-flags/enable-traceability',
+ ],
+ },
],
},
{