From 3d7393c34059e3d8e0606b618c659be865d7c50c Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 17 Aug 2023 16:55:52 +0200 Subject: [PATCH 01/10] fix: disallow empty summaries and descriptions (#4529) --- src/test/e2e/api/openapi/openapi.e2e.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/e2e/api/openapi/openapi.e2e.test.ts b/src/test/e2e/api/openapi/openapi.e2e.test.ts index 9477e7fffb..29ae6bbc00 100644 --- a/src/test/e2e/api/openapi/openapi.e2e.test.ts +++ b/src/test/e2e/api/openapi/openapi.e2e.test.ts @@ -193,7 +193,7 @@ test('all tags are listed in the root "tags" list', async () => { expect(invalidTags).toStrictEqual({}); }); -test('all API operations have summaries and descriptions', async () => { +test('all API operations have non-empty summaries and descriptions', async () => { const { body: spec } = await app.request .get('/docs/openapi.json') .expect('Content-Type', /json/) @@ -203,8 +203,8 @@ test('all API operations have summaries and descriptions', async () => { return Object.entries(data) .map(([verb, operationDescription]) => { if ( - 'summary' in operationDescription && - 'description' in operationDescription + operationDescription.summary && + operationDescription.description ) { return undefined; } else { From d263cbe6fba48b4d9c633884757af3946c696561 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 14:58:58 +0000 Subject: [PATCH 02/10] chore(deps): update dependency node to v18.17.1 (#4530) 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 | Update | Change | |---|---|---| | [node](https://togithub.com/nodejs/node) | patch | `18.17.0` -> `18.17.1` | --- ### Release Notes
nodejs/node (node) ### [`v18.17.1`](https://togithub.com/nodejs/node/releases/tag/v18.17.1): 2023-08-09, Version 18.17.1 'Hydrogen' (LTS), @​RafaelGSS [Compare Source](https://togithub.com/nodejs/node/compare/v18.17.0...v18.17.1) This is a security release. ##### Notable Changes The following CVEs are fixed in this release: - [CVE-2023-32002](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-32002): Policies can be bypassed via Module.\_load (High) - [CVE-2023-32006](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-32006): Policies can be bypassed by module.constructor.createRequire (Medium) - [CVE-2023-32559](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-32559): Policies can be bypassed via process.binding (Medium) - OpenSSL Security Releases - [OpenSSL security advisory 14th July](https://mta.openssl.org/pipermail/openssl-announce/2023-July/000264.html). - [OpenSSL security advisory 19th July](https://mta.openssl.org/pipermail/openssl-announce/2023-July/000265.html). - [OpenSSL security advisory 31st July](https://mta.openssl.org/pipermail/openssl-announce/2023-July/000267.html) More detailed information on each of the vulnerabilities can be found in [August 2023 Security Releases](https://nodejs.org/en/blog/vulnerability/august-2023-security-releases/) blog post. ##### Commits - \[[`fe3abdf82e`](https://togithub.com/nodejs/node/commit/fe3abdf82e)] - **deps**: update archs files for openssl-3.0.10+quic1 (Node.js GitHub Bot) [#​49036](https://togithub.com/nodejs/node/pull/49036) - \[[`2c5a522d9c`](https://togithub.com/nodejs/node/commit/2c5a522d9c)] - **deps**: upgrade openssl sources to quictls/openssl-3.0.10+quic1 (Node.js GitHub Bot) [#​49036](https://togithub.com/nodejs/node/pull/49036) - \[[`15bced0bde`](https://togithub.com/nodejs/node/commit/15bced0bde)] - **policy**: handle Module.constructor and main.extensions bypass (RafaelGSS) [nodejs-private/node-private#417](https://togithub.com/nodejs-private/node-private/pull/417) - \[[`d4570fae35`](https://togithub.com/nodejs/node/commit/d4570fae35)] - **policy**: disable process.binding() when enabled (Tobias Nießen) [nodejs-private/node-private#460](https://togithub.com/nodejs-private/node-private/pull/460)
--- ### 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> --- .node-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.node-version b/.node-version index 603606bc91..4a1f488b6c 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -18.17.0 +18.17.1 From ebf35790fe2cbbe21341517ce6db896ed1ff8a54 Mon Sep 17 00:00:00 2001 From: Drew Gorton Date: Thu, 17 Aug 2023 10:32:57 -0500 Subject: [PATCH 03/10] Update docs README.md to include `yarn generate` (#4519) Add missing `yarn generate` command to docs --- website/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/website/README.md b/website/README.md index 231a499c0d..7c71f1eeb0 100644 --- a/website/README.md +++ b/website/README.md @@ -8,13 +8,20 @@ This website is built using [Docusaurus 2](https://docusaurus.io/), a modern sta yarn install ``` +## Generate Open API docs + +```console +yarn generate +``` +Generate the Open API docs that live at Reference documentation > APIs > OpenAPI + ## Local Development ```console yarn start ``` -This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. + Start a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. ## Build From 808936d28a8335c0b07e5508f3396540765d620e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 15:43:54 +0000 Subject: [PATCH 04/10] chore(deps): update dependency sass to v1.65.1 (#4531) 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 | |---|---|---|---|---|---| | [sass](https://togithub.com/sass/dart-sass) | [`1.64.2` -> `1.65.1`](https://renovatebot.com/diffs/npm/sass/1.64.2/1.65.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/sass/1.65.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/sass/1.65.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/sass/1.64.2/1.65.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/sass/1.64.2/1.65.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
sass/dart-sass (sass) ### [`v1.65.1`](https://togithub.com/sass/dart-sass/blob/HEAD/CHANGELOG.md#1651) [Compare Source](https://togithub.com/sass/dart-sass/compare/1.65.0...1.65.1) - Update abs-percent deprecatedIn version to `1.65.0`. ### [`v1.65.0`](https://togithub.com/sass/dart-sass/blob/HEAD/CHANGELOG.md#1650) [Compare Source](https://togithub.com/sass/dart-sass/compare/1.64.2...1.65.0) - All functions defined in CSS Values and Units 4 are now parsed as calculation objects: `round()`, `mod()`, `rem()`, `sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `atan2()`, `pow()`, `sqrt()`, `hypot()`, `log()`, `exp()`, `abs()`, and `sign()`. - Deprecate explicitly passing the `%` unit to the global `abs()` function. In future releases, this will emit a CSS abs() function to be resolved by the browser. This deprecation is named `abs-percent`.
--- ### 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> --- frontend/package.json | 2 +- frontend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 0a99c04f6f..1cf9ab2e31 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -104,7 +104,7 @@ "react-table": "7.8.0", "react-test-renderer": "17.0.2", "react-timeago": "7.1.0", - "sass": "1.64.2", + "sass": "1.65.1", "semver": "7.5.4", "swr": "2.2.0", "tss-react": "4.8.8", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 60d448f3b4..37881dc929 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -8617,10 +8617,10 @@ safe-stable-stringify@^1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sass@1.64.2: - version "1.64.2" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.64.2.tgz#0d9805ad6acf31c59c3acc725fcfb91b7fcc6909" - integrity sha512-TnDlfc+CRnUAgLO9D8cQLFu/GIjJIzJCGkE7o4ekIGQOH7T3GetiRR/PsTWJUHhkzcSPrARkPI+gNWn5alCzDg== +sass@1.65.1: + version "1.65.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.65.1.tgz#8f283b0c26335a88246a448d22e1342ba2ea1432" + integrity sha512-9DINwtHmA41SEd36eVPQ9BJKpn7eKDQmUHmpI0y5Zv2Rcorrh0zS+cFrt050hdNbmmCNKTW3hV5mWfuegNRsEA== dependencies: chokidar ">=3.0.0 <4.0.0" immutable "^4.0.0" From 26e7267c5e71861c437265793429aaa27971f738 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 18:52:04 +0000 Subject: [PATCH 05/10] chore(deps): update react-router monorepo to v6.15.0 (#4532) 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 | |---|---|---|---|---|---| | [react-router](https://togithub.com/remix-run/react-router) | [`6.14.2` -> `6.15.0`](https://renovatebot.com/diffs/npm/react-router/6.14.2/6.15.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/react-router/6.15.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/react-router/6.15.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/react-router/6.14.2/6.15.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/react-router/6.14.2/6.15.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | | [react-router-dom](https://togithub.com/remix-run/react-router) | [`6.14.2` -> `6.15.0`](https://renovatebot.com/diffs/npm/react-router-dom/6.14.2/6.15.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/react-router-dom/6.15.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/react-router-dom/6.15.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/react-router-dom/6.14.2/6.15.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/react-router-dom/6.14.2/6.15.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
remix-run/react-router (react-router) ### [`v6.15.0`](https://togithub.com/remix-run/react-router/blob/HEAD/packages/react-router/CHANGELOG.md#6150) [Compare Source](https://togithub.com/remix-run/react-router/compare/react-router@6.14.2...react-router@6.15.0) ##### Minor Changes - Add's a new `redirectDocument()` function which allows users to specify that a redirect from a `loader`/`action` should trigger a document reload (via `window.location`) instead of attempting to navigate to the redirected location via React Router ([#​10705](https://togithub.com/remix-run/react-router/pull/10705)) ##### Patch Changes - Ensure `useRevalidator` is referentially stable across re-renders if revalidations are not actively occurring ([#​10707](https://togithub.com/remix-run/react-router/pull/10707)) - Updated dependencies: - `@remix-run/router@1.8.0`
remix-run/react-router (react-router-dom) ### [`v6.15.0`](https://togithub.com/remix-run/react-router/blob/HEAD/packages/react-router-dom/CHANGELOG.md#6150) [Compare Source](https://togithub.com/remix-run/react-router/compare/react-router-dom@6.14.2...react-router-dom@6.15.0) ##### Minor Changes - Add's a new `redirectDocument()` function which allows users to specify that a redirect from a `loader`/`action` should trigger a document reload (via `window.location`) instead of attempting to navigate to the redirected location via React Router ([#​10705](https://togithub.com/remix-run/react-router/pull/10705)) ##### Patch Changes - Fixes an edge-case affecting web extensions in Firefox that use `URLSearchParams` and the `useSearchParams` hook. ([#​10620](https://togithub.com/remix-run/react-router/pull/10620)) - Do not include hash in `useFormAction()` for unspecified actions since it cannot be determined on the server and causes hydration issues ([#​10758](https://togithub.com/remix-run/react-router/pull/10758)) - Reorder effects in `unstable_usePrompt` to avoid throwing an exception if the prompt is unblocked and a navigation is performed synchronously ([#​10687](https://togithub.com/remix-run/react-router/pull/10687), [#​10718](https://togithub.com/remix-run/react-router/pull/10718)) - Updated dependencies: - `@remix-run/router@1.8.0` - `react-router@6.15.0`
--- ### 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 these updates 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> --- frontend/package.json | 2 +- frontend/yarn.lock | 30 +++++++++++++++--------------- website/package.json | 2 +- website/yarn.lock | 18 +++++++++--------- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 1cf9ab2e31..93cc482ee2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -100,7 +100,7 @@ "react-joyride": "^2.5.3", "react-linkify": "^1.0.0-alpha", "react-markdown": "^8.0.4", - "react-router-dom": "6.14.2", + "react-router-dom": "6.15.0", "react-table": "7.8.0", "react-test-renderer": "17.0.2", "react-timeago": "7.1.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 37881dc929..f38a990cf8 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2275,10 +2275,10 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== -"@remix-run/router@1.7.2": - version "1.7.2" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.2.tgz#cba1cf0a04bc04cb66027c51fa600e9cbc388bc8" - integrity sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A== +"@remix-run/router@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.8.0.tgz#e848d2f669f601544df15ce2a313955e4bf0bafc" + integrity sha512-mrfKqIHnSZRyIzBcanNJmVQELTnX+qagEDlcKO90RgRBVOZGSGvZKeDihTRfWcqoDn5N/NkUcwWTccnpN18Tfg== "@rollup/plugin-commonjs@~22.0.2": version "22.0.2" @@ -8261,20 +8261,20 @@ react-refresh@^0.14.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== -react-router-dom@6.14.2: - version "6.14.2" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.14.2.tgz#88f520118b91aa60233bd08dbd3fdcaea3a68488" - integrity sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg== +react-router-dom@6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.15.0.tgz#6da7db61e56797266fbbef0d5e324d6ac443ee40" + integrity sha512-aR42t0fs7brintwBGAv2+mGlCtgtFQeOzK0BM1/OiqEzRejOZtpMZepvgkscpMUnKb8YO84G7s3LsHnnDNonbQ== dependencies: - "@remix-run/router" "1.7.2" - react-router "6.14.2" + "@remix-run/router" "1.8.0" + react-router "6.15.0" -react-router@6.14.2: - version "6.14.2" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.2.tgz#1f60994d8c369de7b8ba7a78d8f7ec23df76b300" - integrity sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ== +react-router@6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.15.0.tgz#bf2cb5a4a7ed57f074d4ea88db0d95033f39cac8" + integrity sha512-NIytlzvzLwJkCQj2HLefmeakxxWHWAP+02EGqWEZy+DgfHHKQMUoBBjUQLOtFInBMhWtb3hiUy6MfFgwLjXhqg== dependencies: - "@remix-run/router" "1.7.2" + "@remix-run/router" "1.8.0" react-shallow-renderer@^16.13.1: version "16.15.0" diff --git a/website/package.json b/website/package.json index 23bfb7754c..2deaaa84fd 100644 --- a/website/package.json +++ b/website/package.json @@ -81,7 +81,7 @@ "@tsconfig/docusaurus": "2.0.0", "babel-loader": "9.1.3", "enhanced-resolve": "5.15.0", - "react-router": "6.14.2", + "react-router": "6.15.0", "replace-in-file": "7.0.1", "storybook-addon-root-attribute": "1.0.2", "typescript": "4.8.4" diff --git a/website/yarn.lock b/website/yarn.lock index 62bc8d8a1a..cf3d0628fd 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -3291,10 +3291,10 @@ redux-thunk "^2.4.2" reselect "^4.1.7" -"@remix-run/router@1.7.2": - version "1.7.2" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.2.tgz#cba1cf0a04bc04cb66027c51fa600e9cbc388bc8" - integrity sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A== +"@remix-run/router@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.8.0.tgz#e848d2f669f601544df15ce2a313955e4bf0bafc" + integrity sha512-mrfKqIHnSZRyIzBcanNJmVQELTnX+qagEDlcKO90RgRBVOZGSGvZKeDihTRfWcqoDn5N/NkUcwWTccnpN18Tfg== "@sideway/address@^4.1.3": version "4.1.4" @@ -13714,12 +13714,12 @@ react-router@5.3.4, react-router@^5.3.3: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-router@6.14.2: - version "6.14.2" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.2.tgz#1f60994d8c369de7b8ba7a78d8f7ec23df76b300" - integrity sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ== +react-router@6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.15.0.tgz#bf2cb5a4a7ed57f074d4ea88db0d95033f39cac8" + integrity sha512-NIytlzvzLwJkCQj2HLefmeakxxWHWAP+02EGqWEZy+DgfHHKQMUoBBjUQLOtFInBMhWtb3hiUy6MfFgwLjXhqg== dependencies: - "@remix-run/router" "1.7.2" + "@remix-run/router" "1.8.0" react-textarea-autosize@^8.3.2: version "8.4.0" From 208ca059336d510a23e44cbc50d0674be86d52f6 Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Fri, 18 Aug 2023 10:40:35 +0200 Subject: [PATCH 06/10] Update menu for billing page (#4525) ## About the changes - [x] Show admin menu on billing page in cloud offering. Category menu was not showing up because we have 2 different pages for billing (hosted or not) - [x] Fix menu tab padding https://linear.app/unleash/issue/1-1266/update-menu-for-billing-page --- frontend/src/component/admin/adminRoutes.ts | 2 +- .../component/admin/invoice/InvoiceList.tsx | 4 +- .../component/admin/menu/AdminTabsMenu.tsx | 1 + .../component/admin/menu/CenteredNavLink.tsx | 50 ++++++------------- .../src/component/admin/network/Network.tsx | 1 + .../src/component/admin/roles/RolesPage.tsx | 1 + .../src/component/admin/useAdminRoutes.ts | 13 ++++- .../Header/NavigationMenu/NavigationMenu.tsx | 2 +- 8 files changed, 35 insertions(+), 39 deletions(-) diff --git a/frontend/src/component/admin/adminRoutes.ts b/frontend/src/component/admin/adminRoutes.ts index ff5e7916ed..eed5467cd1 100644 --- a/frontend/src/component/admin/adminRoutes.ts +++ b/frontend/src/component/admin/adminRoutes.ts @@ -90,7 +90,7 @@ export const adminRoutes: INavigationMenuItem[] = [ { path: '/admin/admin-invoices', title: 'Billing & invoices', - menu: { adminSettings: true, mode: ['pro'], billing: true }, + menu: { adminSettings: true, billing: true }, group: 'instance', }, { diff --git a/frontend/src/component/admin/invoice/InvoiceList.tsx b/frontend/src/component/admin/invoice/InvoiceList.tsx index 5e14509599..a690f43536 100644 --- a/frontend/src/component/admin/invoice/InvoiceList.tsx +++ b/frontend/src/component/admin/invoice/InvoiceList.tsx @@ -115,7 +115,9 @@ const InvoiceList = () => { } - elseShow={
{isLoaded && 'No invoices to show.'}
} + elseShow={ + {isLoaded && 'No invoices to show.'} + } /> ); }; diff --git a/frontend/src/component/admin/menu/AdminTabsMenu.tsx b/frontend/src/component/admin/menu/AdminTabsMenu.tsx index 685f4f76a2..81c758984b 100644 --- a/frontend/src/component/admin/menu/AdminTabsMenu.tsx +++ b/frontend/src/component/admin/menu/AdminTabsMenu.tsx @@ -55,6 +55,7 @@ export const AdminTabsMenu: VFC = () => { > {tabs.map(tab => ( { - const navLinkStyle = { - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - width: '100%', - textDecoration: 'none', - color: 'inherit', - padding: props.theme.spacing(1.5, 3), - }; - - const activeNavLinkStyle: React.CSSProperties = { +const StyledNavLink = styled(NavLink)(({ theme }) => ({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + width: '100%', + height: '100%', + textDecoration: 'none', + color: 'inherit', + padding: theme.spacing(0, 5), + '&.active': { fontWeight: 'bold', - borderRadius: '3px', - padding: props.theme.spacing(1.5, 3), - }; - - return props.isActive - ? { ...navLinkStyle, ...activeNavLinkStyle } - : navLinkStyle; -}; + }, +})); export const CenteredNavLink: FC<{ to: string }> = ({ to, children }) => { - const theme = useTheme(); - return ( - createNavLinkStyle({ isActive, theme })} - > - {children} - - ); + return {children}; }; diff --git a/frontend/src/component/admin/network/Network.tsx b/frontend/src/component/admin/network/Network.tsx index a6fa643b24..33d32d6216 100644 --- a/frontend/src/component/admin/network/Network.tsx +++ b/frontend/src/component/admin/network/Network.tsx @@ -43,6 +43,7 @@ export const Network = () => { {label} } + sx={{ padding: 0 }} /> ))} diff --git a/frontend/src/component/admin/roles/RolesPage.tsx b/frontend/src/component/admin/roles/RolesPage.tsx index e708b02e98..681ae9e6d5 100644 --- a/frontend/src/component/admin/roles/RolesPage.tsx +++ b/frontend/src/component/admin/roles/RolesPage.tsx @@ -97,6 +97,7 @@ export const RolesPage = () => { } + sx={{ padding: 0 }} /> ))} diff --git a/frontend/src/component/admin/useAdminRoutes.ts b/frontend/src/component/admin/useAdminRoutes.ts index 61b85f11de..1e4b529f86 100644 --- a/frontend/src/component/admin/useAdminRoutes.ts +++ b/frontend/src/component/admin/useAdminRoutes.ts @@ -10,8 +10,19 @@ export const useAdminRoutes = () => { const showEnterpriseOptionsInPro = Boolean( uiConfig?.flags?.frontendNavigationUpdate ); + const routes = [...adminRoutes]; - return adminRoutes + if (uiConfig.flags.UNLEASH_CLOUD) { + const adminBillingMenuItem = adminRoutes.findIndex( + route => route.title === 'Billing & invoices' + ); + routes[adminBillingMenuItem] = { + ...routes[adminBillingMenuItem], + path: '/admin/billing', + }; + } + + return routes .filter(filterByConfig(uiConfig)) .filter(route => filterAdminRoutes( diff --git a/frontend/src/component/menu/Header/NavigationMenu/NavigationMenu.tsx b/frontend/src/component/menu/Header/NavigationMenu/NavigationMenu.tsx index da60c200cb..56b538a987 100644 --- a/frontend/src/component/menu/Header/NavigationMenu/NavigationMenu.tsx +++ b/frontend/src/component/menu/Header/NavigationMenu/NavigationMenu.tsx @@ -98,9 +98,9 @@ export const NavigationMenu = ({ } arrow placement="left" + key={option.path} > Date: Fri, 18 Aug 2023 14:26:22 +0300 Subject: [PATCH 07/10] feat: create client_applications_usage table migration (#4521) Creates client_applications_usage table --------- Signed-off-by: andreas-unleash --- ...7095805-client-applications-usage-table.js | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/migrations/20230817095805-client-applications-usage-table.js diff --git a/src/migrations/20230817095805-client-applications-usage-table.js b/src/migrations/20230817095805-client-applications-usage-table.js new file mode 100644 index 0000000000..64b095e3ab --- /dev/null +++ b/src/migrations/20230817095805-client-applications-usage-table.js @@ -0,0 +1,24 @@ +'use strict'; + +exports.up = function (db, callback) { + db.runSql( + ` + CREATE TABLE IF NOT EXISTS client_applications_usage ( + app_name VARCHAR(255) REFERENCES client_applications(app_name) ON DELETE CASCADE, + project VARCHAR(255) REFERENCES projects(id) ON DELETE CASCADE, + environment VARCHAR(100) REFERENCES environments(name) ON DELETE CASCADE, + PRIMARY KEY(app_name, project, environment) + ) ; + `, + callback, + ); +}; + +exports.down = function (db, callback) { + db.runSql( + ` + DROP TABLE IF EXISTS client_applications_usage; + `, + callback, + ); +}; From dbae2d115399f7ccc8d80bbcdfa310739eac1101 Mon Sep 17 00:00:00 2001 From: Jaanus Sellin Date: Fri, 18 Aug 2023 14:55:23 +0300 Subject: [PATCH 08/10] feat: application usage new ui (#4528) --- .../ApplicationList/ApplicationList.tsx | 197 ++++++++++++++---- .../ApplicationList/OldApplicationList.tsx | 99 +++++++++ .../TemporaryApplicationListWrapper.tsx | 36 ++++ frontend/src/component/menu/routes.ts | 4 +- frontend/src/interfaces/uiConfig.ts | 1 + src/lib/types/experimental.ts | 3 +- src/server-dev.ts | 1 + 7 files changed, 297 insertions(+), 44 deletions(-) create mode 100644 frontend/src/component/application/ApplicationList/OldApplicationList.tsx create mode 100644 frontend/src/component/application/ApplicationList/TemporaryApplicationListWrapper.tsx diff --git a/frontend/src/component/application/ApplicationList/ApplicationList.tsx b/frontend/src/component/application/ApplicationList/ApplicationList.tsx index 19d31c7f86..d324e910c3 100644 --- a/frontend/src/component/application/ApplicationList/ApplicationList.tsx +++ b/frontend/src/component/application/ApplicationList/ApplicationList.tsx @@ -1,41 +1,37 @@ -import { useEffect, useMemo, useState } from 'react'; -import { CircularProgress, Link } from '@mui/material'; +import { useMemo } from 'react'; +import { + Avatar, + CircularProgress, + Icon, + Link, + styled, + Typography, + useTheme, +} from '@mui/material'; import { Warning } from '@mui/icons-material'; -import { AppsLinkList, styles as themeStyles } from 'component/common'; +import { styles as themeStyles } from 'component/common'; import { PageContent } from 'component/common/PageContent/PageContent'; import { PageHeader } from 'component/common/PageHeader/PageHeader'; import useApplications from 'hooks/api/getters/useApplications/useApplications'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { useSearchParams } from 'react-router-dom'; import { Search } from 'component/common/Search/Search'; -import { safeRegExp } from '@server/util/escape-regex'; - -type PageQueryType = Partial>; +import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; +import { + SortableTableHeader, + Table, + TableBody, + TableCell, + TableRow, +} from 'component/common/Table'; +import { useGlobalFilter, useSortBy, useTable } from 'react-table'; +import { sortTypes } from 'utils/sortTypes'; +import { IconCell } from 'component/common/Table/cells/IconCell/IconCell'; +import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; +import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; export const ApplicationList = () => { - const { applications, loading } = useApplications(); - const [searchParams, setSearchParams] = useSearchParams(); - const [searchValue, setSearchValue] = useState( - searchParams.get('search') || '' - ); - - useEffect(() => { - const tableState: PageQueryType = {}; - if (searchValue) { - tableState.search = searchValue; - } - - setSearchParams(tableState, { - replace: true, - }); - }, [searchValue, setSearchParams]); - - const filteredApplications = useMemo(() => { - const regExp = safeRegExp(searchValue, 'i'); - return searchValue - ? applications?.filter(a => regExp.test(a.appName)) - : applications; - }, [applications, searchValue]); + const { applications: data, loading } = useApplications(); + const theme = useTheme(); const renderNoApplications = () => ( <> @@ -56,25 +52,115 @@ export const ApplicationList = () => { ); - if (!filteredApplications) { + const initialState = useMemo( + () => ({ + sortBy: [{ id: 'name', desc: false }], + hiddenColumns: ['description', 'sortOrder'], + }), + [] + ); + + const columns = useMemo( + () => [ + { + id: 'Icon', + Cell: ({ + row: { + original: { icon }, + }, + }: any) => ( + + {icon} + + } + /> + ), + disableGlobalFilter: true, + }, + { + Header: 'Name', + accessor: 'appName', + width: '50%', + Cell: ({ + row: { + original: { appName, description }, + }, + }: any) => ( + + ), + sortType: 'alphanumeric', + }, + { + Header: 'Project(environment)', + accessor: 'usage', + width: '50%', + Cell: () => ( + + + not connected + + + ), + sortType: 'alphanumeric', + }, + { + accessor: 'description', + disableSortBy: true, + }, + { + accessor: 'sortOrder', + disableGlobalFilter: true, + sortType: 'number', + }, + ], + [] + ); + + const { + getTableProps, + getTableBodyProps, + headerGroups, + rows, + prepareRow, + state: { globalFilter }, + setGlobalFilter, + } = useTable( + { + columns: columns as any[], // TODO: fix after `react-table` v8 update + data, + initialState, + sortTypes, + autoResetGlobalFilter: false, + autoResetSortBy: false, + disableSortRemove: true, + }, + useGlobalFilter, + useSortBy + ); + + if (!data) { return ; } - let applicationCount = - filteredApplications.length < applications.length - ? `${filteredApplications.length} of ${applications.length}` - : applications.length; - return ( <> } /> @@ -82,8 +168,37 @@ export const ApplicationList = () => { >
0} - show={} + condition={data.length > 0} + show={ + + + + + {rows.map(row => { + prepareRow(row); + return ( + + {row.cells.map(cell => ( + + {cell.render( + 'Cell' + )} + + ))} + + ); + })} + +
+
+ } elseShow={ >; + +export const OldApplicationList = () => { + const { applications, loading } = useApplications(); + const [searchParams, setSearchParams] = useSearchParams(); + const [searchValue, setSearchValue] = useState( + searchParams.get('search') || '' + ); + + useEffect(() => { + const tableState: PageQueryType = {}; + if (searchValue) { + tableState.search = searchValue; + } + + setSearchParams(tableState, { + replace: true, + }); + }, [searchValue, setSearchParams]); + + const filteredApplications = useMemo(() => { + const regExp = safeRegExp(searchValue, 'i'); + return searchValue + ? applications?.filter(a => regExp.test(a.appName)) + : applications; + }, [applications, searchValue]); + + const renderNoApplications = () => ( + <> +
+
+
+ Oh snap, it does not seem like you have connected any + applications. To connect your application to Unleash you will + require a Client SDK. +
+
+ You can read more about how to use Unleash in your application + in the{' '} + + documentation. + +
+ + ); + + if (!filteredApplications) { + return ; + } + + let applicationCount = + filteredApplications.length < applications.length + ? `${filteredApplications.length} of ${applications.length}` + : applications.length; + + return ( + <> + + } + /> + } + > +
+ 0} + show={} + elseShow={ + ...loading
} + elseShow={renderNoApplications()} + /> + } + /> +
+
+ + ); +}; diff --git a/frontend/src/component/application/ApplicationList/TemporaryApplicationListWrapper.tsx b/frontend/src/component/application/ApplicationList/TemporaryApplicationListWrapper.tsx new file mode 100644 index 0000000000..120bd5e536 --- /dev/null +++ b/frontend/src/component/application/ApplicationList/TemporaryApplicationListWrapper.tsx @@ -0,0 +1,36 @@ +import { useMemo } from 'react'; +import { Avatar, CircularProgress, Icon, Link } from '@mui/material'; +import { Warning } from '@mui/icons-material'; +import { styles as themeStyles } from 'component/common'; +import { PageContent } from 'component/common/PageContent/PageContent'; +import { PageHeader } from 'component/common/PageHeader/PageHeader'; +import useApplications from 'hooks/api/getters/useApplications/useApplications'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { Search } from 'component/common/Search/Search'; +import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; +import { + SortableTableHeader, + Table, + TableBody, + TableCell, + TableRow, +} from '../../common/Table'; +import { useGlobalFilter, useSortBy, useTable } from 'react-table'; +import { sortTypes } from 'utils/sortTypes'; +import { IconCell } from 'component/common/Table/cells/IconCell/IconCell'; +import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; +import useUiConfig from '../../../hooks/api/getters/useUiConfig/useUiConfig'; +import { ApplicationList } from './ApplicationList'; +import { OldApplicationList } from './OldApplicationList'; + +export const TemporaryApplicationListWrapper = () => { + const { uiConfig } = useUiConfig(); + + return ( + } + elseShow={} + /> + ); +}; diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts index 484342dcb8..4055881f64 100644 --- a/frontend/src/component/menu/routes.ts +++ b/frontend/src/component/menu/routes.ts @@ -19,7 +19,6 @@ import EditProject from 'component/project/Project/EditProject/EditProject'; import CreateFeature from 'component/feature/CreateFeature/CreateFeature'; import EditFeature from 'component/feature/EditFeature/EditFeature'; import { ApplicationEdit } from 'component/application/ApplicationEdit/ApplicationEdit'; -import { ApplicationList } from 'component/application/ApplicationList/ApplicationList'; import ContextList from 'component/context/ContextList/ContextList/ContextList'; import RedirectFeatureView from 'component/feature/RedirectFeatureView/RedirectFeatureView'; import { CreateAddon } from 'component/addons/CreateAddon/CreateAddon'; @@ -44,6 +43,7 @@ import { LazyAdmin } from 'component/admin/LazyAdmin'; import { LazyProject } from 'component/project/Project/LazyProject'; import { LoginHistory } from 'component/loginHistory/LoginHistory'; import { FeatureTypesList } from 'component/featureTypes/FeatureTypesList'; +import { TemporaryApplicationListWrapper } from 'component/application/ApplicationList/TemporaryApplicationListWrapper'; export const routes: IRoute[] = [ // Splash @@ -179,7 +179,7 @@ export const routes: IRoute[] = [ { path: '/applications', title: 'Applications', - component: ApplicationList, + component: TemporaryApplicationListWrapper, type: 'protected', menu: { mobile: true, advanced: true }, }, diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index fcbd073237..64421cc4e3 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -58,6 +58,7 @@ export interface IFlags { segmentChangeRequests?: boolean; changeRequestReject?: boolean; lastSeenByEnvironment?: boolean; + newApplicationList?: boolean; } export interface IVersionInfo { diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index da73b1c41f..4fb67a01e9 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -30,7 +30,8 @@ export type IFlagKey = | 'lastSeenByEnvironment' | 'segmentChangeRequests' | 'changeRequestReject' - | 'customRootRolesKillSwitch'; + | 'customRootRolesKillSwitch' + | 'newApplicationList'; export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>; diff --git a/src/server-dev.ts b/src/server-dev.ts index fe4fee7c64..769c110eb8 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -45,6 +45,7 @@ process.nextTick(async () => { frontendNavigationUpdate: true, lastSeenByEnvironment: true, segmentChangeRequests: true, + newApplicationList: true, }, }, authentication: { From f114aa401acc95b18d42063d9760a2039c6d6a52 Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Mon, 21 Aug 2023 09:00:06 +0200 Subject: [PATCH 09/10] feat: slack-app can now post to both tagged and default channel (#4520) Currently the slack-app addon only posts to either the tagged channel for the feature or the default channels. This PR adds a new field that will allow you to configure the addon to post to both the default channels and the tagged channel (s) --- src/lib/addons/slack-app-definition.ts | 8 ++++++++ src/lib/addons/slack-app.ts | 21 ++++++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/lib/addons/slack-app-definition.ts b/src/lib/addons/slack-app-definition.ts index 5df01d5c7b..fb2d5eb68d 100644 --- a/src/lib/addons/slack-app-definition.ts +++ b/src/lib/addons/slack-app-definition.ts @@ -57,6 +57,14 @@ const slackAppDefinition: IAddonDefinition = { required: false, sensitive: false, }, + { + name: 'alwaysPostToDefault', + displayName: 'Always post to default channels', + description: `If set to 'true' or 'yes', the app will always post events to the default channels, even if the feature toggle has slack tags`, + type: 'text', + required: false, + sensitive: false, + }, ], events: [ FEATURE_CREATED, diff --git a/src/lib/addons/slack-app.ts b/src/lib/addons/slack-app.ts index bdf61c91d0..47f901de8c 100644 --- a/src/lib/addons/slack-app.ts +++ b/src/lib/addons/slack-app.ts @@ -23,6 +23,7 @@ import { IEvent } from '../types/events'; interface ISlackAppAddonParameters { accessToken: string; defaultChannels: string; + alwaysPostToDefault: string; } export default class SlackAppAddon extends Addon { @@ -45,16 +46,26 @@ export default class SlackAppAddon extends Addon { parameters: ISlackAppAddonParameters, ): Promise { try { - const { accessToken, defaultChannels } = parameters; + const { accessToken, defaultChannels, alwaysPostToDefault } = + parameters; if (!accessToken) { this.logger.warn('No access token provided.'); return; } - + let postToDefault = + alwaysPostToDefault === 'true' || alwaysPostToDefault === 'yes'; + this.logger.debug(`Post to default was set to ${postToDefault}`); const taggedChannels = this.findTaggedChannels(event); - const eventChannels = taggedChannels.length - ? taggedChannels - : this.getDefaultChannels(defaultChannels); + let eventChannels: string[]; + if (postToDefault) { + eventChannels = taggedChannels.concat( + this.getDefaultChannels(defaultChannels), + ); + } else { + eventChannels = taggedChannels.length + ? taggedChannels + : this.getDefaultChannels(defaultChannels); + } if (!eventChannels.length) { this.logger.debug( From 2cfb99c7688d751e7ede6b958c3310845eec2dba Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Mon, 21 Aug 2023 11:09:09 +0200 Subject: [PATCH 10/10] feat: features overwrite warning (#4535) --- .../export-import-permissions.e2e.test.ts | 5 +++++ .../export-import-toggles/export-import-service.ts | 14 +++++++++++++- .../import-toggles-store-type.ts | 5 +++++ .../export-import-toggles/import-toggles-store.ts | 12 ++++++++++++ .../import-validation-messages.ts | 8 ++++++++ 5 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts b/src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts index cbd114f258..bd868722f1 100644 --- a/src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts +++ b/src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts @@ -335,6 +335,11 @@ test('validate import data', async () => { 'The following features will not be imported as they are currently archived. To import them, please unarchive them first:', affectedItems: [archivedFeature], }, + { + message: + 'The following features already exist in this project and will be overwritten:', + affectedItems: ['existing_feature'], + }, ], permissions: [ { diff --git a/src/lib/features/export-import-toggles/export-import-service.ts b/src/lib/features/export-import-toggles/export-import-service.ts index 3c8c8252ab..3e553acefc 100644 --- a/src/lib/features/export-import-toggles/export-import-service.ts +++ b/src/lib/features/export-import-toggles/export-import-service.ts @@ -151,6 +151,7 @@ export default class ExportImportService { unsupportedContextFields, archivedFeatures, otherProjectFeatures, + existingProjectFeatures, missingPermissions, ] = await Promise.all([ this.getUnsupportedStrategies(dto), @@ -158,6 +159,7 @@ export default class ExportImportService { this.getUnsupportedContextFields(dto), this.getArchivedFeatures(dto), this.getOtherProjectFeatures(dto), + this.getExistingProjectFeatures(dto), this.importPermissionsService.getMissingPermissions( dto, user, @@ -176,6 +178,7 @@ export default class ExportImportService { const warnings = ImportValidationMessages.compileWarnings( usedCustomStrategies, archivedFeatures, + existingProjectFeatures, ); const permissions = ImportValidationMessages.compilePermissionErrors( @@ -299,7 +302,7 @@ export default class ExportImportService { this.contextService.createContextField( { name: contextField.name, - description: contextField.description, + description: contextField.description || '', legalValues: contextField.legalValues, stickiness: contextField.stickiness, }, @@ -529,6 +532,15 @@ export default class ExportImportService { ); } + private async getExistingProjectFeatures(dto: ImportTogglesSchema) { + const existingProjectsFeatures = + await this.importTogglesStore.getFeaturesInProject( + dto.data.features.map((feature) => feature.name), + dto.project, + ); + return existingProjectsFeatures; + } + private async getNewTagTypes(dto: ImportTogglesSchema) { const existingTagTypes = (await this.tagTypeService.getAll()).map( (tagType) => tagType.name, diff --git a/src/lib/features/export-import-toggles/import-toggles-store-type.ts b/src/lib/features/export-import-toggles/import-toggles-store-type.ts index 1277e8e61b..d8c1aabd2a 100644 --- a/src/lib/features/export-import-toggles/import-toggles-store-type.ts +++ b/src/lib/features/export-import-toggles/import-toggles-store-type.ts @@ -11,6 +11,11 @@ export interface IImportTogglesStore { project: string, ): Promise<{ name: string; project: string }[]>; + getFeaturesInProject( + featureNames: string[], + project: string, + ): Promise; + deleteTagsForFeatures(tags: string[]): Promise; strategiesExistForFeatures( diff --git a/src/lib/features/export-import-toggles/import-toggles-store.ts b/src/lib/features/export-import-toggles/import-toggles-store.ts index fe74fa0cff..0daf877622 100644 --- a/src/lib/features/export-import-toggles/import-toggles-store.ts +++ b/src/lib/features/export-import-toggles/import-toggles-store.ts @@ -74,6 +74,18 @@ export class ImportTogglesStore implements IImportTogglesStore { return rows.map((row) => ({ name: row.name, project: row.project })); } + async getFeaturesInProject( + featureNames: string[], + project: string, + ): Promise { + const rows = await this.db(T.features) + .select(['name', 'project']) + .where('project', project) + .where('archived_at', null) + .whereIn('name', featureNames); + return rows.map((row) => row.name); + } + async deleteTagsForFeatures(features: string[]): Promise { return this.db(T.featureTag).whereIn('feature_name', features).del(); } diff --git a/src/lib/features/export-import-toggles/import-validation-messages.ts b/src/lib/features/export-import-toggles/import-validation-messages.ts index 9017776792..376df05189 100644 --- a/src/lib/features/export-import-toggles/import-validation-messages.ts +++ b/src/lib/features/export-import-toggles/import-validation-messages.ts @@ -73,6 +73,7 @@ export class ImportValidationMessages { static compileWarnings( usedCustomStrategies: string[], archivedFeatures: string[], + existingFeatures: string[], ): ImportTogglesValidateItemSchema[] { const warnings: ImportTogglesValidateItemSchema[] = []; if (usedCustomStrategies.length > 0) { @@ -89,6 +90,13 @@ export class ImportValidationMessages { affectedItems: archivedFeatures, }); } + if (existingFeatures.length > 0) { + warnings.push({ + message: + 'The following features already exist in this project and will be overwritten:', + affectedItems: existingFeatures, + }); + } return warnings; } }