From c6fd558da4b435c1d1a595b1f1d9e76de1dd6bf3 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Thu, 14 Mar 2024 10:15:33 +0100 Subject: [PATCH] feat: show outdated sdks banner (#6541) --- .../OutdatedSdksBanner.test.tsx | 36 +++++++++++++++++++ .../OutdatedSdksBanner/OutdatedSdksBanner.tsx | 35 +++++++++++++++--- .../api/getters/useApiGetter/useApiGetter.ts | 7 ++++ .../useOutdatedSdks/useOutdatedSdks.ts | 14 ++++++++ frontend/src/interfaces/uiConfig.ts | 1 + .../src/openapi/models/getOutdatedSdks404.ts | 14 ++++++++ frontend/src/openapi/models/index.ts | 4 +++ .../src/openapi/models/outdatedSdksSchema.ts | 14 ++++++++ .../models/outdatedSdksSchemaSdksItem.ts | 12 +++++++ .../models/toggleFeatureActionSchema.ts | 17 +++++++++ .../__snapshots__/create-config.test.ts.snap | 1 + src/lib/types/experimental.ts | 5 +++ 12 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 frontend/src/component/banners/OutdatedSdksBanner/OutdatedSdksBanner.test.tsx create mode 100644 frontend/src/hooks/api/getters/useOutdatedSdks/useOutdatedSdks.ts create mode 100644 frontend/src/openapi/models/getOutdatedSdks404.ts create mode 100644 frontend/src/openapi/models/outdatedSdksSchema.ts create mode 100644 frontend/src/openapi/models/outdatedSdksSchemaSdksItem.ts create mode 100644 frontend/src/openapi/models/toggleFeatureActionSchema.ts diff --git a/frontend/src/component/banners/OutdatedSdksBanner/OutdatedSdksBanner.test.tsx b/frontend/src/component/banners/OutdatedSdksBanner/OutdatedSdksBanner.test.tsx new file mode 100644 index 0000000000..398ce42197 --- /dev/null +++ b/frontend/src/component/banners/OutdatedSdksBanner/OutdatedSdksBanner.test.tsx @@ -0,0 +1,36 @@ +import { screen } from '@testing-library/react'; +import { render } from 'utils/testRenderer'; +import { testServerRoute, testServerSetup } from 'utils/testServer'; +import { OutdatedSdksSchema } from 'openapi'; +import { OutdatedSdksBanner } from './OutdatedSdksBanner'; + +const server = testServerSetup(); + +const setupApi = (outdatedSdks: OutdatedSdksSchema) => { + testServerRoute(server, '/api/admin/metrics/sdks/outdated', outdatedSdks); + testServerRoute(server, '/api/admin/ui-config', { + flags: { + outdatedSdksBanner: true, + }, + }); +}; + +test('Show outdated SDKs and apps using them', async () => { + setupApi({ + sdks: [ + { + sdkVersion: 'unleash-node-client:3.2.1', + applications: ['application1', 'application2'], + }, + ], + }); + render(); + + const link = await screen.findByText('Please update those versions'); + + link.click(); + + await screen.findByText('unleash-node-client:3.2.1'); + await screen.findByText('application1'); + await screen.findByText('application2'); +}); diff --git a/frontend/src/component/banners/OutdatedSdksBanner/OutdatedSdksBanner.tsx b/frontend/src/component/banners/OutdatedSdksBanner/OutdatedSdksBanner.tsx index b3f45a2a1d..e749a2e7eb 100644 --- a/frontend/src/component/banners/OutdatedSdksBanner/OutdatedSdksBanner.tsx +++ b/frontend/src/component/banners/OutdatedSdksBanner/OutdatedSdksBanner.tsx @@ -1,21 +1,48 @@ import { ConditionallyRender } from '../../common/ConditionallyRender/ConditionallyRender'; import { Banner } from '../Banner/Banner'; -import { IBanner } from '../../../interfaces/banner'; +import { IBanner } from 'interfaces/banner'; +import { useOutdatedSdks } from 'hooks/api/getters/useOutdatedSdks/useOutdatedSdks'; +import { useUiFlag } from 'hooks/useUiFlag'; +import { Link } from 'react-router-dom'; +import { styled } from '@mui/material'; + +const StyledList = styled('ul')({ margin: 0 }); export const OutdatedSdksBanner = () => { - const displayOutdatedSdksBanner = false; + const { + data: { sdks }, + } = useOutdatedSdks(); + const flagEnabled = useUiFlag('outdatedSdksBanner'); + const outdatedSdksBanner: IBanner = { message: `We noticed that you're using outdated SDKs. `, variant: 'warning', link: 'dialog', linkText: 'Please update those versions', dialogTitle: 'Outdated SDKs', - dialog:

Outdated SDKs

, + dialog: ( + <> + {sdks.map((item) => ( +
+ {item.sdkVersion} + + {item.applications.map((application) => ( +
  • + + {application} + +
  • + ))} +
    +
    + ))} + + ), }; return ( <> 0} show={} /> diff --git a/frontend/src/hooks/api/getters/useApiGetter/useApiGetter.ts b/frontend/src/hooks/api/getters/useApiGetter/useApiGetter.ts index 93a9548190..9c78032d3c 100644 --- a/frontend/src/hooks/api/getters/useApiGetter/useApiGetter.ts +++ b/frontend/src/hooks/api/getters/useApiGetter/useApiGetter.ts @@ -1,5 +1,6 @@ import useSWR, { SWRConfiguration, Key } from 'swr'; import { useCallback } from 'react'; +import handleErrorResponses from '../httpErrorResponseHandler'; interface IUseApiGetterOutput { data?: T; @@ -26,3 +27,9 @@ export const useApiGetter = ( loading: !error && !data, }; }; + +export const fetcher = (path: string, errorTarget: string) => { + return fetch(path) + .then(handleErrorResponses(errorTarget)) + .then((res) => res.json()); +}; diff --git a/frontend/src/hooks/api/getters/useOutdatedSdks/useOutdatedSdks.ts b/frontend/src/hooks/api/getters/useOutdatedSdks/useOutdatedSdks.ts new file mode 100644 index 0000000000..979e94a740 --- /dev/null +++ b/frontend/src/hooks/api/getters/useOutdatedSdks/useOutdatedSdks.ts @@ -0,0 +1,14 @@ +import { fetcher, useApiGetter } from '../useApiGetter/useApiGetter'; +import { OutdatedSdksSchema } from '../../../../openapi'; + +const PATH = 'api/admin/metrics/sdks/outdated'; + +export const useOutdatedSdks = () => { + const { data, refetch, loading, error } = useApiGetter( + PATH, + () => fetcher(PATH, 'Outdated SDKs'), + { refreshInterval: 60 * 1000 }, + ); + + return { data: data || { sdks: [] }, refetch, error }; +}; diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index 34c8f6bdd0..0c4a1d6308 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -79,6 +79,7 @@ export type UiFlags = { featureSearchFeedbackPosting?: boolean; userAccessUIEnabled?: boolean; sdkReporting?: boolean; + outdatedSdksBanner?: boolean; }; export interface IVersionInfo { diff --git a/frontend/src/openapi/models/getOutdatedSdks404.ts b/frontend/src/openapi/models/getOutdatedSdks404.ts new file mode 100644 index 0000000000..a88fb9b8c3 --- /dev/null +++ b/frontend/src/openapi/models/getOutdatedSdks404.ts @@ -0,0 +1,14 @@ +/** + * Generated by Orval + * Do not edit manually. + * See `gen:api` script in package.json + */ + +export type GetOutdatedSdks404 = { + /** The ID of the error instance */ + id?: string; + /** A description of what went wrong. */ + message?: string; + /** The name of the error kind */ + name?: string; +}; diff --git a/frontend/src/openapi/models/index.ts b/frontend/src/openapi/models/index.ts index eafe8e59b6..7cce221cf7 100644 --- a/frontend/src/openapi/models/index.ts +++ b/frontend/src/openapi/models/index.ts @@ -648,6 +648,7 @@ export * from './getMe401'; export * from './getOidcSettings400'; export * from './getOidcSettings401'; export * from './getOidcSettings403'; +export * from './getOutdatedSdks404'; export * from './getPats401'; export * from './getPats403'; export * from './getPats404'; @@ -789,6 +790,8 @@ export * from './notificationsSchemaItemNotificationType'; export * from './oidcSettingsSchema'; export * from './oidcSettingsSchemaDefaultRootRole'; export * from './oidcSettingsSchemaIdTokenSigningAlgorithm'; +export * from './outdatedSdksSchema'; +export * from './outdatedSdksSchemaSdksItem'; export * from './overrideSchema'; export * from './overwriteEnvironmentFeatureVariants400'; export * from './overwriteEnvironmentFeatureVariants401'; @@ -1044,6 +1047,7 @@ export * from './toggleEnvironmentOff404'; export * from './toggleEnvironmentOn401'; export * from './toggleEnvironmentOn403'; export * from './toggleEnvironmentOn404'; +export * from './toggleFeatureActionSchema'; export * from './toggleFeatureEnvironmentOff400'; export * from './toggleFeatureEnvironmentOff401'; export * from './toggleFeatureEnvironmentOff403'; diff --git a/frontend/src/openapi/models/outdatedSdksSchema.ts b/frontend/src/openapi/models/outdatedSdksSchema.ts new file mode 100644 index 0000000000..6ec55a0616 --- /dev/null +++ b/frontend/src/openapi/models/outdatedSdksSchema.ts @@ -0,0 +1,14 @@ +/** + * Generated by Orval + * Do not edit manually. + * See `gen:api` script in package.json + */ +import type { OutdatedSdksSchemaSdksItem } from './outdatedSdksSchemaSdksItem'; + +/** + * Data about outdated SDKs that should be upgraded. + */ +export interface OutdatedSdksSchema { + /** A list of SDKs */ + sdks: OutdatedSdksSchemaSdksItem[]; +} diff --git a/frontend/src/openapi/models/outdatedSdksSchemaSdksItem.ts b/frontend/src/openapi/models/outdatedSdksSchemaSdksItem.ts new file mode 100644 index 0000000000..3fbacc2dff --- /dev/null +++ b/frontend/src/openapi/models/outdatedSdksSchemaSdksItem.ts @@ -0,0 +1,12 @@ +/** + * Generated by Orval + * Do not edit manually. + * See `gen:api` script in package.json + */ + +export type OutdatedSdksSchemaSdksItem = { + /** A list of applications using the SDK version */ + applications: string[]; + /** An outdated SDK version identifier. Usually formatted as "unleash-client-:" */ + sdkVersion: string; +}; diff --git a/frontend/src/openapi/models/toggleFeatureActionSchema.ts b/frontend/src/openapi/models/toggleFeatureActionSchema.ts new file mode 100644 index 0000000000..ec58b8bb4d --- /dev/null +++ b/frontend/src/openapi/models/toggleFeatureActionSchema.ts @@ -0,0 +1,17 @@ +/** + * Generated by Orval + * Do not edit manually. + * See `gen:api` script in package.json + */ + +/** + * Input data required for the action + */ +export interface ToggleFeatureActionSchema { + /** The environment we want to target */ + environment: string; + /** The name of the feature we want to target */ + featureName: string; + /** The project where the feature is located */ + project: string; +} diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index c44430d36a..aa2a5d490c 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -130,6 +130,7 @@ exports[`should create default config 1`] = ` }, "migrationLock": true, "newStrategyConfigurationFeedback": false, + "outdatedSdksBanner": false, "personalAccessTokensKillSwitch": false, "proPlanAutoCharge": false, "queryMissingTokens": false, diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 8bc7cba736..3912973a94 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -51,6 +51,7 @@ export type IFlagKey = | 'disableUpdateMaxRevisionId' | 'disablePublishUnannouncedEvents' | 'sdkReporting' + | 'outdatedSdksBanner' | 'responseTimeMetricsFix' | 'scimApi' | 'displayEdgeBanner' @@ -203,6 +204,10 @@ const flags: IFlags = { process.env.UNLEASH_EXPERIMENTAL_SDK_REPORTING, false, ), + outdatedSdksBanner: parseEnvVarBoolean( + process.env.UNLEASH_EXPERIMENTAL_OUTDATED_SDKS_BANNER, + false, + ), feedbackComments: { name: 'feedbackComments', enabled: parseEnvVarBoolean(