From 3207eee5bfcd49b073ddfe5d0331c23b80a8bd7e Mon Sep 17 00:00:00 2001 From: Maximilian Vorbrodt Date: Wed, 2 Nov 2022 11:36:48 +0100 Subject: [PATCH 01/68] prevent scrolling when hovering over graph Signed-off-by: Maximilian Vorbrodt --- .../src/components/DependencyGraph/DependencyGraph.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 5159f67d9e..4ff68443f4 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -259,6 +259,8 @@ export function DependencyGraph( ); } + container.on('wheel', e => e.preventDefault()); + if (zoom === 'enabled') { enableZoom(); } else if (zoom === 'enable-on-click') { From 11c1c4dc239d3eea677d070a987e7fc598199856 Mon Sep 17 00:00:00 2001 From: Maximilian Vorbrodt Date: Wed, 2 Nov 2022 11:38:57 +0100 Subject: [PATCH 02/68] add background to group diagram Signed-off-by: Maximilian Vorbrodt --- .../GroupsExplorerContent/GroupsDiagram.tsx | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx index 23553b8b76..c366d1c6c1 100644 --- a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx @@ -35,7 +35,7 @@ import { getEntityRelations, } from '@backstage/plugin-catalog-react'; import { BackstageTheme } from '@backstage/theme'; -import { makeStyles, Typography, useTheme } from '@material-ui/core'; +import { makeStyles, Typography, Paper, useTheme } from '@material-ui/core'; import ZoomOutMap from '@material-ui/icons/ZoomOutMap'; import classNames from 'classnames'; import React from 'react'; @@ -46,6 +46,12 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ flex: 1, minHeight: 0, }, + graphWrapper: { + position: 'relative', + flex: 1, + minHeight: 0, + display: 'flex', + }, organizationNode: { fill: theme.palette.secondary.light, stroke: theme.palette.secondary.light, @@ -62,6 +68,15 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ justifyContent: 'center', color: 'black', }, + legend: { + position: 'absolute', + bottom: 0, + right: 0, + padding: theme.spacing(1), + '& .icon': { + verticalAlign: 'bottom', + }, + }, textOrganization: { color: theme.palette.secondary.contrastText, }, @@ -222,21 +237,26 @@ export function GroupsDiagram() { return ( <> - - - Use pinch & zoom - to move around the diagram. - + + + + + Use pinch & zoom to move around + the diagram. + + ); } From e169c225a43d05befcde7b1d62a1e3b06ab44dfc Mon Sep 17 00:00:00 2001 From: Maximilian Vorbrodt Date: Wed, 2 Nov 2022 11:40:17 +0100 Subject: [PATCH 03/68] remove max height from graph Signed-off-by: Maximilian Vorbrodt --- .../src/components/DependencyGraph/DependencyGraph.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 4ff68443f4..3ecdbedddf 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -396,7 +396,6 @@ export function DependencyGraph( ref={containerRef} {...svgProps} width="100%" - height={maxHeight} viewBox={`0 0 ${maxWidth} ${maxHeight}`} > From 9140cb69c32905710f97dcde343ed445d783057d Mon Sep 17 00:00:00 2001 From: Maximilian Vorbrodt Date: Wed, 2 Nov 2022 11:43:20 +0100 Subject: [PATCH 04/68] add changeset Signed-off-by: Maximilian Vorbrodt --- .changeset/cuddly-jeans-laugh.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/cuddly-jeans-laugh.md diff --git a/.changeset/cuddly-jeans-laugh.md b/.changeset/cuddly-jeans-laugh.md new file mode 100644 index 0000000000..6dadb85022 --- /dev/null +++ b/.changeset/cuddly-jeans-laugh.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-explore': patch +--- + +Adds a background to the group diagram and prevents weird scroll/zoom behavior when hovering the graph. With this fix the user can only zoom in and out while hovering the graph. If the cursor is not hovering the graph the user can scroll up and down on the page. In total this creates more consistent scrolling or zooming behavior. From 17a8e32f39fc359de39fd4fca0da6bf0a4e6e5a2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 9 Nov 2022 12:11:11 +0000 Subject: [PATCH 05/68] Update dependency rc-progress to v3.4.1 Signed-off-by: Renovate Bot --- .changeset/renovate-778b2fa.md | 7 +++++++ packages/core-components/package.json | 2 +- plugins/codescene/package.json | 2 +- plugins/sonarqube/package.json | 2 +- yarn.lock | 20 +++++++++++++++++--- 5 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 .changeset/renovate-778b2fa.md diff --git a/.changeset/renovate-778b2fa.md b/.changeset/renovate-778b2fa.md new file mode 100644 index 0000000000..f4f57e448b --- /dev/null +++ b/.changeset/renovate-778b2fa.md @@ -0,0 +1,7 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-codescene': patch +'@backstage/plugin-sonarqube': patch +--- + +Updated dependency `rc-progress` to `3.4.1`. diff --git a/packages/core-components/package.json b/packages/core-components/package.json index c7a667e7e2..3aa65efa8d 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -56,7 +56,7 @@ "pluralize": "^8.0.0", "prop-types": "^15.7.2", "qs": "^6.9.4", - "rc-progress": "3.4.0", + "rc-progress": "3.4.1", "react-helmet": "6.1.0", "react-hook-form": "^7.12.2", "react-markdown": "^8.0.0", diff --git a/plugins/codescene/package.json b/plugins/codescene/package.json index 31970219e3..3863b3ef7f 100644 --- a/plugins/codescene/package.json +++ b/plugins/codescene/package.json @@ -30,7 +30,7 @@ "@material-ui/core": "^4.9.10", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.57", - "rc-progress": "3.4.0", + "rc-progress": "3.4.1", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 2d217ee57c..12567ae189 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -44,7 +44,7 @@ "@material-ui/lab": "4.0.0-alpha.57", "@material-ui/styles": "^4.10.0", "cross-fetch": "^3.1.5", - "rc-progress": "3.4.0", + "rc-progress": "3.4.1", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/yarn.lock b/yarn.lock index 0802bef2c2..61a3385dfc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3700,7 +3700,7 @@ __metadata: pluralize: ^8.0.0 prop-types: ^15.7.2 qs: ^6.9.4 - rc-progress: 3.4.0 + rc-progress: 3.4.1 react-helmet: 6.1.0 react-hook-form: ^7.12.2 react-markdown: ^8.0.0 @@ -5412,7 +5412,7 @@ __metadata: "@types/node": "*" cross-fetch: ^3.1.5 msw: ^0.48.0 - rc-progress: 3.4.0 + rc-progress: 3.4.1 react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 @@ -7292,7 +7292,7 @@ __metadata: "@types/node": ^16.11.26 cross-fetch: ^3.1.5 msw: ^0.48.0 - rc-progress: 3.4.0 + rc-progress: 3.4.1 react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 @@ -33164,6 +33164,20 @@ __metadata: languageName: node linkType: hard +"rc-progress@npm:3.4.1": + version: 3.4.1 + resolution: "rc-progress@npm:3.4.1" + dependencies: + "@babel/runtime": ^7.10.1 + classnames: ^2.2.6 + rc-util: ^5.16.1 + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: d4dce5231ea29bfa866935a59f05473711f9dfc944f95cc78b0fdcd1508a063983cc58973f54c2b20797f0b0f480c4a2b84aaa2ca185158c14800ec659163880 + languageName: node + linkType: hard + "rc-util@npm:^5.16.1": version: 5.16.1 resolution: "rc-util@npm:5.16.1" From 3232e31a23fcc90553dfa02aa7f5ff02bba4a1df Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 26 Oct 2022 14:30:10 +0200 Subject: [PATCH 06/68] backend: Add support for lifecycle events Signed-off-by: Johan Haals --- packages/backend-app-api/api-report.md | 6 ++ .../src/services/implementations/index.ts | 1 + .../implementations/lifecycleService.ts | 81 +++++++++++++++++++ .../backend-defaults/src/CreateBackend.ts | 2 + packages/backend-plugin-api/api-report.md | 13 +++ .../src/services/definitions/index.ts | 5 ++ .../definitions/lifecycleServiceRef.ts | 42 ++++++++++ .../src/service/CatalogPlugin.ts | 9 ++- 8 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 packages/backend-app-api/src/services/implementations/lifecycleService.ts create mode 100644 packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts diff --git a/packages/backend-app-api/api-report.md b/packages/backend-app-api/api-report.md index 4d9fbea4a5..5d200be93c 100644 --- a/packages/backend-app-api/api-report.md +++ b/packages/backend-app-api/api-report.md @@ -4,6 +4,7 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; +import { BackendLifecycle } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { HttpRouterService } from '@backstage/backend-plugin-api'; @@ -66,6 +67,11 @@ export type HttpRouterFactoryOptions = { indexPlugin?: string; }; +// @public (undocumented) +export const lifecycleFactory: ( + options?: undefined, +) => ServiceFactory; + // @public (undocumented) export const loggerFactory: (options?: undefined) => ServiceFactory; diff --git a/packages/backend-app-api/src/services/implementations/index.ts b/packages/backend-app-api/src/services/implementations/index.ts index 608399bad9..2c26097f48 100644 --- a/packages/backend-app-api/src/services/implementations/index.ts +++ b/packages/backend-app-api/src/services/implementations/index.ts @@ -25,4 +25,5 @@ export { schedulerFactory } from './schedulerService'; export { tokenManagerFactory } from './tokenManagerService'; export { urlReaderFactory } from './urlReaderService'; export { httpRouterFactory } from './httpRouterService'; +export { lifecycleFactory } from './lifecycleService'; export type { HttpRouterFactoryOptions } from './httpRouterService'; diff --git a/packages/backend-app-api/src/services/implementations/lifecycleService.ts b/packages/backend-app-api/src/services/implementations/lifecycleService.ts new file mode 100644 index 0000000000..3549ec31f2 --- /dev/null +++ b/packages/backend-app-api/src/services/implementations/lifecycleService.ts @@ -0,0 +1,81 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + BackendLifecycle, + createServiceFactory, + lifecycleServiceRef, + loggerToWinstonLogger, + pluginMetadataServiceRef, + rootLoggerServiceRef, + ShutdownHookOptions, +} from '@backstage/backend-plugin-api'; +import { Logger } from 'winston'; + +class BackendLifecycleImpl { + constructor(private readonly logger: Logger) {} + #shutdownTasks: Array = []; + + addShutdownHook(options: ShutdownHookOptions & { pluginId: string }): void { + this.#shutdownTasks.push(options); + } + + async shutdown(): Promise { + await Promise.all( + this.#shutdownTasks.map(hook => + hook + .fn() + .catch(e => { + this.logger.error( + `Shutdown hook registered by plugin '${hook.pluginId}' failed with: ${e}`, + ); + }) + .then(() => + this.logger.debug( + `Successfully ran hook registered by plugin ${hook.pluginId}`, + ), + ), + ), + ); + } +} + +class PluginScopedLifecycleImpl implements BackendLifecycle { + constructor( + private readonly lifecycle: BackendLifecycleImpl, + private readonly pluginId: string, + ) {} + addShutdownHook(options: ShutdownHookOptions): void { + this.lifecycle.addShutdownHook({ ...options, pluginId: this.pluginId }); + } +} + +/** @public */ +export const lifecycleFactory = createServiceFactory({ + service: lifecycleServiceRef, + deps: { + logger: rootLoggerServiceRef, + plugin: pluginMetadataServiceRef, + }, + async factory({ logger }) { + const rootLifecycle = new BackendLifecycleImpl( + loggerToWinstonLogger(logger), + ); + process.on('SIGTERM', async () => await rootLifecycle.shutdown()); + return async ({ plugin }) => { + return new PluginScopedLifecycleImpl(rootLifecycle, plugin.getId()); + }; + }, +}); diff --git a/packages/backend-defaults/src/CreateBackend.ts b/packages/backend-defaults/src/CreateBackend.ts index b95bd6f35b..9fde31e3bd 100644 --- a/packages/backend-defaults/src/CreateBackend.ts +++ b/packages/backend-defaults/src/CreateBackend.ts @@ -22,6 +22,7 @@ import { databaseFactory, discoveryFactory, httpRouterFactory, + lifecycleFactory, loggerFactory, permissionsFactory, rootLoggerFactory, @@ -43,6 +44,7 @@ export const defaultServiceFactories = [ tokenManagerFactory, urlReaderFactory, httpRouterFactory, + lifecycleFactory, ]; /** diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index 70ab168053..0183707735 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -24,6 +24,11 @@ export interface BackendFeature { register(reg: BackendRegistrationPoints): void; } +// @public (undocumented) +export interface BackendLifecycle { + addShutdownHook(options: ShutdownHookOptions): void; +} + // @public (undocumented) export interface BackendModuleConfig { // (undocumented) @@ -158,6 +163,9 @@ export interface HttpRouterService { // @public (undocumented) export const httpRouterServiceRef: ServiceRef; +// @public (undocumented) +export const lifecycleServiceRef: ServiceRef; + // @public (undocumented) export interface Logger { // (undocumented) @@ -235,6 +243,11 @@ export type ServiceRef< $$ref: 'service'; }; +// @public (undocumented) +export type ShutdownHookOptions = { + fn: () => Promise; +}; + // @public (undocumented) export const tokenManagerServiceRef: ServiceRef; diff --git a/packages/backend-plugin-api/src/services/definitions/index.ts b/packages/backend-plugin-api/src/services/definitions/index.ts index e5f032ef60..19f75ffeb5 100644 --- a/packages/backend-plugin-api/src/services/definitions/index.ts +++ b/packages/backend-plugin-api/src/services/definitions/index.ts @@ -28,4 +28,9 @@ export { permissionsServiceRef } from './permissionsServiceRef'; export { schedulerServiceRef } from './schedulerServiceRef'; export { rootLoggerServiceRef } from './rootLoggerServiceRef'; export { pluginMetadataServiceRef } from './pluginMetadataServiceRef'; +export { lifecycleServiceRef } from './lifecycleServiceRef'; +export type { + BackendLifecycle, + ShutdownHookOptions, +} from './lifecycleServiceRef'; export type { PluginMetadata } from './pluginMetadataServiceRef'; diff --git a/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts b/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts new file mode 100644 index 0000000000..ffdd4c414b --- /dev/null +++ b/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createServiceRef } from '../system/types'; + +/** + * @public + **/ +export type ShutdownHookOptions = { + fn: () => Promise; +}; + +/** + * @public + **/ +export interface BackendLifecycle { + /** + * Register a function to be called and when the backend is shutting down. + */ + addShutdownHook(options: ShutdownHookOptions): void; +} + +/** + * @public + */ +export const lifecycleServiceRef = createServiceRef({ + id: 'core.lifecycle', + scope: 'plugin', +}); diff --git a/plugins/catalog-backend/src/service/CatalogPlugin.ts b/plugins/catalog-backend/src/service/CatalogPlugin.ts index 64e1e52494..84cad86d9a 100644 --- a/plugins/catalog-backend/src/service/CatalogPlugin.ts +++ b/plugins/catalog-backend/src/service/CatalogPlugin.ts @@ -22,6 +22,7 @@ import { permissionsServiceRef, urlReaderServiceRef, httpRouterServiceRef, + lifecycleServiceRef, } from '@backstage/backend-plugin-api'; import { CatalogBuilder } from './CatalogBuilder'; import { @@ -78,6 +79,7 @@ export const catalogPlugin = createBackendPlugin({ permissions: permissionsServiceRef, database: databaseServiceRef, httpRouter: httpRouterServiceRef, + lifecycle: lifecycleServiceRef, }, async init({ logger, @@ -86,6 +88,7 @@ export const catalogPlugin = createBackendPlugin({ database, permissions, httpRouter, + lifecycle, }) { const winstonLogger = loggerToWinstonLogger(logger); const builder = await CatalogBuilder.create({ @@ -100,7 +103,11 @@ export const catalogPlugin = createBackendPlugin({ const { processingEngine, router } = await builder.build(); await processingEngine.start(); - + lifecycle.addShutdownHook({ + fn: async () => { + await processingEngine.stop(); + }, + }); httpRouter.use(router); }, }); From 7cdc99a002bf257876f1b2b6ab014872d1c16dd8 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 9 Nov 2022 15:33:16 +0100 Subject: [PATCH 07/68] Update packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Johan Haals Signed-off-by: Johan Haals --- .../src/services/definitions/lifecycleServiceRef.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts b/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts index ffdd4c414b..3e8dd31fd1 100644 --- a/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts +++ b/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts @@ -28,7 +28,7 @@ export type ShutdownHookOptions = { **/ export interface BackendLifecycle { /** - * Register a function to be called and when the backend is shutting down. + * Register a function to be called when the backend is shutting down. */ addShutdownHook(options: ShutdownHookOptions): void; } From 678a1583039c01b5afe3d9c2b8bea32975c637f1 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 9 Nov 2022 16:02:10 +0100 Subject: [PATCH 08/68] chore: Rename options, add additional logging Signed-off-by: Johan Haals --- .../src/services/implementations/lifecycleService.ts | 12 ++++++++---- .../src/services/definitions/index.ts | 2 +- .../src/services/definitions/lifecycleServiceRef.ts | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/backend-app-api/src/services/implementations/lifecycleService.ts b/packages/backend-app-api/src/services/implementations/lifecycleService.ts index 3549ec31f2..578bd9ca01 100644 --- a/packages/backend-app-api/src/services/implementations/lifecycleService.ts +++ b/packages/backend-app-api/src/services/implementations/lifecycleService.ts @@ -20,19 +20,23 @@ import { loggerToWinstonLogger, pluginMetadataServiceRef, rootLoggerServiceRef, - ShutdownHookOptions, + BackendLifecycleShutdownHook, } from '@backstage/backend-plugin-api'; import { Logger } from 'winston'; class BackendLifecycleImpl { constructor(private readonly logger: Logger) {} - #shutdownTasks: Array = []; + #shutdownTasks: Array = + []; - addShutdownHook(options: ShutdownHookOptions & { pluginId: string }): void { + addShutdownHook( + options: BackendLifecycleShutdownHook & { pluginId: string }, + ): void { this.#shutdownTasks.push(options); } async shutdown(): Promise { + this.logger.info(`Running ${this.#shutdownTasks.length} shutdown tasks...`); await Promise.all( this.#shutdownTasks.map(hook => hook @@ -57,7 +61,7 @@ class PluginScopedLifecycleImpl implements BackendLifecycle { private readonly lifecycle: BackendLifecycleImpl, private readonly pluginId: string, ) {} - addShutdownHook(options: ShutdownHookOptions): void { + addShutdownHook(options: BackendLifecycleShutdownHook): void { this.lifecycle.addShutdownHook({ ...options, pluginId: this.pluginId }); } } diff --git a/packages/backend-plugin-api/src/services/definitions/index.ts b/packages/backend-plugin-api/src/services/definitions/index.ts index 19f75ffeb5..cf997a344d 100644 --- a/packages/backend-plugin-api/src/services/definitions/index.ts +++ b/packages/backend-plugin-api/src/services/definitions/index.ts @@ -31,6 +31,6 @@ export { pluginMetadataServiceRef } from './pluginMetadataServiceRef'; export { lifecycleServiceRef } from './lifecycleServiceRef'; export type { BackendLifecycle, - ShutdownHookOptions, + BackendLifecycleShutdownHook, } from './lifecycleServiceRef'; export type { PluginMetadata } from './pluginMetadataServiceRef'; diff --git a/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts b/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts index 3e8dd31fd1..4fd9f8daae 100644 --- a/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts +++ b/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts @@ -19,7 +19,7 @@ import { createServiceRef } from '../system/types'; /** * @public **/ -export type ShutdownHookOptions = { +export type BackendLifecycleShutdownHook = { fn: () => Promise; }; @@ -30,7 +30,7 @@ export interface BackendLifecycle { /** * Register a function to be called when the backend is shutting down. */ - addShutdownHook(options: ShutdownHookOptions): void; + addShutdownHook(options: BackendLifecycleShutdownHook): void; } /** From d0c09089a85eaa5fd33874550a8ed8af4a8c0996 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 10 Nov 2022 15:16:30 +0100 Subject: [PATCH 09/68] Add support for non async hooks Signed-off-by: Johan Haals --- .../src/services/implementations/lifecycleService.ts | 4 ++-- .../src/services/definitions/lifecycleServiceRef.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend-app-api/src/services/implementations/lifecycleService.ts b/packages/backend-app-api/src/services/implementations/lifecycleService.ts index 578bd9ca01..81f1b16b53 100644 --- a/packages/backend-app-api/src/services/implementations/lifecycleService.ts +++ b/packages/backend-app-api/src/services/implementations/lifecycleService.ts @@ -39,8 +39,8 @@ class BackendLifecycleImpl { this.logger.info(`Running ${this.#shutdownTasks.length} shutdown tasks...`); await Promise.all( this.#shutdownTasks.map(hook => - hook - .fn() + Promise.resolve() + .then(() => hook.fn()) .catch(e => { this.logger.error( `Shutdown hook registered by plugin '${hook.pluginId}' failed with: ${e}`, diff --git a/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts b/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts index 4fd9f8daae..15610e2643 100644 --- a/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts +++ b/packages/backend-plugin-api/src/services/definitions/lifecycleServiceRef.ts @@ -20,7 +20,7 @@ import { createServiceRef } from '../system/types'; * @public **/ export type BackendLifecycleShutdownHook = { - fn: () => Promise; + fn: () => void | Promise; }; /** From 263283c7bd2e5f7197a6b6b53c25b0af99e560ee Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 10 Nov 2022 15:31:42 +0100 Subject: [PATCH 10/68] update api docs Signed-off-by: Johan Haals --- packages/backend-plugin-api/api-report.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index 0183707735..b73a85c32e 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -26,9 +26,14 @@ export interface BackendFeature { // @public (undocumented) export interface BackendLifecycle { - addShutdownHook(options: ShutdownHookOptions): void; + addShutdownHook(options: BackendLifecycleShutdownHook): void; } +// @public (undocumented) +export type BackendLifecycleShutdownHook = { + fn: () => void | Promise; +}; + // @public (undocumented) export interface BackendModuleConfig { // (undocumented) @@ -243,11 +248,6 @@ export type ServiceRef< $$ref: 'service'; }; -// @public (undocumented) -export type ShutdownHookOptions = { - fn: () => Promise; -}; - // @public (undocumented) export const tokenManagerServiceRef: ServiceRef; From f3ac47b6c61dc0e2517dcf9b6b7ec26eaf335da6 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 10 Nov 2022 16:27:07 +0100 Subject: [PATCH 11/68] Handle multiple signals, prevent concurrent calls Signed-off-by: Johan Haals --- .../implementations/lifecycleService.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/backend-app-api/src/services/implementations/lifecycleService.ts b/packages/backend-app-api/src/services/implementations/lifecycleService.ts index 81f1b16b53..0ee0946469 100644 --- a/packages/backend-app-api/src/services/implementations/lifecycleService.ts +++ b/packages/backend-app-api/src/services/implementations/lifecycleService.ts @@ -24,8 +24,13 @@ import { } from '@backstage/backend-plugin-api'; import { Logger } from 'winston'; +const CALLBACKS = ['SIGTERM', 'SIGINT', 'beforeExit']; class BackendLifecycleImpl { - constructor(private readonly logger: Logger) {} + constructor(private readonly logger: Logger) { + CALLBACKS.map(signal => process.on(signal, () => this.shutdown())); + } + + #isCalled = false; #shutdownTasks: Array = []; @@ -36,6 +41,11 @@ class BackendLifecycleImpl { } async shutdown(): Promise { + if (this.#isCalled) { + return; + } + this.#isCalled = true; + this.logger.info(`Running ${this.#shutdownTasks.length} shutdown tasks...`); await Promise.all( this.#shutdownTasks.map(hook => @@ -47,8 +57,8 @@ class BackendLifecycleImpl { ); }) .then(() => - this.logger.debug( - `Successfully ran hook registered by plugin ${hook.pluginId}`, + this.logger.info( + `Successfully ran shutdown hook registered by plugin ${hook.pluginId}`, ), ), ), @@ -77,7 +87,6 @@ export const lifecycleFactory = createServiceFactory({ const rootLifecycle = new BackendLifecycleImpl( loggerToWinstonLogger(logger), ); - process.on('SIGTERM', async () => await rootLifecycle.shutdown()); return async ({ plugin }) => { return new PluginScopedLifecycleImpl(rootLifecycle, plugin.getId()); }; From cf79f4b68a6431ed55f9fae168f5d6346504972c Mon Sep 17 00:00:00 2001 From: Michael Stergianis Date: Thu, 10 Nov 2022 17:21:58 -0500 Subject: [PATCH 12/68] docs: Replaces use of CatalogResultListItem with correct symbol I copied the docs and noticed that the example didn't compile. According to the changelog this is the fix Signed-off-by: Michael Stergianis --- docs/features/search/getting-started.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/features/search/getting-started.md b/docs/features/search/getting-started.md index 71b9c276d9..726d1557d0 100644 --- a/docs/features/search/getting-started.md +++ b/docs/features/search/getting-started.md @@ -68,7 +68,7 @@ export const searchPage = ( switch (result.type) { case 'software-catalog': return ( - { It's good practice for search results to highlight information that was used to return it in the first place! The code below highlights how you might specify a -custom result item component, using the `` component as +custom result item component, using the `` component as an example: ```tsx {7-13} @@ -265,7 +265,7 @@ an example: switch (result.type) { case 'software-catalog': return ( - Date: Fri, 11 Nov 2022 14:51:37 -0600 Subject: [PATCH 13/68] Added custom provider annotation details Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- docs/features/software-catalog/external-integrations.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 65f97fb8da..f3387d31f0 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -159,7 +159,12 @@ Check out the numbered markings - let's go through them one by one. the outcome of that. This example issues a `fetch` to the right service and issues a full refresh of its entity bucket based on that. 5. The method translates the foreign data model to the native `Entity` form, as - expected by the catalog. + expected by the catalog. Make sure that in this method you include the + `backstage.io/managed-by-location` and `backstage.io/managed-by-origin-location` + annotations on your `Entity`. If these are not present they will not show up + in the Catalog and you will see warnings in your logs. The + [Well-known Annotations](./well-known-annotations.md#backstageiomanaged-by-location) + documentation has guidance on what values to use for these. 6. Finally, we issue a "mutation" to the catalog. This persists the entities in our own bucket, along with an optional `locationKey` that's used for conflict checks. But this is a bigger topic - mutations warrant their own explanatory From 3626b7358e0027cb8ac227c6aea8ce1f27335133 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Sun, 13 Nov 2022 13:30:17 -0600 Subject: [PATCH 14/68] Updated content based on feedback Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- docs/features/software-catalog/external-integrations.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index f3387d31f0..66813cc4d2 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -159,10 +159,10 @@ Check out the numbered markings - let's go through them one by one. the outcome of that. This example issues a `fetch` to the right service and issues a full refresh of its entity bucket based on that. 5. The method translates the foreign data model to the native `Entity` form, as - expected by the catalog. Make sure that in this method you include the - `backstage.io/managed-by-location` and `backstage.io/managed-by-origin-location` - annotations on your `Entity`. If these are not present they will not show up - in the Catalog and you will see warnings in your logs. The + expected by the catalog. The `Entity` must include the + `backstage.io/managed-by-location` and + `backstage.io/managed-by-origin-location annotations`; otherwise, it will not + appear in the Catalog and will generate warning logs. The [Well-known Annotations](./well-known-annotations.md#backstageiomanaged-by-location) documentation has guidance on what values to use for these. 6. Finally, we issue a "mutation" to the catalog. This persists the entities in From e982f77fe3f0f651c9007b938c66b7d3710238e9 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 14 Nov 2022 13:46:21 +0100 Subject: [PATCH 15/68] add changeset Signed-off-by: Johan Haals --- .changeset/silly-wolves-remember.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/silly-wolves-remember.md diff --git a/.changeset/silly-wolves-remember.md b/.changeset/silly-wolves-remember.md new file mode 100644 index 0000000000..f39812db88 --- /dev/null +++ b/.changeset/silly-wolves-remember.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Registered shutdown hook in experimental catalog plugin. From d6dbf1792bfa0bf5fc5f0ddaf3ab15599f01f544 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 14 Nov 2022 13:54:13 +0100 Subject: [PATCH 16/68] add changesets Signed-off-by: Johan Haals --- .changeset/old-keys-leave.md | 5 +++++ .changeset/twenty-dodos-wash.md | 5 +++++ .changeset/young-turkeys-relax.md | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 .changeset/old-keys-leave.md create mode 100644 .changeset/twenty-dodos-wash.md create mode 100644 .changeset/young-turkeys-relax.md diff --git a/.changeset/old-keys-leave.md b/.changeset/old-keys-leave.md new file mode 100644 index 0000000000..d80574b2e4 --- /dev/null +++ b/.changeset/old-keys-leave.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +Added `lifecycleFactory` implementation. diff --git a/.changeset/twenty-dodos-wash.md b/.changeset/twenty-dodos-wash.md new file mode 100644 index 0000000000..d21088a953 --- /dev/null +++ b/.changeset/twenty-dodos-wash.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Added `lifecycleFactory` to default service factories. diff --git a/.changeset/young-turkeys-relax.md b/.changeset/young-turkeys-relax.md new file mode 100644 index 0000000000..c53cd984a6 --- /dev/null +++ b/.changeset/young-turkeys-relax.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-plugin-api': patch +--- + +Added initial support for registering shutdown hooks via `lifecycleServiceRef`. From 4bc0ed33ca8db31d3a0f4ffdf6108eff9d307c77 Mon Sep 17 00:00:00 2001 From: Maximilian Vorbrodt Date: Mon, 14 Nov 2022 15:14:33 +0100 Subject: [PATCH 17/68] bring back old scrolling behaviour Signed-off-by: Maximilian Vorbrodt --- .../src/components/DependencyGraph/DependencyGraph.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 3ecdbedddf..64d39a4425 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -259,8 +259,6 @@ export function DependencyGraph( ); } - container.on('wheel', e => e.preventDefault()); - if (zoom === 'enabled') { enableZoom(); } else if (zoom === 'enable-on-click') { From d4368d1f9498283e9f3d40dbb86e53a15116d91a Mon Sep 17 00:00:00 2001 From: Maximilian Vorbrodt Date: Mon, 14 Nov 2022 15:16:35 +0100 Subject: [PATCH 18/68] make graph fill only visiable space on the page Signed-off-by: Maximilian Vorbrodt --- .../src/components/DependencyGraph/DependencyGraph.tsx | 4 +++- .../src/components/GroupsExplorerContent/GroupsDiagram.tsx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 64d39a4425..5dd8664107 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -223,6 +223,7 @@ export function DependencyGraph( const maxWidth = Math.max(graphWidth, containerWidth); const maxHeight = Math.max(graphHeight, containerHeight); + const minHeight = Math.min(graphHeight, containerHeight); const containerRef = React.useMemo( () => @@ -393,7 +394,8 @@ export function DependencyGraph( diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx index c366d1c6c1..95c6d86d27 100644 --- a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx @@ -44,7 +44,7 @@ import useAsync from 'react-use/lib/useAsync'; const useStyles = makeStyles((theme: BackstageTheme) => ({ graph: { flex: 1, - minHeight: 0, + minHeight: '100%', }, graphWrapper: { position: 'relative', From fd47108dedca4b5ac49278bac32a0ea6e8fa82ad Mon Sep 17 00:00:00 2001 From: Maximilian Vorbrodt Date: Mon, 14 Nov 2022 15:23:18 +0100 Subject: [PATCH 19/68] update changeset Signed-off-by: Maximilian Vorbrodt --- .changeset/cuddly-jeans-laugh.md | 6 ------ .changeset/lucky-carrots-stare.md | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 .changeset/cuddly-jeans-laugh.md create mode 100644 .changeset/lucky-carrots-stare.md diff --git a/.changeset/cuddly-jeans-laugh.md b/.changeset/cuddly-jeans-laugh.md deleted file mode 100644 index 6dadb85022..0000000000 --- a/.changeset/cuddly-jeans-laugh.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/plugin-explore': patch ---- - -Adds a background to the group diagram and prevents weird scroll/zoom behavior when hovering the graph. With this fix the user can only zoom in and out while hovering the graph. If the cursor is not hovering the graph the user can scroll up and down on the page. In total this creates more consistent scrolling or zooming behavior. diff --git a/.changeset/lucky-carrots-stare.md b/.changeset/lucky-carrots-stare.md new file mode 100644 index 0000000000..81ad1b7268 --- /dev/null +++ b/.changeset/lucky-carrots-stare.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-explore': patch +--- + +Adds a background to GroupsDiagram and makes the graph fill up all the visible space on the page for, making it so the user does not have to scroll up or down on the page. From a7bc5d082d43edc404128e4e7315b6e0ed11979f Mon Sep 17 00:00:00 2001 From: Maximilian Vorbrodt Date: Mon, 14 Nov 2022 15:48:35 +0100 Subject: [PATCH 20/68] fix code style issue Signed-off-by: Maximilian Vorbrodt --- .../src/components/DependencyGraph/DependencyGraph.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 5dd8664107..5b26629082 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -394,7 +394,7 @@ export function DependencyGraph( From c94d42b58b737dbcba19aaeb21940ba8ee6efe1f Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 16 Nov 2022 09:09:12 +0100 Subject: [PATCH 21/68] document, add tests Signed-off-by: Johan Haals --- packages/backend-app-api/api-report.md | 2 +- .../implementations/lifecycleService.test.ts | 47 +++++++++++++++++++ .../implementations/lifecycleService.ts | 6 ++- 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 packages/backend-app-api/src/services/implementations/lifecycleService.test.ts diff --git a/packages/backend-app-api/api-report.md b/packages/backend-app-api/api-report.md index 5d200be93c..5008b1efe1 100644 --- a/packages/backend-app-api/api-report.md +++ b/packages/backend-app-api/api-report.md @@ -67,7 +67,7 @@ export type HttpRouterFactoryOptions = { indexPlugin?: string; }; -// @public (undocumented) +// @public export const lifecycleFactory: ( options?: undefined, ) => ServiceFactory; diff --git a/packages/backend-app-api/src/services/implementations/lifecycleService.test.ts b/packages/backend-app-api/src/services/implementations/lifecycleService.test.ts new file mode 100644 index 0000000000..c0b69a3bef --- /dev/null +++ b/packages/backend-app-api/src/services/implementations/lifecycleService.test.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getVoidLogger } from '@backstage/backend-common'; +import { BackendLifecycleImpl } from './lifecycleService'; + +describe('lifecycleService', () => { + it('should execute registered shutdown hook', async () => { + const service = new BackendLifecycleImpl(getVoidLogger()); + const hook = jest.fn(); + service.addShutdownHook({ + pluginId: 'test', + fn: async () => { + hook(); + }, + }); + // should not execute the hook more than once. + await service.shutdown(); + await service.shutdown(); + await service.shutdown(); + expect(hook).toHaveBeenCalledTimes(1); + }); + + it('should not throw errors', async () => { + const service = new BackendLifecycleImpl(getVoidLogger()); + service.addShutdownHook({ + pluginId: 'test', + fn: async () => { + throw new Error('oh no'); + }, + }); + await expect(service.shutdown()).resolves.toBeUndefined(); + }); +}); diff --git a/packages/backend-app-api/src/services/implementations/lifecycleService.ts b/packages/backend-app-api/src/services/implementations/lifecycleService.ts index 0ee0946469..7d41a7a276 100644 --- a/packages/backend-app-api/src/services/implementations/lifecycleService.ts +++ b/packages/backend-app-api/src/services/implementations/lifecycleService.ts @@ -25,7 +25,7 @@ import { import { Logger } from 'winston'; const CALLBACKS = ['SIGTERM', 'SIGINT', 'beforeExit']; -class BackendLifecycleImpl { +export class BackendLifecycleImpl { constructor(private readonly logger: Logger) { CALLBACKS.map(signal => process.on(signal, () => this.shutdown())); } @@ -76,7 +76,9 @@ class PluginScopedLifecycleImpl implements BackendLifecycle { } } -/** @public */ +/** + * Allows plugins to register shutdown hooks that are run when the process is about to exit. + * @public */ export const lifecycleFactory = createServiceFactory({ service: lifecycleServiceRef, deps: { From 16891a212c3f564cb48b742079d2ed9019deb7d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 26 Oct 2022 17:06:01 +0200 Subject: [PATCH 22/68] added a by-refs batch endpoint for entities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/fast-lies-remain.md | 8 +++ plugins/catalog-backend/src/catalog/types.ts | 35 +++++++++++ .../service/AuthorizedEntitiesCatalog.test.ts | 62 +++++++++++++++++++ .../src/service/AuthorizedEntitiesCatalog.ts | 33 ++++++++++ .../service/DefaultEntitiesCatalog.test.ts | 54 ++++++++++++++++ .../src/service/DefaultEntitiesCatalog.ts | 34 ++++++++++ .../src/service/createRouter.test.ts | 35 +++++++++++ .../src/service/createRouter.ts | 21 +++++-- .../service/request/entitiesBatchRequest.ts | 33 ++++++++++ .../src/service/request/index.ts | 1 + 10 files changed, 311 insertions(+), 5 deletions(-) create mode 100644 .changeset/fast-lies-remain.md create mode 100644 plugins/catalog-backend/src/service/request/entitiesBatchRequest.ts diff --git a/.changeset/fast-lies-remain.md b/.changeset/fast-lies-remain.md new file mode 100644 index 0000000000..74be117920 --- /dev/null +++ b/.changeset/fast-lies-remain.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-catalog-backend': minor +--- + +Added new `POST /entities/by-refs` endpoint, which allows you to efficiently +batch-fetch entities by their entity ref. This can be useful e.g. in graphql +resolvers or similar contexts where you need to fetch many entities at the same +time. diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index 34060814aa..84673a3900 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -80,6 +80,36 @@ export type EntitiesResponse = { pageInfo: PageInfo; }; +/** + * A request for a batch of entities. + */ +export interface EntitiesBatchRequest { + /** + * The refs for which to fetch entities. + */ + entityRefs: string[]; + /** + * Any additional filters to apply in the selection of the entities. + */ + filter?: EntityFilter; + /** + * Strips out only the parts of the entity bodies to include in the response. + */ + fields?: (entity: Entity) => Entity; + /** + * The optional token that authorizes the action. + */ + authorizationToken?: string; +} + +export interface EntitiesBatchResponse { + /** + * The list of entities, in the same order as the refs in the request. Entries + * that are null signify that no entity existed with that ref. + */ + items: Array; +} + export type EntityAncestryResponse = { rootEntityRef: string; items: Array<{ @@ -130,6 +160,11 @@ export interface EntitiesCatalog { */ entities(request?: EntitiesRequest): Promise; + /** + * Fetches a batch of entities. + */ + entitiesBatch(request: EntitiesBatchRequest): Promise; + /** * Removes a single entity. * diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts index 66764dc89f..cbaf876071 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts @@ -24,6 +24,7 @@ import { AuthorizedEntitiesCatalog } from './AuthorizedEntitiesCatalog'; describe('AuthorizedEntitiesCatalog', () => { const fakeCatalog = { entities: jest.fn(), + entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), facets: jest.fn(), @@ -92,6 +93,67 @@ describe('AuthorizedEntitiesCatalog', () => { }); }); + describe('entitiesBatch', () => { + it('returns empty response on DENY', async () => { + fakePermissionApi.authorizeConditional.mockResolvedValue([ + { result: AuthorizeResult.DENY }, + ]); + const catalog = createCatalog(); + + await expect( + catalog.entitiesBatch({ + entityRefs: ['component:default/component-a'], + authorizationToken: 'abcd', + }), + ).resolves.toEqual({ + items: [null], + }); + + expect(fakeCatalog.entitiesBatch).not.toHaveBeenCalled(); + }); + + it('calls underlying catalog method with correct filter on CONDITIONAL', async () => { + fakePermissionApi.authorizeConditional.mockResolvedValue([ + { + result: AuthorizeResult.CONDITIONAL, + conditions: { + rule: 'IS_ENTITY_KIND', + params: { kinds: ['b'] }, + }, + }, + ]); + const catalog = createCatalog(isEntityKind); + + await catalog.entitiesBatch({ + entityRefs: ['component:default/component-a'], + authorizationToken: 'abcd', + }); + + expect(fakeCatalog.entitiesBatch).toHaveBeenCalledWith({ + entityRefs: ['component:default/component-a'], + authorizationToken: 'abcd', + filter: { key: 'kind', values: ['b'] }, + }); + }); + + it('calls underlying catalog method on ALLOW', async () => { + fakePermissionApi.authorizeConditional.mockResolvedValue([ + { result: AuthorizeResult.ALLOW }, + ]); + const catalog = createCatalog(); + + await catalog.entitiesBatch({ + entityRefs: ['component:default/component-a'], + authorizationToken: 'abcd', + }); + + expect(fakeCatalog.entitiesBatch).toHaveBeenCalledWith({ + entityRefs: ['component:default/component-a'], + authorizationToken: 'abcd', + }); + }); + }); + describe('removeEntityByUid', () => { it('throws error on DENY', async () => { fakeCatalog.entities.mockResolvedValue({ diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts index 0e861a39e1..c62e9f9a84 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts @@ -26,6 +26,8 @@ import { } from '@backstage/plugin-permission-common'; import { ConditionTransformer } from '@backstage/plugin-permission-node'; import { + EntitiesBatchRequest, + EntitiesBatchResponse, EntitiesCatalog, EntitiesRequest, EntitiesResponse, @@ -73,6 +75,37 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog { return this.entitiesCatalog.entities(request); } + async entitiesBatch( + request: EntitiesBatchRequest, + ): Promise { + const authorizeDecision = ( + await this.permissionApi.authorizeConditional( + [{ permission: catalogEntityReadPermission }], + { token: request?.authorizationToken }, + ) + )[0]; + + if (authorizeDecision.result === AuthorizeResult.DENY) { + return { + items: new Array(request.entityRefs.length).fill(null), + }; + } + + if (authorizeDecision.result === AuthorizeResult.CONDITIONAL) { + const permissionFilter: EntityFilter = this.transformConditions( + authorizeDecision.conditions, + ); + return this.entitiesCatalog.entitiesBatch({ + ...request, + filter: request?.filter + ? { allOf: [permissionFilter, request.filter] } + : permissionFilter, + }); + } + + return this.entitiesCatalog.entitiesBatch(request); + } + async removeEntityByUid( uid: string, options?: { authorizationToken?: string }, diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 49d4fe9e19..515a862515 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -534,6 +534,60 @@ describe('DefaultEntitiesCatalog', () => { ); }); + describe('entitiesBatch', () => { + it.each(databases.eachSupportedId())( + 'queries for entities by ref, including duplicates, and gracefully returns null for missing entities', + async databaseId => { + const { knex } = await createDatabase(databaseId); + + await addEntity( + knex, + { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'one' }, + spec: {}, + relations: [], + }, + [], + ); + await addEntity( + knex, + { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'two' }, + spec: {}, + relations: [], + }, + [], + ); + + const catalog = new DefaultEntitiesCatalog(knex, stitcher); + + const { items } = await catalog.entitiesBatch({ + entityRefs: [ + 'k:default/two', + 'k:default/one', + 'k:default/two', + 'not-even-a-ref', + 'k:default/does-not-exist', + 'k:default/two', + ], + }); + + expect(items.map(e => e && stringifyEntityRef(e))).toEqual([ + 'k:default/two', + 'k:default/one', + 'k:default/two', + null, + null, + 'k:default/two', + ]); + }, + ); + }); + describe('removeEntityByUid', () => { it.each(databases.eachSupportedId())( 'also clears parent hashes', diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index dc84b12484..81de6484c3 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -23,6 +23,8 @@ import { InputError, NotFoundError } from '@backstage/errors'; import { Knex } from 'knex'; import lodash from 'lodash'; import { + EntitiesBatchRequest, + EntitiesBatchResponse, EntitiesCatalog, EntitiesRequest, EntitiesResponse, @@ -237,6 +239,38 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { }; } + async entitiesBatch( + request: EntitiesBatchRequest, + ): Promise { + const lookup = new Map(); + + for (const chunk of lodash.chunk(request.entityRefs, 200)) { + let query = this.database('final_entities') + .innerJoin('refresh_state', { + 'refresh_state.entity_id': 'final_entities.entity_id', + }) + .select({ + entityRef: 'refresh_state.entity_ref', + entity: 'final_entities.final_entity', + }) + .whereIn('refresh_state.entity_ref', chunk); + if (request?.filter) { + query = parseFilter(request.filter, query, this.database); + } + for (const row of await query) { + lookup.set(row.entityRef, row.entity ? JSON.parse(row.entity) : null); + } + } + + let items = request.entityRefs.map(ref => lookup.get(ref) ?? null); + + if (request.fields) { + items = items.map(e => e && request.fields!(e)); + } + + return { items }; + } + async removeEntityByUid(uid: string): Promise { // Clear the hashed state of the immediate parents of the deleted entity. // This makes sure that when they get reprocessed, their output is written diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index f383df7364..541359feb6 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -48,6 +48,7 @@ describe('createRouter readonly disabled', () => { beforeAll(async () => { entitiesCatalog = { entities: jest.fn(), + entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), facets: jest.fn(), @@ -257,6 +258,38 @@ describe('createRouter readonly disabled', () => { }); }); + describe('POST /entities/by-refs', () => { + it.each([ + '', + 'not json', + '[', + '[]', + '{}', + '{"unknown":7}', + '{"entityRefs":7}', + '{"entityRefs":[7]}', + ])('properly rejects malformed request body, %p', async p => { + await expect( + request(app) + .post('/entities/by-refs') + .set('Content-Type', 'application/json') + .send(p), + ).resolves.toMatchObject({ status: 400 }); + }); + + it('can fetch entities by refs', async () => { + const entity: Entity = {} as any; + entitiesCatalog.entitiesBatch.mockResolvedValue({ items: [entity] }); + const response = await request(app) + .post('/entities/by-refs') + .set('Content-Type', 'application/json') + .send('{"entityRefs":["a"]}'); + expect(entitiesCatalog.entitiesBatch).toHaveBeenCalledTimes(1); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ items: [entity] }); + }); + }); + describe('GET /locations', () => { it('happy path: lists locations', async () => { const locations: Location[] = [ @@ -517,6 +550,7 @@ describe('createRouter readonly enabled', () => { beforeAll(async () => { entitiesCatalog = { entities: jest.fn(), + entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), facets: jest.fn(), @@ -706,6 +740,7 @@ describe('NextRouter permissioning', () => { beforeAll(async () => { entitiesCatalog = { entities: jest.fn(), + entitiesBatch: jest.fn(), removeEntityByUid: jest.fn(), entityAncestry: jest.fn(), facets: jest.fn(), diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index 5267519c33..4680dbb899 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -28,24 +28,25 @@ import express from 'express'; import Router from 'express-promise-router'; import { Logger } from 'winston'; import yn from 'yn'; +import { z } from 'zod'; import { EntitiesCatalog } from '../catalog/types'; import { LocationAnalyzer } from '../ingestion/types'; +import { CatalogProcessingOrchestrator } from '../processing/types'; +import { validateEntityEnvelope } from '../processing/util'; import { basicEntityFilter, + entitiesBatchRequest, parseEntityFilterParams, parseEntityPaginationParams, parseEntityTransformParams, } from './request'; +import { parseEntityFacetParams } from './request/parseEntityFacetParams'; +import { LocationService, RefreshOptions, RefreshService } from './types'; import { disallowReadonlyMode, locationInput, validateRequestBody, } from './util'; -import { z } from 'zod'; -import { parseEntityFacetParams } from './request/parseEntityFacetParams'; -import { RefreshOptions, LocationService, RefreshService } from './types'; -import { CatalogProcessingOrchestrator } from '../processing/types'; -import { validateEntityEnvelope } from '../processing/util'; /** * Options used by {@link createRouter}. @@ -173,6 +174,16 @@ export async function createRouter( res.status(200).json(response); }, ) + .post('/entities/by-refs', async (req, res) => { + const request = entitiesBatchRequest(req); + const token = getBearerToken(req.header('authorization')); + const response = await entitiesCatalog.entitiesBatch({ + entityRefs: request.entityRefs, + fields: parseEntityTransformParams(req.query), + authorizationToken: token, + }); + res.status(200).json(response); + }) .get('/entity-facets', async (req, res) => { const response = await entitiesCatalog.facets({ filter: parseEntityFilterParams(req.query), diff --git a/plugins/catalog-backend/src/service/request/entitiesBatchRequest.ts b/plugins/catalog-backend/src/service/request/entitiesBatchRequest.ts new file mode 100644 index 0000000000..b3a91f9491 --- /dev/null +++ b/plugins/catalog-backend/src/service/request/entitiesBatchRequest.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { InputError } from '@backstage/errors'; +import { Request } from 'express'; +import { z } from 'zod'; + +const schema = z.object({ + entityRefs: z.array(z.string()), +}); + +export function entitiesBatchRequest(req: Request) { + try { + return schema.parse(req.body); + } catch (error) { + throw new InputError( + `Malformed request body (did you remember to specify an application/json content type?), ${error.message}`, + ); + } +} diff --git a/plugins/catalog-backend/src/service/request/index.ts b/plugins/catalog-backend/src/service/request/index.ts index 4ab579fbb6..0b09355e72 100644 --- a/plugins/catalog-backend/src/service/request/index.ts +++ b/plugins/catalog-backend/src/service/request/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +export { entitiesBatchRequest } from './entitiesBatchRequest'; export { basicEntityFilter } from './basicEntityFilter'; export { parseEntityFilterParams } from './parseEntityFilterParams'; export { parseEntityPaginationParams } from './parseEntityPaginationParams'; From 00d90b520a954d84c2f049272b9281176382201c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 14 Nov 2022 17:02:24 +0100 Subject: [PATCH 23/68] implement by-refs batch fetching in the catalog client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/chilly-ads-lay.md | 5 + packages/catalog-client/api-report.md | 34 +++- .../catalog-client/src/CatalogClient.test.ts | 29 +++ packages/catalog-client/src/CatalogClient.ts | 36 ++++ packages/catalog-client/src/types/api.ts | 188 ++++++++++++------ packages/catalog-client/src/types/index.ts | 6 +- 6 files changed, 234 insertions(+), 64 deletions(-) create mode 100644 .changeset/chilly-ads-lay.md diff --git a/.changeset/chilly-ads-lay.md b/.changeset/chilly-ads-lay.md new file mode 100644 index 0000000000..844eaa8dc7 --- /dev/null +++ b/.changeset/chilly-ads-lay.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-client': minor +--- + +**BREAKING PRODUCERS**: Added a new `getEntitiesByRefs` endpoint to `CatalogApi`, for efficient batch fetching of entities by ref. diff --git a/packages/catalog-client/api-report.md b/packages/catalog-client/api-report.md index 85fc389af7..2f76be79e2 100644 --- a/packages/catalog-client/api-report.md +++ b/packages/catalog-client/api-report.md @@ -34,6 +34,10 @@ export interface CatalogApi { request?: GetEntitiesRequest, options?: CatalogRequestOptions, ): Promise; + getEntitiesByRefs( + request: GetEntitiesByRefsRequest, + options?: CatalogRequestOptions, + ): Promise; getEntityAncestors( request: GetEntityAncestorsRequest, options?: CatalogRequestOptions, @@ -91,6 +95,10 @@ export class CatalogClient implements CatalogApi { request?: GetEntitiesRequest, options?: CatalogRequestOptions, ): Promise; + getEntitiesByRefs( + request: GetEntitiesByRefsRequest, + options?: CatalogRequestOptions, + ): Promise; getEntityAncestors( request: GetEntityAncestorsRequest, options?: CatalogRequestOptions, @@ -145,14 +153,30 @@ export interface CatalogRequestOptions { export const ENTITY_STATUS_CATALOG_PROCESSING_TYPE = 'backstage.io/catalog-processing'; +// @public +export type EntityFieldsQuery = string[]; + +// @public +export type EntityFilterQuery = + | Record[] + | Record; + +// @public +export interface GetEntitiesByRefsRequest { + entityRefs: string[]; + fields?: EntityFieldsQuery | undefined; +} + +// @public +export interface GetEntitiesByRefsResponse { + items: Array; +} + // @public export interface GetEntitiesRequest { after?: string; - fields?: string[] | undefined; - filter?: - | Record[] - | Record - | undefined; + fields?: EntityFieldsQuery; + filter?: EntityFilterQuery; limit?: number; offset?: number; } diff --git a/packages/catalog-client/src/CatalogClient.test.ts b/packages/catalog-client/src/CatalogClient.test.ts index 5a344f49a3..1206d557c1 100644 --- a/packages/catalog-client/src/CatalogClient.test.ts +++ b/packages/catalog-client/src/CatalogClient.test.ts @@ -195,6 +195,35 @@ describe('CatalogClient', () => { }); }); + describe('getEntitiesByRefs', () => { + it('encodes and decodes the query correctly', async () => { + const entity = { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'Test2', + namespace: 'test1', + }, + }; + server.use( + rest.post(`${mockBaseUrl}/entities/by-refs`, async (req, res, ctx) => { + expect(req.url.searchParams.get('fields')).toBe('a,b'); + await expect(req.json()).resolves.toEqual({ + entityRefs: ['k:n/a', 'k:n/b'], + }); + return res(ctx.json({ items: [entity, null] })); + }), + ); + + const response = await client.getEntitiesByRefs( + { entityRefs: ['k:n/a', 'k:n/b'], fields: ['a', 'b'] }, + { token }, + ); + + expect(response).toEqual({ items: [entity, null] }); + }); + }); + describe('getEntityByRef', () => { const existingEntity: Entity = { apiVersion: 'v1', diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index d67392d80d..d15f382ddd 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -37,6 +37,8 @@ import { GetEntityFacetsRequest, GetEntityFacetsResponse, ValidateEntityResponse, + GetEntitiesByRefsRequest, + GetEntitiesByRefsResponse, } from './types/api'; import { DiscoveryApi } from './types/discovery'; import { FetchApi } from './types/fetch'; @@ -169,6 +171,40 @@ export class CatalogClient implements CatalogApi { return { items: entities.sort(refCompare) }; } + /** + * {@inheritdoc CatalogApi.getEntitiesByRefs} + */ + async getEntitiesByRefs( + request: GetEntitiesByRefsRequest, + options?: CatalogRequestOptions, + ): Promise { + const params: string[] = []; + if (request.fields?.length) { + params.push(`fields=${request.fields.map(encodeURIComponent).join(',')}`); + } + + const baseUrl = await this.discoveryApi.getBaseUrl('catalog'); + const query = params.length ? `?${params.join('&')}` : ''; + const url = `${baseUrl}/entities/by-refs${query}`; + + const response = await this.fetchApi.fetch(url, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'POST', + body: JSON.stringify({ entityRefs: request.entityRefs }), + }); + + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + + const { items } = await response.json(); + + return { items }; + } + /** * {@inheritdoc CatalogApi.getEntityByRef} */ diff --git a/packages/catalog-client/src/types/api.ts b/packages/catalog-client/src/types/api.ts index d3d02022f3..771adaaeee 100644 --- a/packages/catalog-client/src/types/api.ts +++ b/packages/catalog-client/src/types/api.ts @@ -29,6 +29,75 @@ export const CATALOG_FILTER_EXISTS = Symbol.for( 'CATALOG_FILTER_EXISTS_0e15b590c0b343a2bae3e787e84c2111', ); +/** + * A key-value based filter expression for entities. + * + * @remarks + * + * Each key of a record is a dot-separated path into the entity structure, e.g. + * `metadata.name`. + * + * The values are literal values to match against. As a value you can also pass + * in the symbol `CATALOG_FILTER_EXISTS` (exported from this package), which + * means that you assert on the existence of that key, no matter what its value + * is. + * + * All matching of keys and values is case insensitive. + * + * If multiple filter sets are given as an array, then there is effectively an + * OR between each filter set. + * + * Within one filter set, there is effectively an AND between the various keys. + * + * Within one key, if there are more than one value, then there is effectively + * an OR between them. + * + * Example: For an input of + * + * ``` + * [ + * { kind: ['API', 'Component'] }, + * { 'metadata.name': 'a', 'metadata.namespace': 'b' } + * ] + * ``` + * + * This effectively means + * + * ``` + * (kind = EITHER 'API' OR 'Component') + * OR + * (metadata.name = 'a' AND metadata.namespace = 'b' ) + * ``` + * + * @public + */ +export type EntityFilterQuery = + | Record[] + | Record; + +/** + * A set of dot-separated paths into an entity's keys, showing what parts of an + * entity to include in a response, and excluding all others. + * + * @remarks + * + * Example: For an input of `['kind', 'metadata.annotations']`, then response + * objects will be shaped like + * + * ``` + * { + * "kind": "Component", + * "metadata": { + * "annotations": { + * "foo": "bar" + * } + * } + * } + * ``` + * @public + */ +export type EntityFieldsQuery = string[]; + /** * The request type for {@link CatalogClient.getEntities}. * @@ -36,67 +105,14 @@ export const CATALOG_FILTER_EXISTS = Symbol.for( */ export interface GetEntitiesRequest { /** - * If given, return only entities that match the given patterns. - * - * @remarks - * - * If multiple filter sets are given as an array, then there is effectively an - * OR between each filter set. - * - * Within one filter set, there is effectively an AND between the various - * keys. - * - * Within one key, if there are more than one value, then there is effectively - * an OR between them. - * - * Example: For an input of - * - * ``` - * [ - * { kind: ['API', 'Component'] }, - * { 'metadata.name': 'a', 'metadata.namespace': 'b' } - * ] - * ``` - * - * This effectively means - * - * ``` - * (kind = EITHER 'API' OR 'Component') - * OR - * (metadata.name = 'a' AND metadata.namespace = 'b' ) - * ``` - * - * Each key is a dot separated path in each object. - * - * As a value you can also pass in the symbol `CATALOG_FILTER_EXISTS` - * (exported from this package), which means that you assert on the existence - * of that key, no matter what its value is. + * If given, return only entities that match the given filter. */ - filter?: - | Record[] - | Record - | undefined; + filter?: EntityFilterQuery; /** - * If given, return only the parts of each entity that match those dot - * separated paths in each object. - * - * @remarks - * - * Example: For an input of `['kind', 'metadata.annotations']`, then response - * objects will be shaped like - * - * ``` - * { - * "kind": "Component", - * "metadata": { - * "annotations": { - * "foo": "bar" - * } - * } - * } - * ``` + * If given, return only the parts of each entity that match the field + * declarations. */ - fields?: string[] | undefined; + fields?: EntityFieldsQuery; /** * If given, skips over the first N items in the result set. */ @@ -121,6 +137,45 @@ export interface GetEntitiesResponse { items: Entity[]; } +/** + * The request type for {@link CatalogClient.getEntitiesByRefs}. + * + * @public + */ +export interface GetEntitiesByRefsRequest { + /** + * The list of entity refs to fetch. + * + * @remarks + * + * The returned list of entities will be in the same order as the refs, and + * null will be returned in those positions that were not found. + */ + entityRefs: string[]; + /** + * If given, return only the parts of each entity that match the field + * declarations. + */ + fields?: EntityFieldsQuery | undefined; +} + +/** + * The response type for {@link CatalogClient.getEntitiesByRefs}. + * + * @public + */ +export interface GetEntitiesByRefsResponse { + /** + * The returned list of entities. + * + * @remarks + * + * The list will be in the same order as the refs given in the request, and + * null will be returned in those positions that were not found. + */ + items: Array; +} + /** * The request type for {@link CatalogClient.getEntityAncestors}. * @@ -296,6 +351,23 @@ export interface CatalogApi { options?: CatalogRequestOptions, ): Promise; + /** + * Gets a batch of entities, by their entity refs. + * + * @remarks + * + * The output list of entities is of the same size and in the same order as + * the requested list of entity refs. Entries that are not found are returned + * as null. + * + * @param request - Request parameters + * @param options - Additional options + */ + getEntitiesByRefs( + request: GetEntitiesByRefsRequest, + options?: CatalogRequestOptions, + ): Promise; + /** * Gets entity ancestor information, i.e. the hierarchy of parent entities * whose processing resulted in a given entity appearing in the catalog. diff --git a/packages/catalog-client/src/types/index.ts b/packages/catalog-client/src/types/index.ts index 86f7b18ab0..5ac058e349 100644 --- a/packages/catalog-client/src/types/index.ts +++ b/packages/catalog-client/src/types/index.ts @@ -20,13 +20,17 @@ export type { AddLocationResponse, CatalogApi, CatalogRequestOptions, + EntityFieldsQuery, + EntityFilterQuery, + GetEntitiesByRefsRequest, + GetEntitiesByRefsResponse, GetEntitiesRequest, GetEntitiesResponse, GetEntityAncestorsRequest, GetEntityAncestorsResponse, - Location, GetEntityFacetsRequest, GetEntityFacetsResponse, + Location, ValidateEntityResponse, } from './api'; export { ENTITY_STATUS_CATALOG_PROCESSING_TYPE } from './status'; From dec733b6a02f545a7cedd4bdde078a6e4b30052c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Nov 2022 11:26:05 +0000 Subject: [PATCH 24/68] Update dependency @azure/arm-appservice to v13.0.3 Signed-off-by: Renovate Bot --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index b1531e5db9..1e8f50adc7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1100,17 +1100,17 @@ __metadata: linkType: hard "@azure/arm-appservice@npm:^13.0.1": - version: 13.0.2 - resolution: "@azure/arm-appservice@npm:13.0.2" + version: 13.0.3 + resolution: "@azure/arm-appservice@npm:13.0.3" dependencies: "@azure/abort-controller": ^1.0.0 "@azure/core-auth": ^1.3.0 - "@azure/core-client": ^1.5.0 + "@azure/core-client": ^1.6.1 "@azure/core-lro": ^2.2.0 "@azure/core-paging": ^1.2.0 "@azure/core-rest-pipeline": ^1.8.0 tslib: ^2.2.0 - checksum: 6d604c492a70a6cdd4d3ad3f634a74228dc081ed4f49128873e76bfa5e65ec8cce18b7b19319fd2b1b4edbd390dbd66c9ac48646f0ed80cae09cd2f1d05fb279 + checksum: 9cd73985a31744fd7da56654c1de2d1396e93fa0cc3527b7c2843e56913e2b0abd81d996a319447f2f289421c45ede6a87f820ed0f4414e7cd2afeb383ee8a0a languageName: node linkType: hard @@ -1143,7 +1143,7 @@ __metadata: languageName: node linkType: hard -"@azure/core-client@npm:^1.4.0, @azure/core-client@npm:^1.5.0": +"@azure/core-client@npm:^1.4.0, @azure/core-client@npm:^1.6.1": version: 1.6.1 resolution: "@azure/core-client@npm:1.6.1" dependencies: From 8bc81264b90d3521583cda421bbb97d362586077 Mon Sep 17 00:00:00 2001 From: Maximilian Vorbrodt Date: Thu, 17 Nov 2022 19:06:42 +0100 Subject: [PATCH 25/68] remove paper component Signed-off-by: Maximilian Vorbrodt --- .../GroupsExplorerContent/GroupsDiagram.tsx | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx index 95c6d86d27..d00eddb35e 100644 --- a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx @@ -35,7 +35,7 @@ import { getEntityRelations, } from '@backstage/plugin-catalog-react'; import { BackstageTheme } from '@backstage/theme'; -import { makeStyles, Typography, Paper, useTheme } from '@material-ui/core'; +import { makeStyles, Typography, useTheme } from '@material-ui/core'; import ZoomOutMap from '@material-ui/icons/ZoomOutMap'; import classNames from 'classnames'; import React from 'react'; @@ -43,14 +43,10 @@ import useAsync from 'react-use/lib/useAsync'; const useStyles = makeStyles((theme: BackstageTheme) => ({ graph: { - flex: 1, minHeight: '100%', }, graphWrapper: { - position: 'relative', - flex: 1, - minHeight: 0, - display: 'flex', + height: '100%', }, organizationNode: { fill: theme.palette.secondary.light, @@ -236,27 +232,25 @@ export function GroupsDiagram() { } return ( - <> - - +
+ - - Use pinch & zoom to move around - the diagram. - - - + + Use pinch & zoom to move around the + diagram. + +
); } From fd7ec12ec21c18a5b3d11ea55681a3595602c273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 18 Nov 2022 11:06:44 +0100 Subject: [PATCH 26/68] make a duplicate checks script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .github/workflows/ci.yml | 2 +- .github/workflows/verify_microsite.yml | 3 + microsite/yarn.lock | 99 ++------------- package.json | 9 +- scripts/verify-lockfile-duplicates.js | 135 ++++++++++++++++++++ storybook/yarn.lock | 168 ++----------------------- 6 files changed, 168 insertions(+), 248 deletions(-) create mode 100644 scripts/verify-lockfile-duplicates.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d826bc8bb4..eef5058690 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,7 +68,7 @@ jobs: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: verify yarn dependency duplicates - run: yarn dedupe --check + run: node scripts/verify-lockfile-duplicates.js - name: verify changesets run: node scripts/verify-changesets.js diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index 3a24befa84..4085f334f3 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -32,6 +32,9 @@ jobs: run: yarn install --immutable working-directory: microsite + - name: verify yarn dependency duplicates + run: node scripts/verify-lockfile-duplicates.js + - name: prettier run: yarn prettier:check working-directory: microsite diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 0a943bbe83..1fa6e492cb 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -281,14 +281,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.10.4, @babel/helper-validator-identifier@npm:^7.12.11": - version: 7.12.11 - resolution: "@babel/helper-validator-identifier@npm:7.12.11" - checksum: e604c6bf890704fc46c1ae13bf23afb242b810224ec3403bba67cdbf0d8dabfec4b82123d6dfb18135a0ee3f7f79218583c819363ebb5e04a0a49d8418db7fce - languageName: node - linkType: hard - -"@babel/helper-validator-identifier@npm:^7.18.6": +"@babel/helper-validator-identifier@npm:^7.10.4, @babel/helper-validator-identifier@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-validator-identifier@npm:7.18.6" checksum: e295254d616bbe26e48c196a198476ab4d42a73b90478c9842536cf910ead887f5af6b5c4df544d3052a25ccb3614866fa808dc1e3a5a4291acd444e243c0648 @@ -1241,18 +1234,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.10.4, @babel/types@npm:^7.10.5, @babel/types@npm:^7.12.1, @babel/types@npm:^7.12.10, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.12, @babel/types@npm:^7.12.5, @babel/types@npm:^7.12.6, @babel/types@npm:^7.12.7, @babel/types@npm:^7.4.4": - version: 7.12.12 - resolution: "@babel/types@npm:7.12.12" - dependencies: - "@babel/helper-validator-identifier": ^7.12.11 - lodash: ^4.17.19 - to-fast-properties: ^2.0.0 - checksum: 1eefe9468573a32491030f2986f820eb4289ff0b93e437b97f665e73cc8dae0ec0bdd95f37995bb3b70b5ce2a9a45a302affbbad497150021cf874c5ef10e361 - languageName: node - linkType: hard - -"@babel/types@npm:^7.8.3": +"@babel/types@npm:^7.10.4, @babel/types@npm:^7.10.5, @babel/types@npm:^7.12.1, @babel/types@npm:^7.12.10, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.12, @babel/types@npm:^7.12.5, @babel/types@npm:^7.12.6, @babel/types@npm:^7.12.7, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.18.10 resolution: "@babel/types@npm:7.18.10" dependencies: @@ -2917,7 +2899,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.3.3": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.3.3": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -2929,7 +2911,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4.3.1, debug@npm:^4.1.0": +"debug@npm:4.3.1": version: 4.3.1 resolution: "debug@npm:4.3.1" dependencies: @@ -4275,21 +4257,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.0, glob@npm:^7.0.5, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.6": - version: 7.2.0 - resolution: "glob@npm:7.2.0" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.0.4 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 78a8ea942331f08ed2e055cb5b9e40fe6f46f579d7fd3d694f3412fe5db23223d29b7fee1575440202e9a7ff9a72ab106a39fee39934c7bedafe5e5f8ae20134 - languageName: node - linkType: hard - -"glob@npm:^7.1.4": +"glob@npm:^7.0.0, glob@npm:^7.0.5, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -4430,14 +4398,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0": - version: 4.2.4 - resolution: "graceful-fs@npm:4.2.4" - checksum: 9d58c444eb4f391ce30b451aae8a8af2bd675d9f6f624719e97306f571ab89b2bd2b5f9025199bc63a2edfe2e53e7701554012f32a708148d53aa689163728cc - languageName: node - linkType: hard - -"graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": version: 4.2.10 resolution: "graceful-fs@npm:4.2.10" checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da @@ -5056,15 +5017,6 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.1.0": - version: 2.2.0 - resolution: "is-core-module@npm:2.2.0" - dependencies: - has: ^1.0.3 - checksum: 61e2aff4a7db4f8f7d5a97b484808af17290f4197b34a797cd3d3d27b6b448951064f8d3d6ceae4394fa9b7e6cf08aacd2ba7a17ef6352e922fe803580fbde56 - languageName: node - linkType: hard - "is-core-module@npm:^2.8.0": version: 2.8.1 resolution: "is-core-module@npm:2.8.1" @@ -6196,7 +6148,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:3.0.4, minimatch@npm:^3.0.4, minimatch@npm:~3.0.2": +"minimatch@npm:3.0.4, minimatch@npm:~3.0.2": version: 3.0.4 resolution: "minimatch@npm:3.0.4" dependencies: @@ -6205,7 +6157,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.1.1": +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -6941,7 +6893,7 @@ __metadata: languageName: node linkType: hard -"path-parse@npm:^1.0.6, path-parse@npm:^1.0.7": +"path-parse@npm:^1.0.7": version: 1.0.7 resolution: "path-parse@npm:1.0.7" checksum: 49abf3d81115642938a8700ec580da6e830dde670be21893c62f4e10bd7dd4c3742ddc603fe24f898cba7eb0c6bc1777f8d9ac14185d34540c6d4d80cd9cae8a @@ -7967,7 +7919,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6": +"resolve@npm:^1.1.6, resolve@npm:^1.10.0": version: 1.21.0 resolution: "resolve@npm:1.21.0" dependencies: @@ -7980,17 +7932,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.10.0": - version: 1.19.0 - resolution: "resolve@npm:1.19.0" - dependencies: - is-core-module: ^2.1.0 - path-parse: ^1.0.6 - checksum: a05b356e47b85ad3613d9e2a39a824f3c27f4fcad9c9ff6c7cc71a2e314c5904a90ab37481ad0069d03cab9eaaac6eb68aca1bc3355fdb05f1045cd50e2aacea - languageName: node - linkType: hard - -"resolve@patch:resolve@^1.1.6#~builtin": +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin": version: 1.21.0 resolution: "resolve@patch:resolve@npm%3A1.21.0#~builtin::version=1.21.0&hash=07638b" dependencies: @@ -8003,16 +7945,6 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.10.0#~builtin": - version: 1.19.0 - resolution: "resolve@patch:resolve@npm%3A1.19.0#~builtin::version=1.19.0&hash=07638b" - dependencies: - is-core-module: ^2.1.0 - path-parse: ^1.0.6 - checksum: 2443b94d347e6946c87c85faf13071f605e609e0b54784829b0ed2b917d050bfc1cbaf4ecc6453f224cfa7d0c5dcd97cbb273454cd210bee68e4af15c1a5abc9 - languageName: node - linkType: hard - "responselike@npm:1.0.2": version: 1.0.2 resolution: "responselike@npm:1.0.2" @@ -8313,14 +8245,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2": - version: 3.0.3 - resolution: "signal-exit@npm:3.0.3" - checksum: f0169d3f1263d06df32ca072b0bf33b34c6f8f0341a7a1621558a2444dfbe8f5fec76b35537fcc6f0bc4944bdb5336fe0bdcf41a5422c4e45a1dba3f45475e6c - languageName: node - linkType: hard - -"signal-exit@npm:^3.0.7": +"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 diff --git a/package.json b/package.json index 7b0fc5cddd..9439364045 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "build-storybook": "yarn ./storybook run build-storybook", "techdocs-cli": "node scripts/techdocs-cli.js", "techdocs-cli:dev": "cross-env TECHDOCS_CLI_DEV_MODE=true node scripts/techdocs-cli.js", - "prepare": "husky install" + "prepare": "husky install", + "postinstall": "husky install" }, "workspaces": { "packages": [ @@ -92,6 +93,12 @@ ], "*.md": [ "node ./scripts/check-docs-quality" + ], + "./yarn.lock": [ + "node ./scripts/verify-lockfile-duplicates --fix" + ], + "*/yarn.lock": [ + "node ./scripts/verify-lockfile-duplicates --fix" ] }, "packageManager": "yarn@3.2.3" diff --git a/scripts/verify-lockfile-duplicates.js b/scripts/verify-lockfile-duplicates.js new file mode 100644 index 0000000000..590de0f93d --- /dev/null +++ b/scripts/verify-lockfile-duplicates.js @@ -0,0 +1,135 @@ +#!/usr/bin/env node +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* eslint-disable import/no-extraneous-dependencies */ + +const { execFile: execFileCb } = require('child_process'); +const { resolve: resolvePath, dirname: dirnamePath } = require('path'); +const { promisify } = require('util'); + +const execFile = promisify(execFileCb); + +async function findLockFiles() { + const projectRoot = resolvePath(__dirname, '..'); + + let files = process.argv.slice(2).filter(arg => !arg.startsWith('--')); + + for (const argumentFile of files) { + if (!argumentFile.match(/(?:^|[\/\\])yarn.lock$/)) { + throw new Error(`Not a yarn.lock file path argument: "${argumentFile}"`); + } + } + + if (!files.length) { + // List all lock files that are in the root or in an immediate subdirectory + files = ['yarn.lock', 'cypress/yarn.lock', 'microsite/yarn.lock']; + } + + return files.map(file => ({ + fileRelativeToProjectRoot: file, + directoryRelativeToProjectRoot: dirnamePath(file), + directoryAbsolute: resolvePath(projectRoot, dirnamePath(file)), + })); +} + +async function main() { + const lockFiles = await findLockFiles(); + + let fix = false; + for (const arg of process.argv) { + if (arg.startsWith('--')) { + if (arg === '--fix') { + fix = true; + } else { + throw new Error(`Unknown argument ${arg}`); + } + } + } + + for (const lockFile of lockFiles) { + console.log('Checking lock file', lockFile.fileRelativeToProjectRoot); + + let stdout; + let stderr; + let failed; + + try { + const result = await execFile( + 'yarn', + ['dedupe', ...(fix ? [] : ['--check'])], + { + shell: true, + cwd: lockFile.directoryAbsolute, + }, + ); + stdout = result.stdout?.trim(); + stderr = result.stderr?.trim(); + failed = false; + } catch (error) { + stdout = error.stdout?.trim(); + stderr = error.stderr?.trim(); + failed = true; + } + + if (stdout) { + console.log(stdout); + } + + if (stderr) { + console.error(stderr); + } + + if (failed) { + if (!fix) { + const command = `yarn dedupe${ + lockFile.directoryRelativeToProjectRoot === '.' + ? '' + : ` --cwd ${lockFile.directoryRelativeToProjectRoot}` + }`; + const padding = ' '.repeat(Math.max(0, 85 - 6 - command.length)); + console.error(''); + console.error( + '*************************************************************************************', + ); + console.error( + '* You have duplicate versions of some packages in a yarn.lock file. *', + ); + console.error( + '* To solve this, run the following command from the project root and commit all *', + ); + console.log( + '* yarn.lock changes. *', + ); + console.log( + '* *', + ); + console.log(`* ${command}${padding} *`); + console.error( + '*************************************************************************************', + ); + console.error(''); + } + + process.exit(1); + } + } +} + +main().catch(error => { + console.error(error.stack); + process.exit(1); +}); diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 3317acf774..77fbf8ca66 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -1528,18 +1528,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.12.11, @babel/types@npm:^7.12.7, @babel/types@npm:^7.18.10, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.2.0, @babel/types@npm:^7.4.4": - version: 7.18.10 - resolution: "@babel/types@npm:7.18.10" - dependencies: - "@babel/helper-string-parser": ^7.18.10 - "@babel/helper-validator-identifier": ^7.18.6 - to-fast-properties: ^2.0.0 - checksum: 11632c9b106e54021937a6498138014ebc9ad6c327a07b2af3ba8700773945aba4055fd136431cbe3a500d0f363cbf9c68eb4d6d38229897c5de9d06e14c85e8 - languageName: node - linkType: hard - -"@babel/types@npm:^7.8.3": +"@babel/types@npm:^7.12.11, @babel/types@npm:^7.12.7, @babel/types@npm:^7.18.10, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.2.0, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.18.13 resolution: "@babel/types@npm:7.18.13" dependencies: @@ -1985,7 +1974,7 @@ __metadata: languageName: node linkType: hard -"@storybook/addons@npm:6.5.13, @storybook/addons@npm:^6.5.9": +"@storybook/addons@npm:6.5.13, @storybook/addons@npm:^6.0.0, @storybook/addons@npm:^6.5.9": version: 6.5.13 resolution: "@storybook/addons@npm:6.5.13" dependencies: @@ -2007,28 +1996,6 @@ __metadata: languageName: node linkType: hard -"@storybook/addons@npm:^6.0.0": - version: 6.5.12 - resolution: "@storybook/addons@npm:6.5.12" - dependencies: - "@storybook/api": 6.5.12 - "@storybook/channels": 6.5.12 - "@storybook/client-logger": 6.5.12 - "@storybook/core-events": 6.5.12 - "@storybook/csf": 0.0.2--canary.4566f4d.1 - "@storybook/router": 6.5.12 - "@storybook/theming": 6.5.12 - "@types/webpack-env": ^1.16.0 - core-js: ^3.8.2 - global: ^4.4.0 - regenerator-runtime: ^0.13.7 - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: c6242a80c7355544eb309603e77fdc3787d78ad983aba931f00812aeba75cc2cbd0e98c1ac0ce01441b58fabcdb671a9a799358f4bd6511cab289bc030d91f61 - languageName: node - linkType: hard - "@storybook/api@npm:6.5.10": version: 6.5.10 resolution: "@storybook/api@npm:6.5.10" @@ -2057,35 +2024,7 @@ __metadata: languageName: node linkType: hard -"@storybook/api@npm:6.5.12, @storybook/api@npm:^6.0.0": - version: 6.5.12 - resolution: "@storybook/api@npm:6.5.12" - dependencies: - "@storybook/channels": 6.5.12 - "@storybook/client-logger": 6.5.12 - "@storybook/core-events": 6.5.12 - "@storybook/csf": 0.0.2--canary.4566f4d.1 - "@storybook/router": 6.5.12 - "@storybook/semver": ^7.3.2 - "@storybook/theming": 6.5.12 - core-js: ^3.8.2 - fast-deep-equal: ^3.1.3 - global: ^4.4.0 - lodash: ^4.17.21 - memoizerific: ^1.11.3 - regenerator-runtime: ^0.13.7 - store2: ^2.12.0 - telejson: ^6.0.8 - ts-dedent: ^2.0.0 - util-deprecate: ^1.0.2 - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 3982cea5aaf851ccc19ff97ef82b7590d47839f0ebee28399e3b9381578edc130b4e46fe36431c62fa281949b2e0d5da2b1feafab0c2d24f70c4097d800b2679 - languageName: node - linkType: hard - -"@storybook/api@npm:6.5.13": +"@storybook/api@npm:6.5.13, @storybook/api@npm:^6.0.0": version: 6.5.13 resolution: "@storybook/api@npm:6.5.13" dependencies: @@ -2265,17 +2204,6 @@ __metadata: languageName: node linkType: hard -"@storybook/channels@npm:6.5.12": - version: 6.5.12 - resolution: "@storybook/channels@npm:6.5.12" - dependencies: - core-js: ^3.8.2 - ts-dedent: ^2.0.0 - util-deprecate: ^1.0.2 - checksum: e6b240a6c62a68a485bf8f4db536df0504cfcbe9685654e5a5712b833917b9a620e91994bf2283a420e413511c967e92ead522c98ad7e6c0e88b3830ddfd4e30 - languageName: node - linkType: hard - "@storybook/channels@npm:6.5.13": version: 6.5.13 resolution: "@storybook/channels@npm:6.5.13" @@ -2318,7 +2246,7 @@ __metadata: languageName: node linkType: hard -"@storybook/client-logger@npm:6.5.10, @storybook/client-logger@npm:^6.4.0": +"@storybook/client-logger@npm:6.5.10": version: 6.5.10 resolution: "@storybook/client-logger@npm:6.5.10" dependencies: @@ -2328,17 +2256,7 @@ __metadata: languageName: node linkType: hard -"@storybook/client-logger@npm:6.5.12": - version: 6.5.12 - resolution: "@storybook/client-logger@npm:6.5.12" - dependencies: - core-js: ^3.8.2 - global: ^4.4.0 - checksum: bd11bc25115f9b4a965e378d7dac28f9152038173ab5debb1e116a7aba69c814752d2c8aa4092dd1fc3f60cd99d4896c9e74d5e6f3c85768e7633adaf5bd2bf2 - languageName: node - linkType: hard - -"@storybook/client-logger@npm:6.5.13": +"@storybook/client-logger@npm:6.5.13, @storybook/client-logger@npm:^6.4.0": version: 6.5.13 resolution: "@storybook/client-logger@npm:6.5.13" dependencies: @@ -2348,7 +2266,7 @@ __metadata: languageName: node linkType: hard -"@storybook/components@npm:6.5.13": +"@storybook/components@npm:6.5.13, @storybook/components@npm:^6.0.0": version: 6.5.13 resolution: "@storybook/components@npm:6.5.13" dependencies: @@ -2367,25 +2285,6 @@ __metadata: languageName: node linkType: hard -"@storybook/components@npm:^6.0.0": - version: 6.5.12 - resolution: "@storybook/components@npm:6.5.12" - dependencies: - "@storybook/client-logger": 6.5.12 - "@storybook/csf": 0.0.2--canary.4566f4d.1 - "@storybook/theming": 6.5.12 - core-js: ^3.8.2 - memoizerific: ^1.11.3 - qs: ^6.10.0 - regenerator-runtime: ^0.13.7 - util-deprecate: ^1.0.2 - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: fa469ae615d9146df7e23f01b85731d27e6400e2d94035db172deb1f61903d86c121d858558dd12307ecc6344d21b496db020731e73eff6ace3f82672b953a93 - languageName: node - linkType: hard - "@storybook/core-client@npm:6.5.13": version: 6.5.13 resolution: "@storybook/core-client@npm:6.5.13" @@ -2494,16 +2393,7 @@ __metadata: languageName: node linkType: hard -"@storybook/core-events@npm:6.5.12, @storybook/core-events@npm:^6.0.0": - version: 6.5.12 - resolution: "@storybook/core-events@npm:6.5.12" - dependencies: - core-js: ^3.8.2 - checksum: 82a4b9cb2a8599f3916db84b08b4cfbde8f56cb96a7afe641b3f144676fc7dc5a705e65f8844430b36ee6e6e14d6b2cb741622a8b39411276682219df5e04271 - languageName: node - linkType: hard - -"@storybook/core-events@npm:6.5.13": +"@storybook/core-events@npm:6.5.13, @storybook/core-events@npm:^6.0.0": version: 6.5.13 resolution: "@storybook/core-events@npm:6.5.13" dependencies: @@ -2913,22 +2803,6 @@ __metadata: languageName: node linkType: hard -"@storybook/router@npm:6.5.12": - version: 6.5.12 - resolution: "@storybook/router@npm:6.5.12" - dependencies: - "@storybook/client-logger": 6.5.12 - core-js: ^3.8.2 - memoizerific: ^1.11.3 - qs: ^6.10.0 - regenerator-runtime: ^0.13.7 - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 545f4b767021b88f82eac69b9356fa5fa3a5866285c3a34fa762abc5743e3280895858aa5c820195d95a04c6768299191d4dd788a7d9fd3f17ff1d8236c0ba75 - languageName: node - linkType: hard - "@storybook/router@npm:6.5.13": version: 6.5.13 resolution: "@storybook/router@npm:6.5.13" @@ -3052,22 +2926,7 @@ __metadata: languageName: node linkType: hard -"@storybook/theming@npm:6.5.12, @storybook/theming@npm:^6.0.0": - version: 6.5.12 - resolution: "@storybook/theming@npm:6.5.12" - dependencies: - "@storybook/client-logger": 6.5.12 - core-js: ^3.8.2 - memoizerific: ^1.11.3 - regenerator-runtime: ^0.13.7 - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: a982ebf88c7e1e21127febd17feebf26ac8d655f0c868bf110cbcaaef87eedb257300087618c525cb654808b590dc4b7b98dd6fec92fd76a040441d86c4b8289 - languageName: node - linkType: hard - -"@storybook/theming@npm:6.5.13": +"@storybook/theming@npm:6.5.13, @storybook/theming@npm:^6.0.0": version: 6.5.13 resolution: "@storybook/theming@npm:6.5.13" dependencies: @@ -8409,16 +8268,7 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^3.0.0, minipass@npm:^3.1.1": - version: 3.3.4 - resolution: "minipass@npm:3.3.4" - dependencies: - yallist: ^4.0.0 - checksum: 5d95a7738c54852ba78d484141e850c792e062666a2d0c681a5ac1021275beb7e1acb077e59f9523ff1defb80901aea4e30fac10ded9a20a25d819a42916ef1b - languageName: node - linkType: hard - -"minipass@npm:^3.1.6": +"minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": version: 3.3.5 resolution: "minipass@npm:3.3.5" dependencies: From cb716004efe145c6e0847e95e2c6652240674d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 16 Nov 2022 09:32:58 +0100 Subject: [PATCH 27/68] add some eslint rules for testing-library use in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/angry-dingos-lick.md | 9 ++ .eslintrc.js | 7 +- package.json | 1 + packages/app/cypress/integration/app.js | 4 + .../src/routing/FeatureFlagged.test.tsx | 9 +- .../FeatureCalloutCircular.test.tsx | 12 +-- .../src/components/Select/Select.test.tsx | 2 +- .../WarningPanel/WarningPanel.test.tsx | 3 +- .../BitriseBuildDetailsDialog.test.tsx | 2 +- .../CatalogGraphPage/MaxDepthFilter.test.tsx | 57 +++++++------ .../CatalogGraphPage/MaxDepthFilter.tsx | 33 +++++-- .../EntityContextMenu.test.tsx | 2 +- .../UnregisterEntity.test.tsx | 2 +- .../SyntheticsLocation.test.tsx | 3 +- .../src/components/Select/Select.test.tsx | 2 +- .../AuditList/AuditListForEntity.test.tsx | 4 +- .../Cards/LastLighthouseAuditCard.test.tsx | 6 +- .../GroupListPicker/GroupListPicker.test.tsx | 9 +- plugins/pagerduty/src/api/client.ts | 12 +-- .../actions/builtin/github/helpers.ts | 2 + .../ListTasksPage/ListTaskPage.test.tsx | 2 +- .../ListTasksPage/OwnerListPicker.test.tsx | 7 +- .../TemplatePage/TemplatePage.test.tsx | 9 +- .../RegisterExistingButton.test.tsx | 6 +- .../Stepper/ReviewState.test.tsx | 5 +- plugins/splunk-on-call/src/api/client.ts | 14 +-- .../EntitySplunkOnCallCard.test.tsx | 3 +- plugins/techdocs-react/api-report.md | 2 +- plugins/techdocs-react/src/hooks.ts | 8 +- .../Grids/EntityListDocsGrid.test.tsx | 48 ++++------- yarn.lock | 85 +++++++++++++++++-- 31 files changed, 236 insertions(+), 134 deletions(-) create mode 100644 .changeset/angry-dingos-lick.md diff --git a/.changeset/angry-dingos-lick.md b/.changeset/angry-dingos-lick.md new file mode 100644 index 0000000000..933f19a839 --- /dev/null +++ b/.changeset/angry-dingos-lick.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-catalog-graph': patch +'@backstage/plugin-pagerduty': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-splunk-on-call': patch +'@backstage/plugin-techdocs-react': patch +--- + +Internal refactor to improve tests diff --git a/.eslintrc.js b/.eslintrc.js index 47a47daee6..6d07fab291 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -18,7 +18,7 @@ var path = require('path'); module.exports = { root: true, - plugins: ['notice'], + plugins: ['notice', 'testing-library'], rules: { 'notice/notice': [ 'error', @@ -45,5 +45,10 @@ module.exports = { "CallExpression[arguments.length=0] > MemberExpression[property.name='toUpperCase']", }, ], + 'testing-library/await-async-query': 'error', + 'testing-library/await-async-utils': 'error', + 'testing-library/no-await-sync-query': 'error', + 'testing-library/prefer-wait-for': 'error', + 'testing-library/no-dom-import': 'error', }, }; diff --git a/package.json b/package.json index 9439364045..2f20eeeee3 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "e2e-test": "workspace:*", "eslint": "^8.6.0", "eslint-plugin-notice": "^0.9.10", + "eslint-plugin-testing-library": "^5.9.1", "fs-extra": "10.1.0", "husky": "^8.0.0", "lint-staged": "^13.0.0", diff --git a/packages/app/cypress/integration/app.js b/packages/app/cypress/integration/app.js index 7b874907be..6371bcc4f8 100644 --- a/packages/app/cypress/integration/app.js +++ b/packages/app/cypress/integration/app.js @@ -25,14 +25,17 @@ describe('App', () => { it('should display support info when clicking the button', () => { cy.visit('/'); + // eslint-disable-next-line testing-library/await-async-query cy.findByTestId('support-button').click({ force: true }); cy.contains('#backstage'); }); it('should display error message when triggering it', () => { cy.visit('/'); + // eslint-disable-next-line testing-library/await-async-query cy.findByTestId('error-button').click({ force: true }); cy.contains('Error: Oh no!'); + // eslint-disable-next-line testing-library/await-async-query cy.findByTestId('error-button-close').click({ force: true }); }); @@ -55,6 +58,7 @@ describe('App', () => { cy.contains('Token') .get('input[name=github-auth-tf]') .type('password', { force: true }); + // eslint-disable-next-line testing-library/await-async-query cy.findByTestId('github-auth-button').click({ force: true }); cy.contains(`Welcome, ${name}!`); cy.contains('Logout').click({ force: true }); diff --git a/packages/core-app-api/src/routing/FeatureFlagged.test.tsx b/packages/core-app-api/src/routing/FeatureFlagged.test.tsx index d1b45c39d1..410540f77e 100644 --- a/packages/core-app-api/src/routing/FeatureFlagged.test.tsx +++ b/packages/core-app-api/src/routing/FeatureFlagged.test.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; import { FeatureFlagged } from './FeatureFlagged'; import { render } from '@testing-library/react'; @@ -44,7 +45,7 @@ describe('FeatureFlagged', () => { , ); - expect(await queryByText('BACKSTAGE!')).toBeInTheDocument(); + expect(queryByText('BACKSTAGE!')).toBeInTheDocument(); }); it('should not render contents when the feature flag is disabled', async () => { jest @@ -61,7 +62,7 @@ describe('FeatureFlagged', () => { , ); - expect(await queryByText('BACKSTAGE!')).not.toBeInTheDocument(); + expect(queryByText('BACKSTAGE!')).not.toBeInTheDocument(); }); }); describe('without', () => { @@ -80,7 +81,7 @@ describe('FeatureFlagged', () => { , ); - expect(await queryByText('BACKSTAGE!')).not.toBeInTheDocument(); + expect(queryByText('BACKSTAGE!')).not.toBeInTheDocument(); }); it('should render contents when the feature flag is disabled', async () => { jest @@ -97,7 +98,7 @@ describe('FeatureFlagged', () => { , ); - expect(await queryByText('BACKSTAGE!')).toBeInTheDocument(); + expect(queryByText('BACKSTAGE!')).toBeInTheDocument(); }); }); }); diff --git a/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.test.tsx b/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.test.tsx index 35ea30ffa6..f72e33ac4b 100644 --- a/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.test.tsx +++ b/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.test.tsx @@ -70,8 +70,8 @@ describe('', () => { description="description" />, ); - const dot = await getByTestId('dot'); - const text = await getByTestId('text'); + const dot = getByTestId('dot'); + const text = getByTestId('text'); expect(dot).toBeInTheDocument(); expect(text).toBeInTheDocument(); @@ -96,8 +96,8 @@ describe('', () => { description="description" />, ); - const dot = await getByTestId('dot'); - const text = await getByTestId('text'); + const dot = getByTestId('dot'); + const text = getByTestId('text'); act(() => { Element.prototype.getBoundingClientRect = jest.fn( @@ -128,8 +128,8 @@ describe('', () => { description="description" />, ); - const dot = await getByTestId('dot'); - const text = await getByTestId('text'); + const dot = getByTestId('dot'); + const text = getByTestId('text'); act(() => { Element.prototype.getBoundingClientRect = jest.fn( diff --git a/packages/core-components/src/components/Select/Select.test.tsx b/packages/core-components/src/components/Select/Select.test.tsx index 41e787a5b4..df29983222 100644 --- a/packages/core-components/src/components/Select/Select.test.tsx +++ b/packages/core-components/src/components/Select/Select.test.tsx @@ -41,7 +41,7 @@ describe('); expect(getByText('Default')).toBeInTheDocument(); - const input = await getByTestId('select'); + const input = getByTestId('select'); expect(input.textContent).toBe('All results'); // Simulate click on input diff --git a/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx b/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx index 25cf642c28..5d00784fd1 100644 --- a/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx +++ b/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx @@ -18,7 +18,6 @@ import React from 'react'; import { fireEvent, screen } from '@testing-library/react'; import { renderInTestApp } from '@backstage/test-utils'; import Typography from '@material-ui/core/Typography'; - import { WarningPanel, WarningProps } from './WarningPanel'; const propsTitle: WarningProps = { title: 'Mock title' }; @@ -41,7 +40,7 @@ describe('', () => { it('renders title', async () => { await renderInTestApp(); - const expandIcon = await screen.getByText('Warning: Mock title'); + const expandIcon = screen.getByText('Warning: Mock title'); fireEvent.click(expandIcon); expect(screen.getByText('Warning: Mock title')).toBeInTheDocument(); expect(screen.getByText('Some more info')).toBeInTheDocument(); diff --git a/plugins/bitrise/src/components/BitriseBuildDetailsDialog/BitriseBuildDetailsDialog.test.tsx b/plugins/bitrise/src/components/BitriseBuildDetailsDialog/BitriseBuildDetailsDialog.test.tsx index 2dcffc3bb3..7992e08f37 100644 --- a/plugins/bitrise/src/components/BitriseBuildDetailsDialog/BitriseBuildDetailsDialog.test.tsx +++ b/plugins/bitrise/src/components/BitriseBuildDetailsDialog/BitriseBuildDetailsDialog.test.tsx @@ -46,7 +46,7 @@ describe('BitriseArtifactsComponent', () => { const rendered = renderComponent(); const btn = await rendered.findByTestId('btn'); - expect(await rendered.queryByText('VISIBLE')).not.toBeInTheDocument(); + expect(rendered.queryByText('VISIBLE')).not.toBeInTheDocument(); btn.click(); diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.test.tsx index 3f3a7f82a6..89f3169981 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.test.tsx @@ -13,64 +13,65 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { render, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; + +import { render, screen } from '@testing-library/react'; +import user from '@testing-library/user-event'; import React from 'react'; import { MaxDepthFilter } from './MaxDepthFilter'; describe('', () => { test('should display current value', () => { - const { getByLabelText } = render( - {}} />, - ); + render( {}} />); - expect(getByLabelText('maxp')).toBeInTheDocument(); - expect(getByLabelText('maxp')).toHaveValue(5); + expect(screen.getByLabelText('maxp')).toBeInTheDocument(); + expect(screen.getByLabelText('maxp')).toHaveValue(5); }); test('should display infinite if non finite', () => { - const { getByPlaceholderText, getByLabelText } = render( + render( {}} />, ); - expect(getByPlaceholderText(/Infinite/)).toBeInTheDocument(); - expect(getByLabelText('maxp')).toHaveValue(null); + expect(screen.getByPlaceholderText(/Infinite/)).toBeInTheDocument(); + expect(screen.getByLabelText('maxp')).toHaveValue(null); }); test('should clear max depth', async () => { const onChange = jest.fn(); - const { getByLabelText } = render( - , - ); + render(); - await userEvent.click(getByLabelText('clear max depth')); + expect(onChange).not.toHaveBeenCalled(); + await user.click(screen.getByLabelText('clear max depth')); expect(onChange).toHaveBeenCalledWith(Number.POSITIVE_INFINITY); }); test('should set max depth to undefined if below one', async () => { const onChange = jest.fn(); - const { getByLabelText } = render( - , - ); + render(); - await userEvent.clear(getByLabelText('maxp')); - await userEvent.type(getByLabelText('maxp'), '0'); + await user.clear(screen.getByLabelText('maxp')); + await user.type(screen.getByLabelText('maxp'), '0'); expect(onChange).toHaveBeenCalledWith(Number.POSITIVE_INFINITY); }); test('should select direction', async () => { - const onChange = jest.fn(); - const { getByLabelText } = render( - , + let value = 5; + render( + { + value = v; + }} + />, ); - expect(getByLabelText('maxp')).toHaveValue(5); + expect(screen.getByLabelText('maxp')).toHaveValue(5); + expect(value).toBe(5); - await userEvent.clear(getByLabelText('maxp')); - await userEvent.type(getByLabelText('maxp'), '10'); - waitFor(() => { - expect(onChange).toHaveBeenCalledWith(10); - }); + await user.clear(screen.getByLabelText('maxp')); + expect(value).toBe(Number.POSITIVE_INFINITY); + await user.type(screen.getByLabelText('maxp'), '10'); + expect(value).toBe(10); }); }); diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.tsx index b6b03d8831..e6fb2fbab4 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.tsx @@ -23,7 +23,7 @@ import { Typography, } from '@material-ui/core'; import ClearIcon from '@material-ui/icons/Clear'; -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; export type Props = { value: number; @@ -42,18 +42,37 @@ const useStyles = makeStyles( export const MaxDepthFilter = ({ value, onChange }: Props) => { const classes = useStyles(); + const onChangeRef = useRef(onChange); + const [currentValue, setCurrentValue] = useState(value); + // Keep a fresh reference to the latest callback + useEffect(() => { + onChangeRef.current = onChange; + }, [onChange]); + + // If the value changes externally, update ourselves + useEffect(() => { + setCurrentValue(value); + }, [value]); + + // When the entered text changes, update ourselves and communicate externally const handleChange = useCallback( (event: React.ChangeEvent) => { - const v = Number(event.target.value); - onChange(v <= 0 ? Number.POSITIVE_INFINITY : v); + const newValueNumeric = Number(event.target.value); + const newValue = + Number.isFinite(newValueNumeric) && newValueNumeric > 0 + ? newValueNumeric + : Number.POSITIVE_INFINITY; + setCurrentValue(newValue); + onChangeRef.current(newValue); }, - [onChange], + [], ); const reset = useCallback(() => { - onChange(Number.POSITIVE_INFINITY); - }, [onChange]); + setCurrentValue(Number.POSITIVE_INFINITY); + onChangeRef.current(Number.POSITIVE_INFINITY); + }, [onChangeRef]); return ( @@ -62,7 +81,7 @@ export const MaxDepthFilter = ({ value, onChange }: Props) => { diff --git a/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.test.tsx b/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.test.tsx index 04cfa1b947..f9d7ca216f 100644 --- a/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.test.tsx +++ b/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.test.tsx @@ -75,7 +75,7 @@ describe('ComponentContextMenu', () => { expect(button).toBeInTheDocument(); fireEvent.click(button); - const unregister = await screen.getByText('Unregister entity'); + const unregister = screen.getByText('Unregister entity'); expect(unregister).toBeInTheDocument(); const unregisterSpanItem = getByText(/Unregister entity/); diff --git a/plugins/catalog/src/components/EntityContextMenu/UnregisterEntity.test.tsx b/plugins/catalog/src/components/EntityContextMenu/UnregisterEntity.test.tsx index 95cb601ea0..f1cd8c2546 100644 --- a/plugins/catalog/src/components/EntityContextMenu/UnregisterEntity.test.tsx +++ b/plugins/catalog/src/components/EntityContextMenu/UnregisterEntity.test.tsx @@ -69,7 +69,7 @@ describe('ComponentContextMenu', () => { />, ); - const unregister = await screen.getByText('Unregister entity'); + const unregister = screen.getByText('Unregister entity'); expect(unregister).toBeInTheDocument(); const unregisterSpanItem = getByText(/Unregister entity/); diff --git a/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.test.tsx b/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.test.tsx index de04f73e36..6d772efc9b 100644 --- a/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.test.tsx +++ b/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.test.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; import { SyntheticsLocation } from './SyntheticsLocation'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; @@ -60,6 +61,6 @@ describe('SyntheticsLocation', () => { , ); expect(await rendered.findByText(/__location__/)).toBeInTheDocument(); - expect(await rendered.queryByText(/failed/)).not.toBeInTheDocument(); + expect(rendered.queryByText(/failed/)).not.toBeInTheDocument(); }); }); diff --git a/plugins/gocd/src/components/Select/Select.test.tsx b/plugins/gocd/src/components/Select/Select.test.tsx index fb467b946f..9d73194ed8 100644 --- a/plugins/gocd/src/components/Select/Select.test.tsx +++ b/plugins/gocd/src/components/Select/Select.test.tsx @@ -40,7 +40,7 @@ describe('Select', () => { expect(rendered.getAllByText(testLabel)).toHaveLength(2); }); - describe('when the user hasn`t clicked on it', () => { + describe("when the user hasn't clicked on it", () => { it('should only render the current select item', async () => { const rendered = await renderInTestApp( ', () => { it('renders nothing', async () => { const { queryByTestId } = subject(); - expect(await queryByTestId('AuditListTable')).toBeNull(); + expect(queryByTestId('AuditListTable')).toBeNull(); }); }); @@ -137,7 +137,7 @@ describe('', () => { it('renders nothing', async () => { const { queryByTestId } = subject(); - expect(await queryByTestId('AuditListTable')).toBeNull(); + expect(queryByTestId('AuditListTable')).toBeNull(); }); }); }); diff --git a/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.test.tsx b/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.test.tsx index 58a3033a1f..ab1e2683b6 100644 --- a/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.test.tsx +++ b/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.test.tsx @@ -151,10 +151,10 @@ describe('', () => { it('renders nothing', async () => { const { queryByTestId } = subject(); - expect(await queryByTestId('AuditListTable')).toBeNull(); + expect(queryByTestId('AuditListTable')).toBeNull(); }); }); - // + describe('where there is no data', () => { beforeEach(() => { (useWebsiteForEntity as jest.Mock).mockReturnValue({ @@ -166,7 +166,7 @@ describe('', () => { it('renders nothing', async () => { const { queryByTestId } = subject(); - expect(await queryByTestId('AuditListTable')).toBeNull(); + expect(queryByTestId('AuditListTable')).toBeNull(); }); }); }); diff --git a/plugins/org-react/src/components/GroupListPicker/GroupListPicker.test.tsx b/plugins/org-react/src/components/GroupListPicker/GroupListPicker.test.tsx index b8a499b8fe..21b85ffe5c 100644 --- a/plugins/org-react/src/components/GroupListPicker/GroupListPicker.test.tsx +++ b/plugins/org-react/src/components/GroupListPicker/GroupListPicker.test.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; -import { fireEvent, render, waitFor } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { ApiProvider } from '@backstage/core-app-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { CatalogApi } from '@backstage/catalog-client'; @@ -74,13 +75,13 @@ describe('', () => { , ); - fireEvent.click(getByTestId('group-list-picker-button')); + await userEvent.click(getByTestId('group-list-picker-button')); const input = getByTestId('group-list-picker-input').querySelector('input'); - fireEvent.change(input as HTMLElement, { target: { value: 'GR' } }); + await userEvent.type(input as HTMLElement, 'GR'); await waitFor(async () => { expect(getByText('Group A')).toBeInTheDocument(); - fireEvent.click(getByText('Group A')); + await userEvent.click(getByText('Group A')); expect(getByText('Group A')).toBeInTheDocument(); }); }); diff --git a/plugins/pagerduty/src/api/client.ts b/plugins/pagerduty/src/api/client.ts index 7266d5e9eb..7d99be219d 100644 --- a/plugins/pagerduty/src/api/client.ts +++ b/plugins/pagerduty/src/api/client.ts @@ -73,7 +73,7 @@ export class PagerDutyClient implements PagerDutyApi { url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/pagerduty/services?${commonGetServiceParams}&query=${integrationKey}`; - const { services } = await this.getByUrl(url); + const { services } = await this.findByUrl(url); const service = services[0]; if (!service) throw new NotFoundError(); @@ -84,7 +84,7 @@ export class PagerDutyClient implements PagerDutyApi { 'proxy', )}/pagerduty/services/${serviceId}?${commonGetServiceParams}`; - response = await this.getByUrl(url); + response = await this.findByUrl(url); } else { throw new NotFoundError(); } @@ -100,7 +100,7 @@ export class PagerDutyClient implements PagerDutyApi { 'proxy', )}/pagerduty/incidents?${params}`; - return await this.getByUrl(url); + return await this.findByUrl(url); } async getChangeEventsByServiceId( @@ -111,7 +111,7 @@ export class PagerDutyClient implements PagerDutyApi { 'proxy', )}/pagerduty/services/${serviceId}/change_events?${params}`; - return await this.getByUrl(url); + return await this.findByUrl(url); } async getOnCallByPolicyId( @@ -122,7 +122,7 @@ export class PagerDutyClient implements PagerDutyApi { 'proxy', )}/pagerduty/oncalls?${params}`; - return await this.getByUrl(url); + return await this.findByUrl(url); } triggerAlarm(request: PagerDutyTriggerAlarmRequest): Promise { @@ -158,7 +158,7 @@ export class PagerDutyClient implements PagerDutyApi { return this.request(`${url}/enqueue`, options); } - private async getByUrl(url: string): Promise { + private async findByUrl(url: string): Promise { const options = { method: 'GET', headers: { diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts index 2891ea51cf..fe29378e2e 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Config } from '@backstage/config'; import { assertError, InputError } from '@backstage/errors'; import { @@ -124,6 +125,7 @@ export async function createGithubRepoWithCollaboratorsAndTopics( topics: string[] | undefined, logger: Logger, ) { + // eslint-disable-next-line testing-library/no-await-sync-query const user = await client.rest.users.getByUsername({ username: owner, }); diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx index 837b87437f..57dde13a43 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx @@ -236,7 +236,7 @@ describe('', () => { ); await act(async () => { - const allButton = await getByText('All'); + const allButton = getByText('All'); fireEvent.click(allButton); }); diff --git a/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.test.tsx index 63f943d6dd..7c335a0ef7 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.test.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.test.tsx @@ -15,7 +15,6 @@ */ import { renderInTestApp } from '@backstage/test-utils'; - import React from 'react'; import { OwnerListPicker } from './OwnerListPicker'; import { fireEvent } from '@testing-library/react'; @@ -29,8 +28,8 @@ describe('', () => { const { getByText } = await renderInTestApp(); - expect(await getByText('Owned')).toBeDefined(); - expect(await getByText('All')).toBeDefined(); + expect(getByText('Owned')).toBeDefined(); + expect(getByText('All')).toBeDefined(); }); it('should call the function on select other item', async () => { @@ -41,7 +40,7 @@ describe('', () => { const { getByText } = await renderInTestApp(); - fireEvent.click(await getByText('All')); + fireEvent.click(getByText('All')); expect(props.onSelectOwner).toHaveBeenCalledWith('all'); }); }); diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx index a48644e72d..71f5ffad47 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { MockAnalyticsApi, renderInTestApp, @@ -340,9 +341,9 @@ describe('TemplatePage', () => { }, ); - expect(await queryByText('Name')).not.toBeInTheDocument(); - expect(await queryByText('Description')).toBeInTheDocument(); - expect(await queryByText('Owner')).toBeInTheDocument(); - expect(await queryByText('Send data')).toBeInTheDocument(); + expect(queryByText('Name')).not.toBeInTheDocument(); + expect(queryByText('Description')).toBeInTheDocument(); + expect(queryByText('Owner')).toBeInTheDocument(); + expect(queryByText('Send data')).toBeInTheDocument(); }); }); diff --git a/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.test.tsx b/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.test.tsx index e6022d16fb..a8e377bd3d 100644 --- a/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.test.tsx +++ b/plugins/scaffolder/src/next/TemplateListPage/RegisterExistingButton.test.tsx @@ -34,7 +34,7 @@ describe('RegisterExistingButton', () => { , ); - expect(await queryByText('Pick me')).not.toBeInTheDocument(); + expect(queryByText('Pick me')).not.toBeInTheDocument(); }); it('should not render if permissions are not allowed', async () => { @@ -43,7 +43,7 @@ describe('RegisterExistingButton', () => { , ); - expect(await queryByText('Pick me')).not.toBeInTheDocument(); + expect(queryByText('Pick me')).not.toBeInTheDocument(); }); it('should render the button with the text', async () => { @@ -52,6 +52,6 @@ describe('RegisterExistingButton', () => { , ); - expect(await queryByText('Pick me')).toBeInTheDocument(); + expect(queryByText('Pick me')).toBeInTheDocument(); }); }); diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/ReviewState.test.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/ReviewState.test.tsx index 2b2e706102..c03df8f73c 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/ReviewState.test.tsx +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/ReviewState.test.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; import { ReviewState } from './ReviewState'; import { render } from '@testing-library/react'; @@ -97,9 +98,7 @@ describe('ReviewState', () => { , ); - expect( - await queryByRole('row', { name: 'Name ******' }), - ).not.toBeInTheDocument(); + expect(queryByRole('row', { name: 'Name ******' })).not.toBeInTheDocument(); }); it('should allow for masking an option with a set text', () => { diff --git a/plugins/splunk-on-call/src/api/client.ts b/plugins/splunk-on-call/src/api/client.ts index aa655a4772..2c5e122ac8 100644 --- a/plugins/splunk-on-call/src/api/client.ts +++ b/plugins/splunk-on-call/src/api/client.ts @@ -64,7 +64,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { 'proxy', )}/splunk-on-call/v1/incidents`; - const { incidents } = await this.getByUrl(url); + const { incidents } = await this.findByUrl(url); return incidents; } @@ -73,7 +73,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/splunk-on-call/v1/oncall/current`; - const { teamsOnCall } = await this.getByUrl(url); + const { teamsOnCall } = await this.findByUrl(url); return teamsOnCall; } @@ -82,7 +82,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/splunk-on-call/v1/team`; - const teams = await this.getByUrl(url); + const teams = await this.findByUrl(url); return teams; } @@ -91,7 +91,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/splunk-on-call/v1/org/routing-keys`; - const { routingKeys } = await this.getByUrl(url); + const { routingKeys } = await this.findByUrl(url); return routingKeys; } @@ -100,7 +100,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/splunk-on-call/v2/user`; - const { users } = await this.getByUrl(url); + const { users } = await this.findByUrl(url); return users; } @@ -109,7 +109,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/splunk-on-call/v1/policies`; - const { policies } = await this.getByUrl(url); + const { policies } = await this.findByUrl(url); return policies; } @@ -146,7 +146,7 @@ export class SplunkOnCallClient implements SplunkOnCallApi { return this.request(url, options); } - private async getByUrl(url: string): Promise { + private async findByUrl(url: string): Promise { const options = { method: 'GET', headers: { diff --git a/plugins/splunk-on-call/src/components/EntitySplunkOnCallCard.test.tsx b/plugins/splunk-on-call/src/components/EntitySplunkOnCallCard.test.tsx index 051d9de030..704d2d9ed9 100644 --- a/plugins/splunk-on-call/src/components/EntitySplunkOnCallCard.test.tsx +++ b/plugins/splunk-on-call/src/components/EntitySplunkOnCallCard.test.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Entity } from '@backstage/catalog-model'; import { ApiProvider, ConfigReader } from '@backstage/core-app-api'; import { @@ -187,7 +188,7 @@ describe('SplunkOnCallCard', () => { { timeout: 2000 }, ); - const createIncidentButton = await getByText('Create Incident'); + const createIncidentButton = getByText('Create Incident'); await act(async () => { fireEvent.click(createIncidentButton); }); diff --git a/plugins/techdocs-react/api-report.md b/plugins/techdocs-react/api-report.md index d04b852d07..09da4e012c 100644 --- a/plugins/techdocs-react/api-report.md +++ b/plugins/techdocs-react/api-report.md @@ -173,7 +173,7 @@ export const useShadowRootElements: < ) => TReturnedElement[]; // @public -export const useShadowRootSelection: (wait?: number) => Selection | null; +export const useShadowRootSelection: (waitMillis?: number) => Selection | null; // @public export const useTechDocsAddons: () => { diff --git a/plugins/techdocs-react/src/hooks.ts b/plugins/techdocs-react/src/hooks.ts index 560c548156..e8cc7bddc8 100644 --- a/plugins/techdocs-react/src/hooks.ts +++ b/plugins/techdocs-react/src/hooks.ts @@ -57,10 +57,10 @@ const isValidSelection = (newSelection: Selection) => { }; /** - * Hook for retreiving a selection within the ShadowRoot. + * Hook for retrieving a selection within the ShadowRoot. * @public */ -export const useShadowRootSelection = (wait: number = 0) => { +export const useShadowRootSelection = (waitMillis: number = 0) => { const shadowRoot = useShadowRoot(); const [selection, setSelection] = useState(null); const handleSelectionChange = useMemo( @@ -78,8 +78,8 @@ export const useShadowRootSelection = (wait: number = 0) => { } else { setSelection(null); } - }, wait), - [shadowRoot, setSelection, wait], + }, waitMillis), + [shadowRoot, setSelection, waitMillis], ); useEffect(() => { diff --git a/plugins/techdocs/src/home/components/Grids/EntityListDocsGrid.test.tsx b/plugins/techdocs/src/home/components/Grids/EntityListDocsGrid.test.tsx index effd8fe0c5..2837ab340a 100644 --- a/plugins/techdocs/src/home/components/Grids/EntityListDocsGrid.test.tsx +++ b/plugins/techdocs/src/home/components/Grids/EntityListDocsGrid.test.tsx @@ -103,10 +103,10 @@ describe('Entity List Docs Grid', () => { }, ); - expect(await screen.queryByText('All Documentation')).toBeInTheDocument(); - expect(await screen.queryByText('Documentation #1')).toBeInTheDocument(); - expect(await screen.queryByText('Documentation #2')).toBeInTheDocument(); - expect(await screen.queryByTestId('doc-not-found')).not.toBeInTheDocument(); + expect(screen.queryByText('All Documentation')).toBeInTheDocument(); + expect(screen.queryByText('Documentation #1')).toBeInTheDocument(); + expect(screen.queryByText('Documentation #2')).toBeInTheDocument(); + expect(screen.queryByTestId('doc-not-found')).not.toBeInTheDocument(); }); it('should render only filtered entities with filtering', async () => { @@ -131,14 +131,10 @@ describe('Entity List Docs Grid', () => { }, ); - expect( - await screen.queryByText('Curated Documentation'), - ).toBeInTheDocument(); - expect(await screen.queryByText('Documentation #1')).toBeInTheDocument(); - expect( - await screen.queryByText('Documentation #2'), - ).not.toBeInTheDocument(); - expect(await screen.queryByTestId('doc-not-found')).not.toBeInTheDocument(); + expect(screen.queryByText('Curated Documentation')).toBeInTheDocument(); + expect(screen.queryByText('Documentation #1')).toBeInTheDocument(); + expect(screen.queryByText('Documentation #2')).not.toBeInTheDocument(); + expect(screen.queryByTestId('doc-not-found')).not.toBeInTheDocument(); }); it('should render nothing with filtering yielding no result', async () => { @@ -163,16 +159,10 @@ describe('Entity List Docs Grid', () => { }, ); - expect( - await screen.queryByText('Curated Documentation'), - ).not.toBeInTheDocument(); - expect( - await screen.queryByText('Documentation #1'), - ).not.toBeInTheDocument(); - expect( - await screen.queryByText('Documentation #2'), - ).not.toBeInTheDocument(); - expect(await screen.queryByTestId('doc-not-found')).not.toBeInTheDocument(); + expect(screen.queryByText('Curated Documentation')).not.toBeInTheDocument(); + expect(screen.queryByText('Documentation #1')).not.toBeInTheDocument(); + expect(screen.queryByText('Documentation #2')).not.toBeInTheDocument(); + expect(screen.queryByTestId('doc-not-found')).not.toBeInTheDocument(); }); it('should render an error without any documentation and without filtering', async () => { @@ -189,15 +179,9 @@ describe('Entity List Docs Grid', () => { }, ); - expect( - await screen.queryByText('All Documentation'), - ).not.toBeInTheDocument(); - expect( - await screen.queryByText('Documentation #1'), - ).not.toBeInTheDocument(); - expect( - await screen.queryByText('Documentation #2'), - ).not.toBeInTheDocument(); - expect(await screen.queryByTestId('doc-not-found')).toBeInTheDocument(); + expect(screen.queryByText('All Documentation')).not.toBeInTheDocument(); + expect(screen.queryByText('Documentation #1')).not.toBeInTheDocument(); + expect(screen.queryByText('Documentation #2')).not.toBeInTheDocument(); + expect(screen.queryByTestId('doc-not-found')).toBeInTheDocument(); }); }); diff --git a/yarn.lock b/yarn.lock index b1531e5db9..1a5617c798 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13990,10 +13990,10 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.3.8": - version: 7.3.12 - resolution: "@types/semver@npm:7.3.12" - checksum: 35536b2fc5602904f21cae681f6c9498e177dab3f54ae37c92f9a1b7e43c35f18bcd81e1c98c1cf0d33ee046bb06c771e9928c1c00a401d56a03f56549252a15 +"@types/semver@npm:^7.3.12, @types/semver@npm:^7.3.8": + version: 7.3.13 + resolution: "@types/semver@npm:7.3.13" + checksum: 00c0724d54757c2f4bc60b5032fe91cda6410e48689633d5f35ece8a0a66445e3e57fa1d6e07eb780f792e82ac542948ec4d0b76eb3484297b79bd18b8cf1cb0 languageName: node linkType: hard @@ -14446,6 +14446,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:5.43.0": + version: 5.43.0 + resolution: "@typescript-eslint/scope-manager@npm:5.43.0" + dependencies: + "@typescript-eslint/types": 5.43.0 + "@typescript-eslint/visitor-keys": 5.43.0 + checksum: e594c7a32c3fa29e46dd0b0bc62f97f154bd864682ae7da87a14b6f4336f4cb02f6ed0602bbdb15783e4230ecdf8a0ccc6f7c5820850e8f11240c9e4fb0e388d + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:5.9.0": version: 5.9.0 resolution: "@typescript-eslint/scope-manager@npm:5.9.0" @@ -14480,6 +14490,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:5.43.0": + version: 5.43.0 + resolution: "@typescript-eslint/types@npm:5.43.0" + checksum: fc5e5431c305feee4a3faae84f34df482e08d74b910a6f9376b01326c682ceefeeb0e270d03d7778787bc94ef05b3b85ee6d3c9d732290fbdb4a67ae1b110226 + languageName: node + linkType: hard + "@typescript-eslint/types@npm:5.9.0": version: 5.9.0 resolution: "@typescript-eslint/types@npm:5.9.0" @@ -14505,6 +14522,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:5.43.0": + version: 5.43.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.43.0" + dependencies: + "@typescript-eslint/types": 5.43.0 + "@typescript-eslint/visitor-keys": 5.43.0 + debug: ^4.3.4 + globby: ^11.1.0 + is-glob: ^4.0.3 + semver: ^7.3.7 + tsutils: ^3.21.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 3479f9413d73369ab3d574580c90a72f74d2ae1ec4afe485eebfad054c3d15c89f23a137bb9d6197dfdae33e444a76a99f6832688787feabbb064e09d39a3f55 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.9.0": version: 5.9.0 resolution: "@typescript-eslint/typescript-estree@npm:5.9.0" @@ -14523,7 +14558,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.36.2, @typescript-eslint/utils@npm:^5.10.0": +"@typescript-eslint/utils@npm:5.36.2": version: 5.36.2 resolution: "@typescript-eslint/utils@npm:5.36.2" dependencies: @@ -14539,6 +14574,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:^5.10.0, @typescript-eslint/utils@npm:^5.13.0": + version: 5.43.0 + resolution: "@typescript-eslint/utils@npm:5.43.0" + dependencies: + "@types/json-schema": ^7.0.9 + "@types/semver": ^7.3.12 + "@typescript-eslint/scope-manager": 5.43.0 + "@typescript-eslint/types": 5.43.0 + "@typescript-eslint/typescript-estree": 5.43.0 + eslint-scope: ^5.1.1 + eslint-utils: ^3.0.0 + semver: ^7.3.7 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 4c6b383b51506b57230f2624f883ae21e5d8411d138587c04fe3145f915bf8c289cc2a9f7b0b3faba98345ba230504e5014922bcc578aa0badd594d9eaa8f9ef + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:5.36.2": version: 5.36.2 resolution: "@typescript-eslint/visitor-keys@npm:5.36.2" @@ -14549,6 +14602,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:5.43.0": + version: 5.43.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.43.0" + dependencies: + "@typescript-eslint/types": 5.43.0 + eslint-visitor-keys: ^3.3.0 + checksum: 4820679e50096dcdaadc7c95d32e5dca3ba8510acf1a865e283822bae3940a2faec02ad8abe793f8a25f75b600f1e7215e1fd3b3ba73779eff737fa90d092550 + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:5.9.0": version: 5.9.0 resolution: "@typescript-eslint/visitor-keys@npm:5.9.0" @@ -20502,6 +20565,17 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-testing-library@npm:^5.9.1": + version: 5.9.1 + resolution: "eslint-plugin-testing-library@npm:5.9.1" + dependencies: + "@typescript-eslint/utils": ^5.13.0 + peerDependencies: + eslint: ^7.5.0 || ^8.0.0 + checksum: d09f9486945807e9587d52b6979117bc41b750df741567381a06219671096afb318696a0e0db63e253e150fead40e77ef9653ee00f1dda83fc8920e3b3c47107 + languageName: node + linkType: hard + "eslint-scope@npm:5.1.1, eslint-scope@npm:^5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" @@ -32907,6 +32981,7 @@ __metadata: e2e-test: "workspace:*" eslint: ^8.6.0 eslint-plugin-notice: ^0.9.10 + eslint-plugin-testing-library: ^5.9.1 fs-extra: 10.1.0 husky: ^8.0.0 lint-staged: ^13.0.0 From b978bbb463dc7f1d9c03b893e633394eed4f9c7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 17 Nov 2022 16:46:03 +0100 Subject: [PATCH 28/68] require testing-library/prefer-screen-queries in catalog libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- plugins/catalog-graph/.eslintrc.js | 6 +- .../CatalogGraphCard.test.tsx | 29 +- .../CatalogGraphPage.test.tsx | 63 +- .../CatalogGraphPage/CurveFilter.test.tsx | 21 +- .../CatalogGraphPage/DirectionFilter.test.tsx | 17 +- .../SelectedKindsFilter.test.tsx | 35 +- .../SelectedRelationsFilter.test.tsx | 29 +- .../CatalogGraphPage/SwitchFilter.test.tsx | 21 +- .../EntityRelationsGraph/CustomLabel.test.tsx | 13 +- .../EntityRelationsGraph/CustomNode.test.tsx | 20 +- .../EntityKindIcon.test.tsx | 1 + .../EntityRelationsGraph.test.tsx | 237 ++++---- plugins/catalog-import/.eslintrc.js | 6 +- .../DefaultImportPage.test.tsx | 5 +- .../ImportInfoCard/ImportInfoCard.test.tsx | 15 +- .../components/ImportPage/ImportPage.test.tsx | 9 +- .../StepInitAnalyzeUrl.test.tsx | 145 ++--- .../PreparePullRequestForm.test.tsx | 26 +- .../PreviewPullRequestComponent.test.tsx | 20 +- .../StepPrepareCreatePullRequest.test.tsx | 8 +- .../StepPrepareSelectLocations.test.tsx | 58 +- plugins/catalog-react/.eslintrc.js | 6 +- .../EntityKindPicker.test.tsx | 20 +- .../EntityLifecyclePicker.test.tsx | 32 +- .../EntityOwnerPicker.test.tsx | 32 +- .../EntityProcessingStatusPicker.test.tsx | 36 +- .../EntityRefLink/EntityRefLink.test.tsx | 65 +- .../EntityRefLink/EntityRefLinks.test.tsx | 31 +- .../FetchedEntityRefLinks.test.tsx | 21 +- .../EntitySearchBar/EntitySearchBar.test.tsx | 6 +- .../EntityTable/EntityTable.test.tsx | 12 +- .../components/EntityTable/presets.test.tsx | 26 +- .../EntityTagPicker/EntityTagPicker.test.tsx | 40 +- .../EntityTypePicker.test.tsx | 25 +- .../UserListPicker/UserListPicker.test.tsx | 25 +- plugins/catalog/.eslintrc.js | 6 +- .../components/AboutCard/AboutCard.test.tsx | 51 +- .../AboutCard/AboutContent.test.tsx | 561 +++++++++--------- .../CatalogKindHeader.test.tsx | 40 +- .../CatalogPage/CatalogPage.test.tsx | 9 +- .../CatalogPage/DefaultCatalogPage.test.tsx | 78 ++- .../CatalogTable/CatalogTable.test.tsx | 46 +- .../DependencyOfComponentsCard.test.tsx | 14 +- .../DependsOnComponentsCard.test.tsx | 14 +- .../DependsOnResourcesCard.test.tsx | 14 +- .../EntityContextMenu.test.tsx | 4 +- .../UnregisterEntity.test.tsx | 4 +- .../EntityLayout/EntityLayout.test.tsx | 58 +- .../EntityLinksCard/EntityLinksCard.test.tsx | 13 +- .../EntityLinksCard/IconLink.test.tsx | 6 +- .../EntityNotFound/EntityNotFound.test.tsx | 11 +- .../EntityOrphanWarning.test.tsx | 6 +- .../EntityProcessingErrorsPanel.test.tsx | 23 +- .../EntitySwitch/EntitySwitch.test.tsx | 54 +- .../HasComponentsCard.test.tsx | 14 +- .../HasResourcesCard.test.tsx | 14 +- .../HasSubcomponentsCard.test.tsx | 14 +- .../HasSystemsCard/HasSystemsCard.test.tsx | 16 +- .../SystemDiagramCard.test.tsx | 25 +- 59 files changed, 1124 insertions(+), 1132 deletions(-) diff --git a/plugins/catalog-graph/.eslintrc.js b/plugins/catalog-graph/.eslintrc.js index e2a53a6ad2..45bb6db521 100644 --- a/plugins/catalog-graph/.eslintrc.js +++ b/plugins/catalog-graph/.eslintrc.js @@ -1 +1,5 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { + rules: { + 'testing-library/prefer-screen-queries': 'error', + }, +}); diff --git a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx index c5940be395..27b67102e4 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx @@ -28,6 +28,7 @@ import { TestApiProvider, TestApiRegistry, } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { catalogGraphRouteRef } from '../../routes'; @@ -79,15 +80,15 @@ describe('', () => { relations: [], })); - const { findByText, findAllByTestId } = await renderInTestApp(wrapper, { + await renderInTestApp(wrapper, { mountedRoutes: { '/entity/{kind}/{namespace}/{name}': entityRouteRef, '/catalog-graph': catalogGraphRouteRef, }, }); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(1); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(1); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(1); }); @@ -97,7 +98,7 @@ describe('', () => { relations: [], })); - const { findByText } = await renderInTestApp( + await renderInTestApp( @@ -111,7 +112,7 @@ describe('', () => { }, ); - expect(await findByText('Custom Title')).toBeInTheDocument(); + expect(await screen.findByText('Custom Title')).toBeInTheDocument(); }); test('renders link to standalone viewer', async () => { @@ -120,15 +121,15 @@ describe('', () => { relations: [], })); - const { findByText, getByText } = await renderInTestApp(wrapper, { + await renderInTestApp(wrapper, { mountedRoutes: { '/entity/{kind}/{namespace}/{name}': entityRouteRef, '/catalog-graph': catalogGraphRouteRef, }, }); - expect(await findByText('b:d/c')).toBeInTheDocument(); - const button = getByText('View graph'); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + const button = screen.getByText('View graph'); expect(button).toBeInTheDocument(); expect(button.closest('a')).toHaveAttribute( 'href', @@ -137,7 +138,7 @@ describe('', () => { }); test('renders link to standalone viewer with custom config', async () => { - const { findByText, getByText } = await renderInTestApp( + await renderInTestApp( @@ -151,8 +152,8 @@ describe('', () => { }, ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - const button = getByText('View graph'); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + const button = screen.getByText('View graph'); expect(button).toBeInTheDocument(); expect(button.closest('a')).toHaveAttribute( 'href', @@ -167,7 +168,7 @@ describe('', () => { })); const analyticsSpy = new MockAnalyticsApi(); - const { findByText } = await renderInTestApp( + await renderInTestApp( {wrapper} , @@ -179,8 +180,8 @@ describe('', () => { }, ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - await userEvent.click(await findByText('b:d/c')); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + await userEvent.click(await screen.findByText('b:d/c')); expect(analyticsSpy.getEvents()[0]).toMatchObject({ action: 'click', diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx index 9a5d1f833e..f7e1a9fbe7 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx @@ -22,6 +22,7 @@ import { renderInTestApp, TestApiProvider, } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { CatalogGraphPage } from './CatalogGraphPage'; @@ -108,19 +109,16 @@ describe('', () => { n === 'b:d/e' ? entityE : entityC, ); - const { getByText, findByText, findAllByTestId } = await renderInTestApp( - wrapper, - { - mountedRoutes: { - '/entity/{kind}/{namespace}/{name}': entityRouteRef, - }, + await renderInTestApp(wrapper, { + mountedRoutes: { + '/entity/{kind}/{namespace}/{name}': entityRouteRef, }, - ); + }); - expect(getByText('Catalog Graph')).toBeInTheDocument(); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('b:d/e')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(2); + expect(screen.getByText('Catalog Graph')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('b:d/e')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(2); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(2); }); @@ -129,17 +127,17 @@ describe('', () => { n === 'b:d/e' ? entityE : entityC, ); - const { getByText, queryByText } = await renderInTestApp(wrapper, { + await renderInTestApp(wrapper, { mountedRoutes: { '/entity/{kind}/{namespace}/{name}': entityRouteRef, }, }); - expect(queryByText('Max Depth')).toBeNull(); + expect(screen.queryByText('Max Depth')).toBeNull(); - await userEvent.click(getByText('Filters')); + await userEvent.click(screen.getByText('Filters')); - expect(getByText('Max Depth')).toBeInTheDocument(); + expect(screen.getByText('Max Depth')).toBeInTheDocument(); }); test('should select other entity', async () => { @@ -147,20 +145,17 @@ describe('', () => { n === 'b:d/e' ? entityE : entityC, ); - const { getByText, findByText, findAllByTestId } = await renderInTestApp( - wrapper, - { - mountedRoutes: { - '/entity/{kind}/{namespace}/{name}': entityRouteRef, - }, + await renderInTestApp(wrapper, { + mountedRoutes: { + '/entity/{kind}/{namespace}/{name}': entityRouteRef, }, - ); + }); - expect(await findAllByTestId('node')).toHaveLength(2); + expect(await screen.findAllByTestId('node')).toHaveLength(2); - await userEvent.click(getByText('b:d/e')); + await userEvent.click(screen.getByText('b:d/e')); - expect(await findByText('hasPart')).toBeInTheDocument(); + expect(await screen.findByText('hasPart')).toBeInTheDocument(); }); test('should navigate to entity', async () => { @@ -168,17 +163,17 @@ describe('', () => { n === 'b:d/e' ? entityE : entityC, ); - const { getByText, findAllByTestId } = await renderInTestApp(wrapper, { + await renderInTestApp(wrapper, { mountedRoutes: { '/entity/{kind}/{namespace}/{name}': entityRouteRef, }, }); - expect(await findAllByTestId('node')).toHaveLength(2); + expect(await screen.findAllByTestId('node')).toHaveLength(2); const user = userEvent.setup(); await user.keyboard('{Shift>}'); - await user.click(getByText('b:d/e')); + await user.click(screen.getByText('b:d/e')); expect(navigate).toHaveBeenCalledWith('/entity/{kind}/{namespace}/{name}'); }); @@ -188,7 +183,7 @@ describe('', () => { ); const analyticsSpy = new MockAnalyticsApi(); - const { getByText, findAllByTestId } = await renderInTestApp( + await renderInTestApp( {wrapper} , @@ -199,12 +194,12 @@ describe('', () => { }, ); - expect(await findAllByTestId('node')).toHaveLength(2); + expect(await screen.findAllByTestId('node')).toHaveLength(2); // We wait a bit here to reliably reproduce an issue where that requires the `baseVal` and `view` mocks await new Promise(r => setTimeout(r, 100)); - await userEvent.click(getByText('b:d/e')); + await userEvent.click(screen.getByText('b:d/e')); expect(analyticsSpy.getEvents()[0]).toMatchObject({ action: 'click', @@ -218,7 +213,7 @@ describe('', () => { ); const analyticsSpy = new MockAnalyticsApi(); - const { getByText, findAllByTestId } = await renderInTestApp( + await renderInTestApp( {wrapper} , @@ -229,11 +224,11 @@ describe('', () => { }, ); - expect(await findAllByTestId('node')).toHaveLength(2); + expect(await screen.findAllByTestId('node')).toHaveLength(2); const user = userEvent.setup(); await user.keyboard('{Shift>}'); - await user.click(getByText('b:d/e')); + await user.click(screen.getByText('b:d/e')); expect(analyticsSpy.getEvents()[0]).toMatchObject({ action: 'click', diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx index 0be0fca66b..85d9d97c7f 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { render, waitFor } from '@testing-library/react'; + +import { render, waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { CurveFilter } from './CurveFilter'; @@ -21,26 +22,22 @@ import { CurveFilter } from './CurveFilter'; describe('', () => { test('should display current curve label', () => { const onChange = jest.fn(); - const { getByText } = render( - , - ); + render(); - expect(getByText('Monotone X')).toBeInTheDocument(); + expect(screen.getByText('Monotone X')).toBeInTheDocument(); }); test('should select an alternative curve factory', async () => { const onChange = jest.fn(); - const { getByText, getByTestId } = render( - , - ); + render(); - expect(getByText('Step Before')).toBeInTheDocument(); + expect(screen.getByText('Step Before')).toBeInTheDocument(); - await userEvent.click(getByTestId('select')); - await userEvent.click(getByText('Monotone X')); + await userEvent.click(screen.getByTestId('select')); + await userEvent.click(screen.getByText('Monotone X')); await waitFor(() => { - expect(getByText('Monotone X')).toBeInTheDocument(); + expect(screen.getByText('Monotone X')).toBeInTheDocument(); expect(onChange).toHaveBeenCalledWith('curveMonotoneX'); }); }); diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/DirectionFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/DirectionFilter.test.tsx index 05c071f1f9..cb660b5a25 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/DirectionFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/DirectionFilter.test.tsx @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { render, waitFor } from '@testing-library/react'; + +import { render, waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { Direction } from '../EntityRelationsGraph'; @@ -21,26 +22,26 @@ import { DirectionFilter } from './DirectionFilter'; describe('', () => { test('should display current value', () => { - const { getByText } = render( + render( {}} />, ); - expect(getByText('Left to right')).toBeInTheDocument(); + expect(screen.getByText('Left to right')).toBeInTheDocument(); }); test('should select direction', async () => { const onChange = jest.fn(); - const { getByText, getByTestId } = render( + render( , ); - expect(getByText('Right to left')).toBeInTheDocument(); + expect(screen.getByText('Right to left')).toBeInTheDocument(); - await userEvent.click(getByTestId('select')); - await userEvent.click(getByText('Top to bottom')); + await userEvent.click(screen.getByTestId('select')); + await userEvent.click(screen.getByText('Top to bottom')); await waitFor(() => { - expect(getByText('Top to bottom')).toBeInTheDocument(); + expect(screen.getByText('Top to bottom')).toBeInTheDocument(); expect(onChange).toHaveBeenCalledWith(Direction.TOP_BOTTOM); }); }); diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.test.tsx index 580c118b27..511592af0c 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.test.tsx @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { GetEntityFacetsResponse } from '@backstage/catalog-client'; import { ApiProvider } from '@backstage/core-app-api'; import { AlertApi, alertApiRef } from '@backstage/core-plugin-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { SelectedKindsFilter } from './SelectedKindsFilter'; @@ -42,37 +43,37 @@ const apis = TestApiRegistry.from( describe('', () => { it('should not explode while loading', async () => { - const rendered = await renderWithEffects( + const { baseElement } = await renderWithEffects( {}} /> , ); - expect(rendered.baseElement).toBeInTheDocument(); + expect(baseElement).toBeInTheDocument(); }); it('should render current value', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( {}} /> , ); - expect(rendered.getByText('API')).toBeInTheDocument(); - expect(rendered.getByText('Component')).toBeInTheDocument(); + expect(screen.getByText('API')).toBeInTheDocument(); + expect(screen.getByText('Component')).toBeInTheDocument(); }); it('should select value', async () => { const onChange = jest.fn(); - const { getByLabelText, getByText } = await renderWithEffects( + await renderWithEffects( , ); - await userEvent.click(getByLabelText('Open')); - await waitFor(() => expect(getByText('System')).toBeInTheDocument()); + await userEvent.click(screen.getByLabelText('Open')); + await waitFor(() => expect(screen.getByText('System')).toBeInTheDocument()); - await userEvent.click(getByText('System')); + await userEvent.click(screen.getByText('System')); await waitFor(() => { expect(onChange).toHaveBeenCalledWith(['api', 'component', 'system']); @@ -81,7 +82,7 @@ describe('', () => { it('should return undefined if all values are selected', async () => { const onChange = jest.fn(); - const { getByLabelText, getByText } = await renderWithEffects( + await renderWithEffects( ', () => { /> , ); - await userEvent.click(getByLabelText('Open')); + await userEvent.click(screen.getByLabelText('Open')); - await waitFor(() => expect(getByText('Resource')).toBeInTheDocument()); + await waitFor(() => + expect(screen.getByText('Resource')).toBeInTheDocument(), + ); - await userEvent.click(getByText('Resource')); + await userEvent.click(screen.getByText('Resource')); await waitFor(() => { expect(onChange).toHaveBeenCalledWith(undefined); @@ -102,13 +105,13 @@ describe('', () => { it('should return all values when cleared', async () => { const onChange = jest.fn(); - const { getByRole } = await renderWithEffects( + await renderWithEffects( , ); - await userEvent.click(getByRole('combobox')); + await userEvent.click(screen.getByRole('combobox')); await userEvent.tab(); await waitFor(() => { diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedRelationsFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedRelationsFilter.test.tsx index bcc67edc3d..8baa7865b9 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedRelationsFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedRelationsFilter.test.tsx @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { RELATION_CHILD_OF, RELATION_HAS_MEMBER, RELATION_OWNED_BY, } from '@backstage/catalog-model'; -import { render, waitFor } from '@testing-library/react'; +import { render, waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { ALL_RELATION_PAIRS } from '../EntityRelationsGraph'; @@ -26,7 +27,7 @@ import { SelectedRelationsFilter } from './SelectedRelationsFilter'; describe('', () => { test('should render current value', () => { - const { getByText } = render( + render( ', () => { />, ); - expect(getByText(RELATION_OWNED_BY)).toBeInTheDocument(); - expect(getByText(RELATION_CHILD_OF)).toBeInTheDocument(); + expect(screen.getByText(RELATION_OWNED_BY)).toBeInTheDocument(); + expect(screen.getByText(RELATION_CHILD_OF)).toBeInTheDocument(); }); test('should select value', async () => { const onChange = jest.fn(); - const { getByText, getByLabelText } = render( + render( ', () => { />, ); - await userEvent.click(getByLabelText('Open')); + await userEvent.click(screen.getByLabelText('Open')); await waitFor(() => - expect(getByText(RELATION_HAS_MEMBER)).toBeInTheDocument(), + expect(screen.getByText(RELATION_HAS_MEMBER)).toBeInTheDocument(), ); - await userEvent.click(getByText(RELATION_HAS_MEMBER)); + await userEvent.click(screen.getByText(RELATION_HAS_MEMBER)); await waitFor(() => { expect(onChange).toHaveBeenCalledWith([ @@ -67,7 +68,7 @@ describe('', () => { test('should return undefined if all values are selected', async () => { const onChange = jest.fn(); - const { getByText, getByLabelText } = render( + render( p).filter( @@ -77,13 +78,13 @@ describe('', () => { />, ); - await userEvent.click(getByLabelText('Open')); + await userEvent.click(screen.getByLabelText('Open')); await waitFor(() => - expect(getByText(RELATION_HAS_MEMBER)).toBeInTheDocument(), + expect(screen.getByText(RELATION_HAS_MEMBER)).toBeInTheDocument(), ); - await userEvent.click(getByText(RELATION_HAS_MEMBER)); + await userEvent.click(screen.getByText(RELATION_HAS_MEMBER)); await waitFor(() => { expect(onChange).toHaveBeenCalledWith(undefined); @@ -92,7 +93,7 @@ describe('', () => { test('should return all values when cleared', async () => { const onChange = jest.fn(); - const { getByRole } = render( + render( ', () => { />, ); - await userEvent.click(getByRole('combobox')); + await userEvent.click(screen.getByRole('combobox')); await userEvent.tab(); await waitFor(() => { diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/SwitchFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/SwitchFilter.test.tsx index 08c7304989..322232d774 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/SwitchFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/SwitchFilter.test.tsx @@ -13,31 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { render } from '@testing-library/react'; + +import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { SwitchFilter } from './SwitchFilter'; describe('', () => { test('should render value', () => { - const { getByLabelText } = render( - {}} />, - ); + render( {}} />); - expect(getByLabelText('My label')).toBeInTheDocument(); - expect(getByLabelText('My label')).not.toBeChecked(); + expect(screen.getByLabelText('My label')).toBeInTheDocument(); + expect(screen.getByLabelText('My label')).not.toBeChecked(); }); test('should toggle value', async () => { const onChange = jest.fn(); - const { getByLabelText } = render( - , - ); + render(); - expect(getByLabelText('My label')).toBeInTheDocument(); - expect(getByLabelText('My label')).toBeChecked(); + expect(screen.getByLabelText('My label')).toBeInTheDocument(); + expect(screen.getByLabelText('My label')).toBeChecked(); - await userEvent.click(getByLabelText('My label')); + await userEvent.click(screen.getByLabelText('My label')); expect(onChange).toHaveBeenCalledWith(false); }); diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx index 5b8f50b983..ab280aea81 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx @@ -13,17 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { RELATION_CHILD_OF, RELATION_PARENT_OF, } from '@backstage/catalog-model'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import React from 'react'; import { CustomLabel } from './CustomLabel'; describe('', () => { test('renders label', () => { - const { getByText } = render( + render( ', () => { , ); - expect(getByText(RELATION_PARENT_OF)).toBeInTheDocument(); + expect(screen.getByText(RELATION_PARENT_OF)).toBeInTheDocument(); }); test('renders label with multiple relations', () => { - const { getByText } = render( + render( ', () => { , ); - expect(getByText(RELATION_PARENT_OF)).toBeInTheDocument(); - expect(getByText(RELATION_CHILD_OF)).toBeInTheDocument(); + expect(screen.getByText(RELATION_PARENT_OF)).toBeInTheDocument(); + expect(screen.getByText(RELATION_CHILD_OF)).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx index ff9fc9377e..7a34bbf688 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx @@ -13,14 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { CustomNode } from './CustomNode'; import userEvent from '@testing-library/user-event'; describe('', () => { test('renders node', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(getByText('kind:namespace/name')).toBeInTheDocument(); + expect(screen.getByText('kind:namespace/name')).toBeInTheDocument(); }); test('renders node, skips default namespace', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(getByText('kind:name')).toBeInTheDocument(); + expect(screen.getByText('kind:name')).toBeInTheDocument(); }); test('renders node with onClick', async () => { const onClick = jest.fn(); - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(getByText('kind:namespace/name')).toBeInTheDocument(); - await userEvent.click(getByText('kind:namespace/name')); + expect(screen.getByText('kind:namespace/name')).toBeInTheDocument(); + await userEvent.click(screen.getByText('kind:namespace/name')); expect(onClick).toHaveBeenCalledTimes(1); }); test('renders title if entity has one', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(getByText('Custom Title')).toBeInTheDocument(); + expect(screen.getByText('Custom Title')).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityKindIcon.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityKindIcon.test.tsx index 5d5f9dbf3c..6592c6195e 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityKindIcon.test.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityKindIcon.test.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { renderInTestApp } from '@backstage/test-utils'; import React from 'react'; import { EntityKindIcon } from './EntityKindIcon'; diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.test.tsx index d7b9f9e3eb..8a3352abb4 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.test.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.test.tsx @@ -24,6 +24,7 @@ import { import { DependencyGraphTypes } from '@backstage/core-components'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React, { FunctionComponent } from 'react'; import { EntityRelationsGraph } from './EntityRelationsGraph'; @@ -142,7 +143,7 @@ describe('', () => { relations: [], }); - const { findByText, findAllByTestId } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(1); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(1); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(1); }); test('renders a progress indicator while loading', async () => { catalog.getEntityByRef.mockImplementation(() => new Promise(() => {})); - const { findByRole } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(await findByRole('progressbar')).toBeInTheDocument(); + expect(await screen.findByRole('progressbar')).toBeInTheDocument(); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(1); }); @@ -197,7 +198,7 @@ describe('', () => { return undefined; }); - const { findByText, findAllByTestId } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(1); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(1); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(2); }); test('renders at max depth of one', async () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); - const { findByText, findAllByTestId, findAllByText } = - await renderInTestApp( - - - , - ); + await renderInTestApp( + + + , + ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('b:d/c1')).toBeInTheDocument(); - expect(await findByText('k:d/a1')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(3); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c1')).toBeInTheDocument(); + expect(await screen.findByText('k:d/a1')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(3); - expect(await findAllByText('ownerOf')).toHaveLength(1); - expect(await findAllByText('hasPart')).toHaveLength(1); - expect(await findAllByTestId('label')).toHaveLength(2); + expect(await screen.findAllByText('ownerOf')).toHaveLength(1); + expect(await screen.findAllByText('hasPart')).toHaveLength(1); + expect(await screen.findAllByTestId('label')).toHaveLength(2); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(3); }); @@ -238,26 +238,25 @@ describe('', () => { test('renders simplified graph at full depth', async () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); - const { findByText, findAllByText, findAllByTestId } = - await renderInTestApp( - - - , - ); + await renderInTestApp( + + + , + ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('b:d/c1')).toBeInTheDocument(); - expect(await findByText('k:d/a1')).toBeInTheDocument(); - expect(await findByText('b:d/c2')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(4); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c1')).toBeInTheDocument(); + expect(await screen.findByText('k:d/a1')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c2')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(4); - expect(await findAllByText('ownerOf')).toHaveLength(1); - expect(await findAllByText('hasPart')).toHaveLength(2); - expect(await findAllByTestId('label')).toHaveLength(3); + expect(await screen.findAllByText('ownerOf')).toHaveLength(1); + expect(await screen.findAllByText('hasPart')).toHaveLength(2); + expect(await screen.findAllByTestId('label')).toHaveLength(3); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(4); }); @@ -265,28 +264,27 @@ describe('', () => { test('renders full graph at full depth', async () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); - const { findAllByText, findByText, findAllByTestId } = - await renderInTestApp( - - - , - ); + await renderInTestApp( + + + , + ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('b:d/c1')).toBeInTheDocument(); - expect(await findByText('k:d/a1')).toBeInTheDocument(); - expect(await findByText('b:d/c2')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(4); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c1')).toBeInTheDocument(); + expect(await screen.findByText('k:d/a1')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c2')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(4); - expect(await findAllByText('ownerOf')).toHaveLength(2); - expect(await findAllByText('ownedBy')).toHaveLength(2); - expect(await findAllByText('hasPart')).toHaveLength(2); - expect(await findAllByText('partOf')).toHaveLength(2); - expect(await findAllByTestId('label')).toHaveLength(8); + expect(await screen.findAllByText('ownerOf')).toHaveLength(2); + expect(await screen.findAllByText('ownedBy')).toHaveLength(2); + expect(await screen.findAllByText('hasPart')).toHaveLength(2); + expect(await screen.findAllByText('partOf')).toHaveLength(2); + expect(await screen.findAllByTestId('label')).toHaveLength(8); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(4); }); @@ -294,26 +292,25 @@ describe('', () => { test('renders full graph at full depth with merged relations', async () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); - const { findAllByText, findByText, findAllByTestId } = - await renderInTestApp( - - - , - ); + await renderInTestApp( + + + , + ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('b:d/c1')).toBeInTheDocument(); - expect(await findByText('k:d/a1')).toBeInTheDocument(); - expect(await findByText('b:d/c2')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(4); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c1')).toBeInTheDocument(); + expect(await screen.findByText('k:d/a1')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c2')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(4); - expect(await findAllByText('ownerOf')).toHaveLength(2); - expect(await findAllByText('hasPart')).toHaveLength(2); - expect(await findAllByTestId('label')).toHaveLength(4); + expect(await screen.findAllByText('ownerOf')).toHaveLength(2); + expect(await screen.findAllByText('hasPart')).toHaveLength(2); + expect(await screen.findAllByTestId('label')).toHaveLength(4); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(4); }); @@ -321,27 +318,26 @@ describe('', () => { test('renders a graph with multiple root nodes', async () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); - const { findAllByText, findByText, findAllByTestId } = - await renderInTestApp( - - - , - ); + await renderInTestApp( + + + , + ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('b:d/c1')).toBeInTheDocument(); - expect(await findByText('k:d/a1')).toBeInTheDocument(); - expect(await findByText('b:d/c2')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(4); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c1')).toBeInTheDocument(); + expect(await screen.findByText('k:d/a1')).toBeInTheDocument(); + expect(await screen.findByText('b:d/c2')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(4); - expect(await findAllByText('ownerOf')).toHaveLength(1); - expect(await findAllByText('partOf')).toHaveLength(2); - expect(await findAllByTestId('label')).toHaveLength(3); + expect(await screen.findAllByText('ownerOf')).toHaveLength(1); + expect(await screen.findAllByText('partOf')).toHaveLength(2); + expect(await screen.findAllByTestId('label')).toHaveLength(3); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(4); }); @@ -349,23 +345,22 @@ describe('', () => { test('renders a graph with filtered kinds and relations', async () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); - const { findAllByText, findByText, findAllByTestId } = - await renderInTestApp( - - - , - ); + await renderInTestApp( + + + , + ); - expect(await findByText('b:d/c')).toBeInTheDocument(); - expect(await findByText('k:d/a1')).toBeInTheDocument(); - expect(await findAllByTestId('node')).toHaveLength(2); + expect(await screen.findByText('b:d/c')).toBeInTheDocument(); + expect(await screen.findByText('k:d/a1')).toBeInTheDocument(); + expect(await screen.findAllByTestId('node')).toHaveLength(2); - expect(await findAllByText('ownerOf')).toHaveLength(1); - expect(await findAllByTestId('label')).toHaveLength(1); + expect(await screen.findAllByText('ownerOf')).toHaveLength(1); + expect(await screen.findAllByTestId('label')).toHaveLength(1); expect(catalog.getEntityByRef).toHaveBeenCalledTimes(2); }); @@ -374,7 +369,7 @@ describe('', () => { catalog.getEntityByRef.mockImplementation(async n => entities[n as string]); const onNodeClick = jest.fn(); - const { findByText } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - await userEvent.click(await findByText('k:d/a1')); + await userEvent.click(await screen.findByText('k:d/a1')); expect(onNodeClick).toHaveBeenCalledTimes(1); }); @@ -397,7 +392,7 @@ describe('', () => { ); - const { findAllByTestId, container } = await renderInTestApp( + const { container } = await renderInTestApp( ', () => { , ); - const node = await findAllByTestId(CUSTOM_TEST_ID); + const node = await screen.findAllByTestId(CUSTOM_TEST_ID); expect(node[0]).toBeInTheDocument(); expect(container.querySelector('circle')).toBeInTheDocument(); }); @@ -421,7 +416,7 @@ describe('', () => { ); - const { findAllByTestId, findAllByText, container } = await renderInTestApp( + const { container } = await renderInTestApp( ', () => { /> , ); - const node = await findAllByTestId(CUSTOM_TEST_ID); + const node = await screen.findAllByTestId(CUSTOM_TEST_ID); expect(node[0]).toBeInTheDocument(); expect(container.querySelector('circle')).toBeInTheDocument(); - const labels = await findAllByText('Test-Labelvisible'); + const labels = await screen.findAllByText('Test-Labelvisible'); expect(labels[0]).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-import/.eslintrc.js b/plugins/catalog-import/.eslintrc.js index e2a53a6ad2..45bb6db521 100644 --- a/plugins/catalog-import/.eslintrc.js +++ b/plugins/catalog-import/.eslintrc.js @@ -1 +1,5 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { + rules: { + 'testing-library/prefer-screen-queries': 'error', + }, +}); diff --git a/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.test.tsx b/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.test.tsx index d6132b249e..a231563484 100644 --- a/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.test.tsx +++ b/plugins/catalog-import/src/components/DefaultImportPage/DefaultImportPage.test.tsx @@ -19,6 +19,7 @@ import { ApiProvider, ConfigReader } from '@backstage/core-app-api'; import { configApiRef } from '@backstage/core-plugin-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { catalogImportApiRef, CatalogImportClient } from '../../api'; import { DefaultImportPage } from './DefaultImportPage'; @@ -65,14 +66,14 @@ describe('', () => { }); it('renders without exploding', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( , ); expect( - getByText('Start tracking your component in Backstage'), + screen.getByText('Start tracking your component in Backstage'), ).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.test.tsx b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.test.tsx index 9a27532c57..c33de77ffd 100644 --- a/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.test.tsx +++ b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.test.tsx @@ -21,6 +21,7 @@ import { TestApiProvider, TestApiRegistry, } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { CatalogImportApi, catalogImportApiRef } from '../../api'; import { ImportInfoCard } from './ImportInfoCard'; @@ -49,7 +50,7 @@ describe('', () => { }); it('renders without exploding', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { , ); - expect(getByText('Register an existing component')).toBeInTheDocument(); + expect( + screen.getByText('Register an existing component'), + ).toBeInTheDocument(); }); it('renders section on GitHub discovery if supported', async () => { catalogImportApi.preparePullRequest = async () => ({ title: '', body: '' }); - const { getByText } = await renderInTestApp( + await renderInTestApp( , ); - expect(getByText(/The wizard discovers all/)).toBeInTheDocument(); + expect(screen.getByText(/The wizard discovers all/)).toBeInTheDocument(); }); it('renders section on pull requests if supported', async () => { catalogImportApi.preparePullRequest = async () => ({ title: '', body: '' }); - const { getByText } = await renderInTestApp( + await renderInTestApp( , ); expect( - getByText(/the wizard will prepare a Pull Request/), + screen.getByText(/the wizard will prepare a Pull Request/), ).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-import/src/components/ImportPage/ImportPage.test.tsx b/plugins/catalog-import/src/components/ImportPage/ImportPage.test.tsx index 498e786eb3..a38b801607 100644 --- a/plugins/catalog-import/src/components/ImportPage/ImportPage.test.tsx +++ b/plugins/catalog-import/src/components/ImportPage/ImportPage.test.tsx @@ -19,6 +19,7 @@ import { ApiProvider, ConfigReader } from '@backstage/core-app-api'; import { configApiRef } from '@backstage/core-plugin-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { useOutlet } from 'react-router'; import { catalogImportApiRef, CatalogImportClient } from '../../api'; @@ -71,26 +72,26 @@ describe('', () => { afterEach(() => jest.resetAllMocks()); it('renders without exploding', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( , ); expect( - getByText('Start tracking your component in Backstage'), + screen.getByText('Start tracking your component in Backstage'), ).toBeInTheDocument(); }); it('renders with custom children', async () => { (useOutlet as jest.Mock).mockReturnValue(
Hello World
); - const { getByText } = await renderInTestApp( + await renderInTestApp( , ); - expect(getByText('Hello World')).toBeInTheDocument(); + expect(screen.getByText('Hello World')).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.test.tsx b/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.test.tsx index 5a8110eb6c..a9d662cab0 100644 --- a/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.test.tsx +++ b/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.test.tsx @@ -16,7 +16,7 @@ import { errorApiRef } from '@backstage/core-plugin-api'; import { TestApiProvider } from '@backstage/test-utils'; -import { act, render } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { AnalyzeResult, catalogImportApiRef } from '../../api/'; @@ -60,19 +60,20 @@ describe('', () => { }); it('renders without exploding', async () => { - const { getByRole } = render( - undefined} />, - { - wrapper: Wrapper, - }, - ); + render( undefined} />, { + wrapper: Wrapper, + }); - expect(getByRole('textbox', { name: /Repository/i })).toBeInTheDocument(); - expect(getByRole('textbox', { name: /Repository/i })).toHaveValue(''); + expect( + screen.getByRole('textbox', { name: /Repository/i }), + ).toBeInTheDocument(); + expect(screen.getByRole('textbox', { name: /Repository/i })).toHaveValue( + '', + ); }); it('should use default analysis url', async () => { - const { getByRole } = render( + render( undefined} analysisUrl="https://default" @@ -82,8 +83,10 @@ describe('', () => { }, ); - expect(getByRole('textbox', { name: /Repository/i })).toBeInTheDocument(); - expect(getByRole('textbox', { name: /Repository/i })).toHaveValue( + expect( + screen.getByRole('textbox', { name: /Repository/i }), + ).toBeInTheDocument(); + expect(screen.getByRole('textbox', { name: /Repository/i })).toHaveValue( 'https://default', ); }); @@ -91,16 +94,13 @@ describe('', () => { it('should not analyze without url', async () => { const onAnalysisFn = jest.fn(); - const { getByRole } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); await act(async () => { try { - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); } catch { return; } @@ -114,26 +114,23 @@ describe('', () => { it('should not analyze invalid value', async () => { const onAnalysisFn = jest.fn(); - const { getByRole, getByText } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'http:/', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(catalogImportApi.analyzeUrl).toHaveBeenCalledTimes(0); expect(onAnalysisFn).toHaveBeenCalledTimes(0); expect(errorApi.post).toHaveBeenCalledTimes(0); expect( - getByText('Must start with http:// or https://.'), + screen.getByText('Must start with http:// or https://.'), ).toBeInTheDocument(); }); @@ -145,12 +142,9 @@ describe('', () => { locations: [location], } as AnalyzeResult; - const { getByRole } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); catalogImportApi.analyzeUrl.mockReturnValueOnce( Promise.resolve(analyzeResult), @@ -158,10 +152,10 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(1); @@ -182,12 +176,9 @@ describe('', () => { locations: [location, location], } as AnalyzeResult; - const { getByRole } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); catalogImportApi.analyzeUrl.mockReturnValueOnce( Promise.resolve(analyzeResult), @@ -195,10 +186,10 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository-1', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(1); @@ -218,12 +209,9 @@ describe('', () => { locations: [], } as AnalyzeResult; - const { getByRole, getByText } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); catalogImportApi.analyzeUrl.mockReturnValueOnce( Promise.resolve(analyzeResult), @@ -231,15 +219,15 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository-1', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(0); expect( - getByText('There are no entities at this location'), + screen.getByText('There are no entities at this location'), ).toBeInTheDocument(); expect(errorApi.post).toHaveBeenCalledTimes(0); }); @@ -262,12 +250,9 @@ describe('', () => { ], } as AnalyzeResult; - const { getByRole } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); catalogImportApi.analyzeUrl.mockReturnValueOnce( Promise.resolve(analyzeResult), @@ -275,10 +260,10 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository-2', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(1); @@ -300,12 +285,9 @@ describe('', () => { generatedEntities: [], } as AnalyzeResult; - const { getByRole, getByText } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); catalogImportApi.analyzeUrl.mockReturnValueOnce( Promise.resolve(analyzeResult), @@ -313,15 +295,15 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository-2', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(0); expect( - getByText("Couldn't generate entities for your repository"), + screen.getByText("Couldn't generate entities for your repository"), ).toBeInTheDocument(); expect(errorApi.post).toHaveBeenCalledTimes(0); }); @@ -344,7 +326,7 @@ describe('', () => { ], } as AnalyzeResult; - const { getByRole, getByText } = render( + render( , { wrapper: Wrapper, @@ -357,15 +339,15 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository-2', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(0); expect( - getByText("Couldn't generate entities for your repository"), + screen.getByText("Couldn't generate entities for your repository"), ).toBeInTheDocument(); expect(errorApi.post).toHaveBeenCalledTimes(0); }); @@ -373,12 +355,9 @@ describe('', () => { it('should report unknown type to the errorapi', async () => { const onAnalysisFn = jest.fn(); - const { getByRole, getByText } = render( - , - { - wrapper: Wrapper, - }, - ); + render(, { + wrapper: Wrapper, + }); catalogImportApi.analyzeUrl.mockReturnValueOnce( Promise.resolve({ type: 'unknown' } as any as AnalyzeResult), @@ -386,15 +365,15 @@ describe('', () => { await act(async () => { await userEvent.type( - getByRole('textbox', { name: /Repository/i }), + screen.getByRole('textbox', { name: /Repository/i }), 'https://my-repository-2', ); - await userEvent.click(getByRole('button', { name: /Analyze/i })); + await userEvent.click(screen.getByRole('button', { name: /Analyze/i })); }); expect(onAnalysisFn).toHaveBeenCalledTimes(0); expect( - getByText( + screen.getByText( 'Received unknown analysis result of type unknown. Please contact the support team.', ), ).toBeInTheDocument(); diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx index 3acb7390a9..02b67090fa 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx @@ -15,7 +15,7 @@ */ import { FormHelperText, TextField } from '@material-ui/core'; -import { act, render } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { asInputRef } from '../helpers'; @@ -25,7 +25,7 @@ describe('', () => { it('renders without exploding', async () => { const onSubmitFn = jest.fn(); - const { getByRole } = render( + render( defaultValues={{ main: 'default' }} render={({ register }) => ( @@ -39,7 +39,7 @@ describe('', () => { ); await act(async () => { - await userEvent.click(getByRole('button', { name: /submit/i })); + await userEvent.click(screen.getByRole('button', { name: /submit/i })); }); expect(onSubmitFn).toHaveBeenCalledTimes(1); @@ -49,7 +49,7 @@ describe('', () => { it('should register a text field', async () => { const onSubmitFn = jest.fn(); - const { getByRole, getByLabelText } = render( + render( defaultValues={{ main: 'default' }} render={({ register }) => ( @@ -67,9 +67,9 @@ describe('', () => { ); await act(async () => { - await userEvent.clear(getByLabelText('Main Field')); - await userEvent.type(getByLabelText('Main Field'), 'My Text'); - await userEvent.click(getByRole('button', { name: /submit/i })); + await userEvent.clear(screen.getByLabelText('Main Field')); + await userEvent.type(screen.getByLabelText('Main Field'), 'My Text'); + await userEvent.click(screen.getByRole('button', { name: /submit/i })); }); expect(onSubmitFn).toHaveBeenCalledTimes(1); @@ -79,7 +79,7 @@ describe('', () => { it('registers required attribute', async () => { const onSubmitFn = jest.fn(); - const { queryByText, getByRole } = render( + render( defaultValues={{}} render={({ formState, register }) => ( @@ -100,13 +100,17 @@ describe('', () => { />, ); - expect(queryByText('Error in required main field')).not.toBeInTheDocument(); + expect( + screen.queryByText('Error in required main field'), + ).not.toBeInTheDocument(); await act(async () => { - await userEvent.click(getByRole('button', { name: /submit/i })); + await userEvent.click(screen.getByRole('button', { name: /submit/i })); }); expect(onSubmitFn).not.toHaveBeenCalled(); - expect(queryByText('Error in required main field')).toBeInTheDocument(); + expect( + screen.queryByText('Error in required main field'), + ).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewPullRequestComponent.test.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewPullRequestComponent.test.tsx index 8d0d085b8a..d983b6d359 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewPullRequestComponent.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewPullRequestComponent.test.tsx @@ -15,7 +15,7 @@ */ import { makeStyles } from '@material-ui/core'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks'; import React from 'react'; import { PreviewPullRequestComponent } from './PreviewPullRequestComponent'; @@ -28,15 +28,15 @@ const useStyles = makeStyles({ describe('', () => { it('renders without exploding', async () => { - const { getByText } = render( + render( , ); - const title = getByText('My Title'); - const description = getByText('description', { selector: 'strong' }); + const title = screen.getByText('My Title'); + const description = screen.getByText('description', { selector: 'strong' }); expect(title).toBeInTheDocument(); expect(title).toBeVisible(); expect(description).toBeInTheDocument(); @@ -46,7 +46,7 @@ describe('', () => { it('renders card with custom styles', async () => { const { result } = renderHook(() => useStyles()); - const { getByText } = render( + render( ', () => { />, ); - const title = getByText('My Title'); - const description = getByText('description', { selector: 'strong' }); + const title = screen.getByText('My Title'); + const description = screen.getByText('description', { selector: 'strong' }); expect(title).toBeInTheDocument(); expect(title).not.toBeVisible(); expect(description).toBeInTheDocument(); @@ -65,7 +65,7 @@ describe('', () => { it('renders with custom styles', async () => { const { result } = renderHook(() => useStyles()); - const { getByText } = render( + render( ', () => { />, ); - const title = getByText('My Title'); - const description = getByText('description', { selector: 'strong' }); + const title = screen.getByText('My Title'); + const description = screen.getByText('description', { selector: 'strong' }); expect(title).toBeInTheDocument(); expect(title).toBeVisible(); expect(description).toBeInTheDocument(); diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.test.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.test.tsx index ae6225f912..36f1217c37 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.test.tsx @@ -102,7 +102,7 @@ describe('', () => { catalogApi.getEntities.mockReturnValue(Promise.resolve({ items: [] })); await act(async () => { - const { findByText } = render( + render( ', () => { }, ); - const title = await findByText('My title'); - const description = await findByText('body', { selector: 'strong' }); + const title = await screen.findByText('My title'); + const description = await screen.findByText('body', { + selector: 'strong', + }); expect(title).toBeInTheDocument(); expect(title).toBeVisible(); expect(description).toBeInTheDocument(); diff --git a/plugins/catalog-import/src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.test.tsx b/plugins/catalog-import/src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.test.tsx index 1817ece1b8..5a30c7735a 100644 --- a/plugins/catalog-import/src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareSelectLocations/StepPrepareSelectLocations.test.tsx @@ -15,7 +15,7 @@ */ import { renderInTestApp } from '@backstage/test-utils'; -import { act } from '@testing-library/react'; +import { act, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { AnalyzeResult } from '../../api'; @@ -58,7 +58,7 @@ describe('', () => { }); it('renders display locations to be added', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( undefined} @@ -66,15 +66,15 @@ describe('', () => { />, ); - expect(rendered.getByText('url-1')).toBeInTheDocument(); - expect(rendered.getByText('url-2')).toBeInTheDocument(); + expect(screen.getByText('url-1')).toBeInTheDocument(); + expect(screen.getByText('url-2')).toBeInTheDocument(); expect( - rendered.queryByText(/Select one or more locations/), + screen.queryByText(/Select one or more locations/), ).toBeInTheDocument(); expect( - rendered.queryByText(/locations already exist/), + screen.queryByText(/locations already exist/), ).not.toBeInTheDocument(); - expect(rendered.getByRole('button', { name: /Review/i })).toBeDisabled(); + expect(screen.getByRole('button', { name: /Review/i })).toBeDisabled(); }); it('should display existing locations only', async () => { @@ -95,7 +95,7 @@ describe('', () => { ], } as Extract; - const rendered = await renderInTestApp( + await renderInTestApp( undefined} @@ -103,15 +103,15 @@ describe('', () => { />, ); - expect(rendered.getByText(/my-target/)).toBeInTheDocument(); - expect(rendered.queryByText(/locations already exist/)).toBeInTheDocument(); + expect(screen.getByText(/my-target/)).toBeInTheDocument(); + expect(screen.queryByText(/locations already exist/)).toBeInTheDocument(); expect( - rendered.queryByText(/Select one or more locations/), + screen.queryByText(/Select one or more locations/), ).not.toBeInTheDocument(); }); it('should select and deselect all', async () => { - const { getByRole, getAllByRole } = await renderInTestApp( + await renderInTestApp( undefined} @@ -119,27 +119,31 @@ describe('', () => { />, ); - const checkboxes = getAllByRole('checkbox'); + const checkboxes = screen.getAllByRole('checkbox'); checkboxes.forEach(c => expect(c).not.toBeChecked()); - expect(getByRole('button', { name: /Review/i })).toBeDisabled(); + expect(screen.getByRole('button', { name: /Review/i })).toBeDisabled(); await act(async () => { - await userEvent.click(getByRole('button', { name: /Select All/i })); + await userEvent.click( + screen.getByRole('button', { name: /Select All/i }), + ); }); checkboxes.forEach(c => expect(c).toBeChecked()); - expect(getByRole('button', { name: /Review/i })).not.toBeDisabled(); + expect(screen.getByRole('button', { name: /Review/i })).not.toBeDisabled(); await act(async () => { - await userEvent.click(getByRole('button', { name: /Select All/i })); + await userEvent.click( + screen.getByRole('button', { name: /Select All/i }), + ); }); checkboxes.forEach(c => expect(c).not.toBeChecked()); - expect(getByRole('button', { name: /Review/i })).toBeDisabled(); + expect(screen.getByRole('button', { name: /Review/i })).toBeDisabled(); }); it('should preselect prepared locations', async () => { - const { getAllByRole } = await renderInTestApp( + await renderInTestApp( ', () => { />, ); - const checkboxes = getAllByRole('checkbox'); + const checkboxes = screen.getAllByRole('checkbox'); expect(checkboxes[0]).not.toBeChecked(); expect(checkboxes[1]).toBeChecked(); @@ -159,7 +163,7 @@ describe('', () => { }); it('should select items', async () => { - const { getAllByRole } = await renderInTestApp( + await renderInTestApp( undefined} @@ -167,7 +171,7 @@ describe('', () => { />, ); - const checkboxes = getAllByRole('checkbox'); + const checkboxes = screen.getAllByRole('checkbox'); checkboxes.forEach(c => expect(c).not.toBeChecked()); await act(async () => { @@ -188,7 +192,7 @@ describe('', () => { it('should go back', async () => { const onGoBack = jest.fn(); - const { getByRole } = await renderInTestApp( + await renderInTestApp( undefined} @@ -197,7 +201,7 @@ describe('', () => { ); await act(async () => { - await userEvent.click(getByRole('button', { name: /Back/i })); + await userEvent.click(screen.getByRole('button', { name: /Back/i })); }); expect(onGoBack).toHaveBeenCalledTimes(1); @@ -206,7 +210,7 @@ describe('', () => { it('should submit', async () => { const onPrepare = jest.fn(); - const { getAllByRole, getByRole } = await renderInTestApp( + await renderInTestApp( ', () => { />, ); - const checkboxes = getAllByRole('checkbox'); + const checkboxes = screen.getAllByRole('checkbox'); await act(async () => { await userEvent.click(checkboxes[1]); }); await act(async () => { - await userEvent.click(getByRole('button', { name: /Review/i })); + await userEvent.click(screen.getByRole('button', { name: /Review/i })); }); expect(onPrepare).toHaveBeenCalledTimes(1); diff --git a/plugins/catalog-react/.eslintrc.js b/plugins/catalog-react/.eslintrc.js index e2a53a6ad2..45bb6db521 100644 --- a/plugins/catalog-react/.eslintrc.js +++ b/plugins/catalog-react/.eslintrc.js @@ -1 +1,5 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { + rules: { + 'testing-library/prefer-screen-queries': 'error', + }, +}); diff --git a/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx b/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx index 65ad3f227a..8a6b7206c6 100644 --- a/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx @@ -19,7 +19,7 @@ import { Entity } from '@backstage/catalog-model'; import { ApiProvider } from '@backstage/core-app-api'; import { alertApiRef } from '@backstage/core-plugin-api'; import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils'; -import { fireEvent, waitFor } from '@testing-library/react'; +import { fireEvent, waitFor, screen } from '@testing-library/react'; import { capitalize } from 'lodash'; import { default as React } from 'react'; import { catalogApiRef } from '../../api'; @@ -75,7 +75,7 @@ describe('', () => { ); it('renders available entity kinds', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( ', () => { , ); - expect(rendered.getByText('Kind')).toBeInTheDocument(); + expect(screen.getByText('Kind')).toBeInTheDocument(); - const input = rendered.getByTestId('select'); + const input = screen.getByTestId('select'); fireEvent.click(input); - await waitFor(() => rendered.getByText('Domain')); + await waitFor(() => screen.getByText('Domain')); entities.forEach(entity => { expect( - rendered.getByRole('option', { + screen.getByRole('option', { name: capitalize(entity.kind as string), }), ).toBeInTheDocument(); @@ -102,7 +102,7 @@ describe('', () => { it('sets the selected kind filter', async () => { const updateFilters = jest.fn(); - const rendered = await renderWithEffects( + await renderWithEffects( ', () => { , ); - const input = rendered.getByTestId('select'); + const input = screen.getByTestId('select'); fireEvent.click(input); - await waitFor(() => rendered.getByText('Domain')); - fireEvent.click(rendered.getByText('Domain')); + await waitFor(() => screen.getByText('Domain')); + fireEvent.click(screen.getByText('Domain')); expect(updateFilters).toHaveBeenLastCalledWith({ kind: new EntityKindFilter('domain'), diff --git a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx index 47e85292e2..f925625b23 100644 --- a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx @@ -15,7 +15,7 @@ */ import { Entity } from '@backstage/catalog-model'; -import { fireEvent, render } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; import { MockEntityListContextProvider } from '../../testUtils/providers'; import { EntityLifecycleFilter } from '../../filters'; @@ -56,36 +56,36 @@ const sampleEntities: Entity[] = [ describe('', () => { it('renders all lifecycles', () => { - const rendered = render( + render( , ); - expect(rendered.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); - fireEvent.click(rendered.getByTestId('lifecycle-picker-expand')); + fireEvent.click(screen.getByTestId('lifecycle-picker-expand')); sampleEntities .map(e => e.spec?.lifecycle!) .forEach(lifecycle => { - expect(rendered.getByText(lifecycle as string)).toBeInTheDocument(); + expect(screen.getByText(lifecycle as string)).toBeInTheDocument(); }); }); it('renders unique lifecycles in alphabetical order', () => { - const rendered = render( + render( , ); - expect(rendered.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); - fireEvent.click(rendered.getByTestId('lifecycle-picker-expand')); + fireEvent.click(screen.getByTestId('lifecycle-picker-expand')); - expect(rendered.getAllByRole('option').map(o => o.textContent)).toEqual([ + expect(screen.getAllByRole('option').map(o => o.textContent)).toEqual([ 'experimental', 'production', ]); @@ -114,7 +114,7 @@ describe('', () => { it('adds lifecycles to filters', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { lifecycles: undefined, }); - fireEvent.click(rendered.getByTestId('lifecycle-picker-expand')); - fireEvent.click(rendered.getByText('production')); + fireEvent.click(screen.getByTestId('lifecycle-picker-expand')); + fireEvent.click(screen.getByText('production')); expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: new EntityLifecycleFilter(['production']), }); @@ -138,7 +138,7 @@ describe('', () => { it('removes lifecycles from filters', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: new EntityLifecycleFilter(['production']), }); - fireEvent.click(rendered.getByTestId('lifecycle-picker-expand')); - expect(rendered.getByLabelText('production')).toBeChecked(); + fireEvent.click(screen.getByTestId('lifecycle-picker-expand')); + expect(screen.getByLabelText('production')).toBeChecked(); - fireEvent.click(rendered.getByLabelText('production')); + fireEvent.click(screen.getByLabelText('production')); expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: undefined, }); diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.test.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.test.tsx index f648b51456..c816810b10 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.test.tsx @@ -15,7 +15,7 @@ */ import { Entity, parseEntityRef } from '@backstage/catalog-model'; -import { fireEvent, render } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; import { MockEntityListContextProvider } from '../../testUtils/providers'; import { EntityOwnerFilter } from '../../filters'; @@ -69,36 +69,36 @@ const sampleEntities: Entity[] = [ describe('', () => { it('renders all owners', () => { - const rendered = render( + render( , ); - expect(rendered.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner')).toBeInTheDocument(); - fireEvent.click(rendered.getByTestId('owner-picker-expand')); + fireEvent.click(screen.getByTestId('owner-picker-expand')); sampleEntities .flatMap(e => e.relations?.map(r => parseEntityRef(r.targetRef).name)) .forEach(owner => { - expect(rendered.getByText(owner as string)).toBeInTheDocument(); + expect(screen.getByText(owner as string)).toBeInTheDocument(); }); }); it('renders unique owners in alphabetical order', () => { - const rendered = render( + render( , ); - expect(rendered.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner')).toBeInTheDocument(); - fireEvent.click(rendered.getByTestId('owner-picker-expand')); + fireEvent.click(screen.getByTestId('owner-picker-expand')); - expect(rendered.getAllByRole('option').map(o => o.textContent)).toEqual([ + expect(screen.getAllByRole('option').map(o => o.textContent)).toEqual([ 'another-owner', 'some-owner', 'some-owner-2', @@ -128,7 +128,7 @@ describe('', () => { it('adds owners to filters', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { owners: undefined, }); - fireEvent.click(rendered.getByTestId('owner-picker-expand')); - fireEvent.click(rendered.getByText('some-owner')); + fireEvent.click(screen.getByTestId('owner-picker-expand')); + fireEvent.click(screen.getByText('some-owner')); expect(updateFilters).toHaveBeenLastCalledWith({ owners: new EntityOwnerFilter(['some-owner']), }); @@ -152,7 +152,7 @@ describe('', () => { it('removes owners from filters', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { expect(updateFilters).toHaveBeenLastCalledWith({ owners: new EntityOwnerFilter(['some-owner']), }); - fireEvent.click(rendered.getByTestId('owner-picker-expand')); - expect(rendered.getByLabelText('some-owner')).toBeChecked(); + fireEvent.click(screen.getByTestId('owner-picker-expand')); + expect(screen.getByLabelText('some-owner')).toBeChecked(); - fireEvent.click(rendered.getByLabelText('some-owner')); + fireEvent.click(screen.getByLabelText('some-owner')); expect(updateFilters).toHaveBeenLastCalledWith({ owner: undefined, }); diff --git a/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.test.tsx b/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.test.tsx index 8e90e04148..00d36e5e13 100644 --- a/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.test.tsx @@ -15,7 +15,7 @@ */ import { Entity } from '@backstage/catalog-model'; -import { fireEvent, render } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; import { EntityErrorFilter, EntityOrphanFilter } from '../../filters'; import { MockEntityListContextProvider } from '../../testUtils/providers'; @@ -52,23 +52,23 @@ const sampleEntities: Entity[] = [ describe('', () => { it('renders all processing status options', () => { - const rendered = render( + render( , ); - expect(rendered.getByText('Processing Status')).toBeInTheDocument(); + expect(screen.getByText('Processing Status')).toBeInTheDocument(); - fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); - expect(rendered.getByText('Is Orphan')).toBeInTheDocument(); - expect(rendered.getByText('Has Error')).toBeInTheDocument(); + fireEvent.click(screen.getByTestId('processing-status-picker-expand')); + expect(screen.getByText('Is Orphan')).toBeInTheDocument(); + expect(screen.getByText('Has Error')).toBeInTheDocument(); }); it('adds orphan to orphan filter', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { , ); - fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); - fireEvent.click(rendered.getByText('Is Orphan')); + fireEvent.click(screen.getByTestId('processing-status-picker-expand')); + fireEvent.click(screen.getByText('Is Orphan')); expect(updateFilters).toHaveBeenCalledWith({ orphan: new EntityOrphanFilter(true), }); @@ -89,7 +89,7 @@ describe('', () => { it('adds error to error filter', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { , ); - fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); - fireEvent.click(rendered.getByText('Has Error')); + fireEvent.click(screen.getByTestId('processing-status-picker-expand')); + fireEvent.click(screen.getByText('Has Error')); expect(updateFilters).toHaveBeenCalledWith({ error: new EntityErrorFilter(true), }); @@ -110,7 +110,7 @@ describe('', () => { it('remove orphan from orphan filter', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { , ); - fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); - fireEvent.click(rendered.getByText('Is Orphan')); + fireEvent.click(screen.getByTestId('processing-status-picker-expand')); + fireEvent.click(screen.getByText('Is Orphan')); expect(updateFilters).toHaveBeenCalledWith({ orphan: undefined, }); @@ -131,7 +131,7 @@ describe('', () => { it('remove error from error filter', () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { , ); - fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); - fireEvent.click(rendered.getByText('Has Error')); + fireEvent.click(screen.getByTestId('processing-status-picker-expand')); + fireEvent.click(screen.getByText('Has Error')); expect(updateFilters).toHaveBeenCalledWith({ error: undefined, }); diff --git a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.test.tsx b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.test.tsx index eaaaab39bc..b0a6b46bf5 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.test.tsx +++ b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.test.tsx @@ -15,6 +15,7 @@ */ import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { entityRouteRef } from '../../routes'; import { EntityRefLink } from './EntityRefLink'; @@ -33,16 +34,13 @@ describe('', () => { lifecycle: 'production', }, }; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, - ); + }); - expect(getByText('component:software')).toHaveAttribute( + expect(screen.getByText('component:software')).toHaveAttribute( 'href', '/catalog/default/component/software', ); @@ -62,15 +60,12 @@ describe('', () => { lifecycle: 'production', }, }; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, - ); - expect(getByText('component:test/software')).toHaveAttribute( + }); + expect(screen.getByText('component:test/software')).toHaveAttribute( 'href', '/catalog/test/component/software', ); @@ -90,7 +85,7 @@ describe('', () => { lifecycle: 'production', }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( , { mountedRoutes: { @@ -98,7 +93,7 @@ describe('', () => { }, }, ); - expect(getByText('test/software')).toHaveAttribute( + expect(screen.getByText('test/software')).toHaveAttribute( 'href', '/catalog/test/component/software', ); @@ -110,15 +105,12 @@ describe('', () => { namespace: 'default', name: 'software', }; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, - ); - expect(getByText('component:software')).toHaveAttribute( + }); + expect(screen.getByText('component:software')).toHaveAttribute( 'href', '/catalog/default/component/software', ); @@ -130,15 +122,12 @@ describe('', () => { namespace: 'test', name: 'software', }; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, - ); - expect(getByText('component:test/software')).toHaveAttribute( + }); + expect(screen.getByText('component:test/software')).toHaveAttribute( 'href', '/catalog/test/component/software', ); @@ -150,7 +139,7 @@ describe('', () => { namespace: 'test', name: 'software', }; - const { getByText } = await renderInTestApp( + await renderInTestApp( , { mountedRoutes: { @@ -158,7 +147,7 @@ describe('', () => { }, }, ); - expect(getByText('test/software')).toHaveAttribute( + expect(screen.getByText('test/software')).toHaveAttribute( 'href', '/catalog/test/component/software', ); @@ -170,7 +159,7 @@ describe('', () => { namespace: 'test', name: 'software', }; - const { getByText } = await renderInTestApp( + await renderInTestApp( Custom Children , @@ -180,7 +169,7 @@ describe('', () => { }, }, ); - expect(getByText('Custom Children')).toHaveAttribute( + expect(screen.getByText('Custom Children')).toHaveAttribute( 'href', '/catalog/test/component/software', ); diff --git a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.test.tsx b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.test.tsx index 147e139749..4e0d52ef4e 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.test.tsx +++ b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.test.tsx @@ -15,6 +15,7 @@ */ import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { entityRouteRef } from '../../routes'; import { EntityRefLinks } from './EntityRefLinks'; @@ -28,15 +29,12 @@ describe('', () => { name: 'software', }, ]; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, - ); - expect(getByText('component:software')).toHaveAttribute( + }); + expect(screen.getByText('component:software')).toHaveAttribute( 'href', '/catalog/default/component/software', ); @@ -55,20 +53,17 @@ describe('', () => { name: 'interface', }, ]; - const { getByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, - ); - expect(getByText(',')).toBeInTheDocument(); - expect(getByText('component:software')).toHaveAttribute( + }); + expect(screen.getByText(',')).toBeInTheDocument(); + expect(screen.getByText('component:software')).toHaveAttribute( 'href', '/catalog/default/component/software', ); - expect(getByText('api:interface')).toHaveAttribute( + expect(screen.getByText('api:interface')).toHaveAttribute( 'href', '/catalog/default/api/interface', ); diff --git a/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.test.tsx b/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.test.tsx index 36e2c0450d..9db6511662 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.test.tsx +++ b/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.test.tsx @@ -16,6 +16,7 @@ import { FetchedEntityRefLinks } from './FetchedEntityRefLinks'; import { entityRouteRef } from '../../routes'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import { Entity } from '@backstage/catalog-model'; import React from 'react'; import { JsonObject } from '@backstage/types'; @@ -60,7 +61,7 @@ describe('', () => { }), }; - const rendered = await renderInTestApp( + await renderInTestApp( , @@ -71,12 +72,12 @@ describe('', () => { }, ); - expect(rendered.getByText('SOFTWARE')).toHaveAttribute( + expect(screen.getByText('SOFTWARE')).toHaveAttribute( 'href', '/catalog/default/component/software', ); - expect(rendered.getByText('INTERFACE')).toHaveAttribute( + expect(screen.getByText('INTERFACE')).toHaveAttribute( 'href', '/catalog/default/api/interface', ); @@ -111,7 +112,7 @@ describe('', () => { const catalogApi: Partial = {}; - const rendered = await renderInTestApp( + await renderInTestApp( , @@ -122,12 +123,12 @@ describe('', () => { }, ); - expect(rendered.getByText('TOOL')).toHaveAttribute( + expect(screen.getByText('TOOL')).toHaveAttribute( 'href', '/catalog/default/component/tool', ); - expect(rendered.getByText('IMPLEMENTATION')).toHaveAttribute( + expect(screen.getByText('IMPLEMENTATION')).toHaveAttribute( 'href', '/catalog/default/api/implementation', ); @@ -189,7 +190,7 @@ describe('', () => { }), }; - const rendered = await renderInTestApp( + await renderInTestApp( , @@ -200,17 +201,17 @@ describe('', () => { }, ); - expect(rendered.getByText('TOOL')).toHaveAttribute( + expect(screen.getByText('TOOL')).toHaveAttribute( 'href', '/catalog/default/component/tool', ); - expect(rendered.getByText('IMPLEMENTATION')).toHaveAttribute( + expect(screen.getByText('IMPLEMENTATION')).toHaveAttribute( 'href', '/catalog/default/api/implementation', ); - expect(rendered.getByText('INTERFACE')).toHaveAttribute( + expect(screen.getByText('INTERFACE')).toHaveAttribute( 'href', '/catalog/default/component/interface', ); diff --git a/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.test.tsx b/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.test.tsx index 0b9ea9cb98..94db377e26 100644 --- a/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.test.tsx +++ b/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.test.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { fireEvent, render, waitFor } from '@testing-library/react'; +import { fireEvent, render, waitFor, screen } from '@testing-library/react'; import { EntitySearchBar } from './EntitySearchBar'; import { DefaultEntityFilters } from '../../hooks/useEntityListProvider'; import { EntityTextFilter } from '../../filters'; @@ -29,13 +29,13 @@ describe('EntitySearchBar', () => { text: new EntityTextFilter('hello'), }; - const { getByDisplayValue } = render( + render( , ); - const searchInput = getByDisplayValue('hello'); + const searchInput = screen.getByDisplayValue('hello'); expect(searchInput).toBeInTheDocument(); fireEvent.change(searchInput, { target: { value: 'world' } }); diff --git a/plugins/catalog-react/src/components/EntityTable/EntityTable.test.tsx b/plugins/catalog-react/src/components/EntityTable/EntityTable.test.tsx index 99f998118f..d5f1dc20d3 100644 --- a/plugins/catalog-react/src/components/EntityTable/EntityTable.test.tsx +++ b/plugins/catalog-react/src/components/EntityTable/EntityTable.test.tsx @@ -16,13 +16,13 @@ import { Entity } from '@backstage/catalog-model'; import { renderInTestApp } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { EntityTable } from './EntityTable'; describe('', () => { it('shows empty table', async () => { - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { />, ); - expect(getByText('Entities')).toBeInTheDocument(); - expect(getByText('EMPTY')).toBeInTheDocument(); + expect(screen.getByText('Entities')).toBeInTheDocument(); + expect(screen.getByText('EMPTY')).toBeInTheDocument(); }); it('shows entities', async () => { @@ -47,7 +47,7 @@ describe('', () => { }, ]; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { ); await waitFor(() => { - expect(getByText('my-entity')).toBeInTheDocument(); + expect(screen.getByText('my-entity')).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog-react/src/components/EntityTable/presets.test.tsx b/plugins/catalog-react/src/components/EntityTable/presets.test.tsx index cca9c7d494..82075df5e9 100644 --- a/plugins/catalog-react/src/components/EntityTable/presets.test.tsx +++ b/plugins/catalog-react/src/components/EntityTable/presets.test.tsx @@ -21,7 +21,7 @@ import { SystemEntity, } from '@backstage/catalog-model'; import { renderInTestApp } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { entityRouteRef } from '../../routes'; import { EntityTable } from './EntityTable'; @@ -54,7 +54,7 @@ describe('systemEntityColumns', () => { }, ]; - const { getByText } = await renderInTestApp( + await renderInTestApp( { ); await waitFor(() => { - expect(getByText('my-namespace/my-system')).toBeInTheDocument(); - expect(getByText('my-namespace/my-domain')).toBeInTheDocument(); - expect(getByText('test')).toBeInTheDocument(); - expect(getByText(/Some/)).toBeInTheDocument(); + expect(screen.getByText('my-namespace/my-system')).toBeInTheDocument(); + expect(screen.getByText('my-namespace/my-domain')).toBeInTheDocument(); + expect(screen.getByText('test')).toBeInTheDocument(); + expect(screen.getByText(/Some/)).toBeInTheDocument(); }); }); }); @@ -106,7 +106,7 @@ describe('componentEntityColumns', () => { }, ]; - const { getByText } = await renderInTestApp( + await renderInTestApp( { ); await waitFor(() => { - expect(getByText('my-namespace/my-component')).toBeInTheDocument(); - expect(getByText('my-namespace/my-system')).toBeInTheDocument(); - expect(getByText('test')).toBeInTheDocument(); - expect(getByText('production')).toBeInTheDocument(); - expect(getByText('service')).toBeInTheDocument(); - expect(getByText(/Some/)).toBeInTheDocument(); + expect(screen.getByText('my-namespace/my-component')).toBeInTheDocument(); + expect(screen.getByText('my-namespace/my-system')).toBeInTheDocument(); + expect(screen.getByText('test')).toBeInTheDocument(); + expect(screen.getByText('production')).toBeInTheDocument(); + expect(screen.getByText('service')).toBeInTheDocument(); + expect(screen.getByText(/Some/)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.test.tsx b/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.test.tsx index 78bdde1994..7da357d806 100644 --- a/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { fireEvent, render, waitFor } from '@testing-library/react'; +import { fireEvent, render, waitFor, screen } from '@testing-library/react'; import React from 'react'; import { MockEntityListContextProvider } from '../../testUtils/providers'; import { EntityTagFilter } from '../../filters'; @@ -35,34 +35,34 @@ describe('', () => { } as unknown as CatalogApi; it('renders all tags', async () => { - const rendered = render( + render( , ); - await waitFor(() => expect(rendered.getByText('Tags')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText('Tags')).toBeInTheDocument()); - fireEvent.click(rendered.getByTestId('tag-picker-expand')); + fireEvent.click(screen.getByTestId('tag-picker-expand')); tags.forEach(tag => { - expect(rendered.getByText(tag)).toBeInTheDocument(); + expect(screen.getByText(tag)).toBeInTheDocument(); }); }); it('renders unique tags in alphabetical order', async () => { - const rendered = render( + render( , ); - await waitFor(() => expect(rendered.getByText('Tags')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText('Tags')).toBeInTheDocument()); - fireEvent.click(rendered.getByTestId('tag-picker-expand')); + fireEvent.click(screen.getByTestId('tag-picker-expand')); - expect(rendered.getAllByRole('option').map(o => o.textContent)).toEqual([ + expect(screen.getAllByRole('option').map(o => o.textContent)).toEqual([ 'tag1', 'tag2', 'tag3', @@ -71,18 +71,18 @@ describe('', () => { }); it('renders tags with counts', async () => { - const rendered = render( + render( , ); - await waitFor(() => expect(rendered.getByText('Tags')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText('Tags')).toBeInTheDocument()); - fireEvent.click(rendered.getByTestId('tag-picker-expand')); + fireEvent.click(screen.getByTestId('tag-picker-expand')); - expect(rendered.getAllByRole('option').map(o => o.textContent)).toEqual([ + expect(screen.getAllByRole('option').map(o => o.textContent)).toEqual([ 'tag1 (0)', 'tag2 (1)', 'tag3 (2)', @@ -115,7 +115,7 @@ describe('', () => { it('adds tags to filters', async () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { }), ); - fireEvent.click(rendered.getByTestId('tag-picker-expand')); - fireEvent.click(rendered.getByText('tag1')); + fireEvent.click(screen.getByTestId('tag-picker-expand')); + fireEvent.click(screen.getByText('tag1')); expect(updateFilters).toHaveBeenLastCalledWith({ tags: new EntityTagFilter(['tag1']), }); @@ -141,7 +141,7 @@ describe('', () => { it('removes tags from filters', async () => { const updateFilters = jest.fn(); - const rendered = render( + render( ', () => { tags: new EntityTagFilter(['tag1']), }), ); - fireEvent.click(rendered.getByTestId('tag-picker-expand')); - expect(rendered.getByLabelText('tag1')).toBeChecked(); + fireEvent.click(screen.getByTestId('tag-picker-expand')); + expect(screen.getByLabelText('tag1')).toBeChecked(); - fireEvent.click(rendered.getByLabelText('tag1')); + fireEvent.click(screen.getByLabelText('tag1')); expect(updateFilters).toHaveBeenLastCalledWith({ tags: undefined, }); diff --git a/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx b/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx index 525c757dea..d61a6c480f 100644 --- a/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx @@ -15,13 +15,12 @@ */ import React from 'react'; -import { fireEvent, waitFor } from '@testing-library/react'; +import { fireEvent, waitFor, screen } from '@testing-library/react'; import { Entity } from '@backstage/catalog-model'; import { EntityTypePicker } from './EntityTypePicker'; import { MockEntityListContextProvider } from '../../testUtils/providers'; import { catalogApiRef } from '../../api'; import { EntityKindFilter, EntityTypeFilter } from '../../filters'; - import { alertApiRef } from '@backstage/core-plugin-api'; import { ApiProvider } from '@backstage/core-app-api'; import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils'; @@ -84,7 +83,7 @@ const apis = TestApiRegistry.from( describe('', () => { it('renders available entity types', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( ', () => { , ); - expect(rendered.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); - const input = rendered.getByTestId('select'); + const input = screen.getByTestId('select'); fireEvent.click(input); - await waitFor(() => rendered.getByText('service')); + await waitFor(() => screen.getByText('service')); entities.forEach(entity => { - expect( - rendered.getByText(entity.spec!.type as string), - ).toBeInTheDocument(); + expect(screen.getByText(entity.spec!.type as string)).toBeInTheDocument(); }); }); it('sets the selected type filter', async () => { const updateFilters = jest.fn(); - const rendered = await renderWithEffects( + await renderWithEffects( ', () => { , ); - const input = rendered.getByTestId('select'); + const input = screen.getByTestId('select'); fireEvent.click(input); - await waitFor(() => rendered.getByText('service')); - fireEvent.click(rendered.getByText('service')); + await waitFor(() => screen.getByText('service')); + fireEvent.click(screen.getByText('service')); expect(updateFilters).toHaveBeenLastCalledWith({ type: new EntityTypeFilter(['service']), }); fireEvent.click(input); - fireEvent.click(rendered.getByText('all')); + fireEvent.click(screen.getByText('all')); expect(updateFilters).toHaveBeenLastCalledWith({ type: undefined }); }); diff --git a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx index 3db2fab0fb..d5309a383d 100644 --- a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx +++ b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { fireEvent, render, waitFor } from '@testing-library/react'; +import { fireEvent, render, waitFor, screen } from '@testing-library/react'; import { Entity, RELATION_OWNED_BY, @@ -27,7 +27,6 @@ import { EntityTagFilter, UserListFilter } from '../../filters'; import { CatalogApi } from '@backstage/catalog-client'; import { catalogApiRef } from '../../api'; import { MockStorageApi, TestApiRegistry } from '@backstage/test-utils'; - import { ApiProvider } from '@backstage/core-app-api'; import { ConfigApi, @@ -144,7 +143,7 @@ const backendEntities: Entity[] = [ describe('', () => { it('renders filter groups', () => { - const { queryByText } = render( + render( @@ -152,12 +151,12 @@ describe('', () => { , ); - expect(queryByText('Personal')).toBeInTheDocument(); - expect(queryByText('Test Company')).toBeInTheDocument(); + expect(screen.queryByText('Personal')).toBeInTheDocument(); + expect(screen.queryByText('Test Company')).toBeInTheDocument(); }); it('renders filters', () => { - const { getAllByRole } = render( + render( @@ -166,12 +165,12 @@ describe('', () => { ); expect( - getAllByRole('menuitem').map(({ textContent }) => textContent), + screen.getAllByRole('menuitem').map(({ textContent }) => textContent), ).toEqual(['Owned 1', 'Starred 1', 'All 4']); }); it('includes counts alongside each filter', async () => { - const { getAllByRole } = render( + render( @@ -183,13 +182,13 @@ describe('', () => { // menuitem itself, so we pick off the next sibling. await waitFor(() => { expect( - getAllByRole('menuitem').map(({ textContent }) => textContent), + screen.getAllByRole('menuitem').map(({ textContent }) => textContent), ).toEqual(['Owned 1', 'Starred 1', 'All 4']); }); }); it('respects other frontend filters in counts', async () => { - const { getAllByRole } = render( + render( ', () => { await waitFor(() => { expect( - getAllByRole('menuitem').map(({ textContent }) => textContent), + screen.getAllByRole('menuitem').map(({ textContent }) => textContent), ).toEqual(['Owned 1', 'Starred 0', 'All 2']); }); }); @@ -229,7 +228,7 @@ describe('', () => { it('updates user filter when a menuitem is selected', () => { const updateFilters = jest.fn(); - const { getByText } = render( + render( ', () => { , ); - fireEvent.click(getByText('Starred')); + fireEvent.click(screen.getByText('Starred')); expect(updateFilters).toHaveBeenLastCalledWith({ user: new UserListFilter( diff --git a/plugins/catalog/.eslintrc.js b/plugins/catalog/.eslintrc.js index e2a53a6ad2..45bb6db521 100644 --- a/plugins/catalog/.eslintrc.js +++ b/plugins/catalog/.eslintrc.js @@ -1 +1,5 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { + rules: { + 'testing-library/prefer-screen-queries': 'error', + }, +}); diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx index c77787f717..869d6f1757 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx @@ -28,6 +28,7 @@ import { } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import userEvent from '@testing-library/user-event'; +import { screen } from '@testing-library/react'; import React from 'react'; import { viewTechDocRouteRef } from '../../routes'; import { AboutCard } from './AboutCard'; @@ -73,7 +74,7 @@ describe('', () => { ], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); - expect(getByText('service')).toBeInTheDocument(); - expect(getByText('user:guest')).toBeInTheDocument(); - expect(getByText('production')).toBeInTheDocument(); - expect(getByText('This is the description')).toBeInTheDocument(); + expect(screen.getByText('service')).toBeInTheDocument(); + expect(screen.getByText('user:guest')).toBeInTheDocument(); + expect(screen.getByText('production')).toBeInTheDocument(); + expect(screen.getByText('This is the description')).toBeInTheDocument(); }); it('renders "view source" link', async () => { @@ -122,7 +123,7 @@ describe('', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, }, ); - expect(getByText('View Source').closest('a')).toHaveAttribute( + expect(screen.getByText('View Source').closest('a')).toHaveAttribute( 'href', 'https://github.com/backstage/backstage/blob/master/software.yaml', ); @@ -177,7 +178,7 @@ describe('', () => { }, }; - const { getByTitle } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); - const editLink = getByTitle('Edit Metadata').closest('a'); + const editLink = screen.getByTitle('Edit Metadata').closest('a'); expect(editLink).toHaveAttribute( 'href', 'https://github.com/backstage/backstage/edit/master/software.yaml', @@ -230,7 +231,7 @@ describe('', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, }, ); - expect(getByText('View Source')).toBeVisible(); - expect(getByText('View Source').closest('a')).toBeNull(); + expect(screen.getByText('View Source')).toBeVisible(); + expect(screen.getByText('View Source').closest('a')).toBeNull(); }); it.each([ @@ -274,7 +275,7 @@ describe('', () => { }, }; - const { getByTitle } = await renderInTestApp( + await renderInTestApp( ', () => { 'component:default/software', ); - await userEvent.click(getByTitle('Schedule entity refresh')); + await userEvent.click(screen.getByTitle('Schedule entity refresh')); expect(catalogApi.refreshEntity).toHaveBeenCalledWith( 'component:default/software', @@ -320,7 +321,7 @@ describe('', () => { }, }; - const { queryByTitle } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); - expect(queryByTitle('Schedule entity refresh')).not.toBeInTheDocument(); + expect( + screen.queryByTitle('Schedule entity refresh'), + ).not.toBeInTheDocument(); }); it('renders techdocs link', async () => { @@ -361,7 +364,7 @@ describe('', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); - expect(getByText('View TechDocs').closest('a')).toHaveAttribute( + expect(screen.getByText('View TechDocs').closest('a')).toHaveAttribute( 'href', '/docs/default/Component/software', ); @@ -414,7 +417,7 @@ describe('', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); - expect(getByText('View TechDocs')).toBeVisible(); - expect(getByText('View TechDocs').closest('a')).toBeNull(); + expect(screen.getByText('View TechDocs')).toBeVisible(); + expect(screen.getByText('View TechDocs').closest('a')).toBeNull(); }); it('renders disabled techdocs link when route is not bound', async () => { @@ -467,7 +470,7 @@ describe('', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); - expect(getByText('View TechDocs')).toBeVisible(); - expect(getByText('View TechDocs').closest('a')).toBeNull(); + expect(screen.getByText('View TechDocs')).toBeVisible(); + expect(screen.getByText('View TechDocs').closest('a')).toBeNull(); }); }); diff --git a/plugins/catalog/src/components/AboutCard/AboutContent.test.tsx b/plugins/catalog/src/components/AboutCard/AboutContent.test.tsx index f4dbb73b45..2a89f2fd92 100644 --- a/plugins/catalog/src/components/AboutCard/AboutContent.test.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutContent.test.tsx @@ -21,6 +21,7 @@ import { } from '@backstage/catalog-model'; import { entityRouteRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { AboutContent } from './AboutContent'; @@ -62,32 +63,29 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('user:o'); - expect(getByText('Domain')).toBeInTheDocument(); - expect(getByText('Domain').nextSibling).toHaveTextContent('d'); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('s'); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('t'); - expect(getByText('Lifecycle')).toBeInTheDocument(); - expect(getByText('Lifecycle').nextSibling).toHaveTextContent('l'); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent('user:o'); + expect(screen.getByText('Domain')).toBeInTheDocument(); + expect(screen.getByText('Domain').nextSibling).toHaveTextContent('d'); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent('s'); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('t'); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle').nextSibling).toHaveTextContent('l'); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); }); it('highlights missing required fields', async () => { @@ -95,26 +93,25 @@ describe('', () => { entity.spec = {}; entity.relations = []; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(queryByText('Type')).not.toBeInTheDocument(); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.queryByText('Type')).not.toBeInTheDocument(); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); }); }); @@ -151,33 +148,34 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('system'); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('openapi'); - expect(getByText('Lifecycle')).toBeInTheDocument(); - expect(getByText('Lifecycle').nextSibling).toHaveTextContent( + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'user:guest', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent( + 'system', + ); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('openapi'); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle').nextSibling).toHaveTextContent( 'production', ); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); }); it('highlights missing required fields', async () => { @@ -187,31 +185,34 @@ describe('', () => { delete entity.spec!.system; entity.relations = []; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('No System'); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('unknown'); - expect(getByText('Lifecycle')).toBeInTheDocument(); - expect(getByText('Lifecycle').nextSibling).toHaveTextContent('unknown'); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent( + 'No System', + ); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('unknown'); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle').nextSibling).toHaveTextContent( + 'unknown', + ); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('No Tags'); }); }); @@ -252,36 +253,37 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('system'); - expect(getByText('Parent Component')).toBeInTheDocument(); - expect(getByText('Parent Component').nextSibling).toHaveTextContent( - 'parent-software', + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'user:guest', ); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('service'); - expect(getByText('Lifecycle')).toBeInTheDocument(); - expect(getByText('Lifecycle').nextSibling).toHaveTextContent( + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent( + 'system', + ); + expect(screen.getByText('Parent Component')).toBeInTheDocument(); + expect( + screen.getByText('Parent Component').nextSibling, + ).toHaveTextContent('parent-software'); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('service'); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle').nextSibling).toHaveTextContent( 'production', ); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); }); it('highlights missing required fields', async () => { @@ -291,31 +293,34 @@ describe('', () => { delete entity.spec!.system; entity.relations = []; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('No System'); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('unknown'); - expect(getByText('Lifecycle')).toBeInTheDocument(); - expect(getByText('Lifecycle').nextSibling).toHaveTextContent('unknown'); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent( + 'No System', + ); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('unknown'); + expect(screen.getByText('Lifecycle')).toBeInTheDocument(); + expect(screen.getByText('Lifecycle').nextSibling).toHaveTextContent( + 'unknown', + ); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('No Tags'); }); }); @@ -344,56 +349,54 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(queryByText('Type')).not.toBeInTheDocument(); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'user:guest', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.queryByText('Type')).not.toBeInTheDocument(); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); }); it('highlights missing required fields', async () => { delete entity.metadata.tags; entity.relations = []; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(queryByText('Type')).not.toBeInTheDocument(); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.queryByText('Type')).not.toBeInTheDocument(); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('No Tags'); }); }); @@ -418,31 +421,30 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('root'); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); - expect(getByText('Targets')).toBeInTheDocument(); - expect(getByText('Targets').nextSibling).toHaveTextContent( + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('root'); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Targets')).toBeInTheDocument(); + expect(screen.getByText('Targets').nextSibling).toHaveTextContent( 'https://backstage.io', ); }); @@ -451,29 +453,28 @@ describe('', () => { delete entity.metadata.tags; delete entity.spec!.type; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('unknown'); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('unknown'); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('No Tags'); }); }); @@ -508,30 +509,31 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('system'); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('s3'); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'user:guest', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent( + 'system', + ); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('s3'); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); }); it('highlights missing required fields', async () => { @@ -540,30 +542,31 @@ describe('', () => { delete entity.spec!.system; entity.relations = []; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(queryByText('Domain')).not.toBeInTheDocument(); - expect(getByText('System')).toBeInTheDocument(); - expect(getByText('System').nextSibling).toHaveTextContent('No System'); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(getByText('Type')).toBeInTheDocument(); - expect(getByText('Type').nextSibling).toHaveTextContent('unknown'); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.queryByText('Domain')).not.toBeInTheDocument(); + expect(screen.getByText('System')).toBeInTheDocument(); + expect(screen.getByText('System').nextSibling).toHaveTextContent( + 'No System', + ); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.getByText('Type')).toBeInTheDocument(); + expect(screen.getByText('Type').nextSibling).toHaveTextContent('unknown'); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('No Tags'); }); }); @@ -597,29 +600,30 @@ describe('', () => { }); it('renders info', async () => { - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('user:guest'); - expect(getByText('Domain')).toBeInTheDocument(); - expect(getByText('Domain').nextSibling).toHaveTextContent('domain'); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(queryByText('Type')).not.toBeInTheDocument(); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('tag-1'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'user:guest', + ); + expect(screen.getByText('Domain')).toBeInTheDocument(); + expect(screen.getByText('Domain').nextSibling).toHaveTextContent( + 'domain', + ); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.queryByText('Type')).not.toBeInTheDocument(); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('tag-1'); }); it('highlights missing required fields', async () => { @@ -627,29 +631,30 @@ describe('', () => { delete entity.spec!.domain; entity.relations = []; - const { getByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name': entityRouteRef, - }, + await renderInTestApp(, { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, }, - ); + }); - expect(getByText('Description')).toBeInTheDocument(); - expect(getByText('Description').nextSibling).toHaveTextContent( + expect(screen.getByText('Description')).toBeInTheDocument(); + expect(screen.getByText('Description').nextSibling).toHaveTextContent( 'This is the description', ); - expect(getByText('Owner')).toBeInTheDocument(); - expect(getByText('Owner').nextSibling).toHaveTextContent('No Owner'); - expect(getByText('Domain')).toBeInTheDocument(); - expect(getByText('Domain').nextSibling).toHaveTextContent('No Domain'); - expect(queryByText('System')).not.toBeInTheDocument(); - expect(queryByText('Parent Component')).not.toBeInTheDocument(); - expect(queryByText('Type')).not.toBeInTheDocument(); - expect(queryByText('Lifecycle')).not.toBeInTheDocument(); - expect(getByText('Tags')).toBeInTheDocument(); - expect(getByText('Tags').nextSibling).toHaveTextContent('No Tags'); + expect(screen.getByText('Owner')).toBeInTheDocument(); + expect(screen.getByText('Owner').nextSibling).toHaveTextContent( + 'No Owner', + ); + expect(screen.getByText('Domain')).toBeInTheDocument(); + expect(screen.getByText('Domain').nextSibling).toHaveTextContent( + 'No Domain', + ); + expect(screen.queryByText('System')).not.toBeInTheDocument(); + expect(screen.queryByText('Parent Component')).not.toBeInTheDocument(); + expect(screen.queryByText('Type')).not.toBeInTheDocument(); + expect(screen.queryByText('Lifecycle')).not.toBeInTheDocument(); + expect(screen.getByText('Tags')).toBeInTheDocument(); + expect(screen.getByText('Tags').nextSibling).toHaveTextContent('No Tags'); }); }); }); diff --git a/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.test.tsx b/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.test.tsx index 15d41f3ab3..9f68252ca7 100644 --- a/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.test.tsx +++ b/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.test.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { fireEvent } from '@testing-library/react'; +import { fireEvent, screen } from '@testing-library/react'; import { GetEntityFacetsResponse } from '@backstage/catalog-client'; import { Entity } from '@backstage/catalog-model'; import { @@ -83,7 +83,7 @@ const apis = TestApiRegistry.from( describe('', () => { it('renders available kinds', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( @@ -91,18 +91,18 @@ describe('', () => { , ); - const input = rendered.getByText('Components'); + const input = screen.getByText('Components'); fireEvent.mouseDown(input); entities.map(entity => { expect( - rendered.getByRole('option', { name: `${entity.kind}s` }), + screen.getByRole('option', { name: `${entity.kind}s` }), ).toBeInTheDocument(); }); }); it('renders unknown kinds provided in query parameters', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( ', () => { , ); - expect(rendered.getByText('Frobs')).toBeInTheDocument(); + expect(screen.getByText('Frobs')).toBeInTheDocument(); }); it('updates the kind filter', async () => { const updateFilters = jest.fn(); - const rendered = await renderWithEffects( + await renderWithEffects( @@ -125,10 +125,10 @@ describe('', () => { , ); - const input = rendered.getByText('Components'); + const input = screen.getByText('Components'); fireEvent.mouseDown(input); - const option = rendered.getByRole('option', { name: 'Templates' }); + const option = screen.getByRole('option', { name: 'Templates' }); fireEvent.click(option); expect(updateFilters).toHaveBeenCalledWith({ @@ -171,7 +171,7 @@ describe('', () => { }); it('limits kinds when allowedKinds is set', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( @@ -179,22 +179,20 @@ describe('', () => { , ); - const input = rendered.getByText('Components'); + const input = screen.getByText('Components'); fireEvent.mouseDown(input); expect( - rendered.getByRole('option', { name: 'Components' }), + screen.getByRole('option', { name: 'Components' }), ).toBeInTheDocument(); + expect(screen.getByRole('option', { name: 'Systems' })).toBeInTheDocument(); expect( - rendered.getByRole('option', { name: 'Systems' }), - ).toBeInTheDocument(); - expect( - rendered.queryByRole('option', { name: 'Templates' }), + screen.queryByRole('option', { name: 'Templates' }), ).not.toBeInTheDocument(); }); it('renders kind from the query parameter even when not in allowedKinds', async () => { - const rendered = await renderWithEffects( + await renderWithEffects( ', () => { , ); - expect(rendered.getByText('Frobs')).toBeInTheDocument(); - const input = rendered.getByText('Frobs'); + expect(screen.getByText('Frobs')).toBeInTheDocument(); + const input = screen.getByText('Frobs'); fireEvent.mouseDown(input); - expect( - rendered.getByRole('option', { name: 'Systems' }), - ).toBeInTheDocument(); + expect(screen.getByRole('option', { name: 'Systems' })).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx index 6fbdcf40a2..5b80f5976c 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx @@ -16,6 +16,7 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import { useOutlet } from 'react-router'; import { CatalogPage } from './CatalogPage'; @@ -30,15 +31,15 @@ jest.mock('./DefaultCatalogPage', () => ({ describe('CatalogPage', () => { it('renders provided router element', async () => { - const { getByText } = await renderInTestApp(); + await renderInTestApp(); - expect(getByText('Route Children')).toBeInTheDocument(); + expect(screen.getByText('Route Children')).toBeInTheDocument(); }); it('renders DefaultCatalogPage home when no router children are provided', async () => { (useOutlet as jest.Mock).mockReturnValueOnce(null); - const { getByText } = await renderInTestApp(); + await renderInTestApp(); - expect(getByText('DefaultCatalogPage')).toBeInTheDocument(); + expect(screen.getByText('DefaultCatalogPage')).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx index cc00a57314..969992bd4e 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx @@ -178,11 +178,11 @@ describe('DefaultCatalogPage', () => { // limit. We should investigate why these timeouts happen. it('should render the default column of the grid', async () => { - const { getAllByRole } = await renderWrapped(); + await renderWrapped(); - const columnHeader = getAllByRole('button').filter( - c => c.tagName === 'SPAN', - ); + const columnHeader = screen + .getAllByRole('button') + .filter(c => c.tagName === 'SPAN'); const columnHeaderLabels = columnHeader.map(c => c.textContent); expect(columnHeaderLabels).toEqual([ @@ -203,26 +203,22 @@ describe('DefaultCatalogPage', () => { { title: 'Bar', field: 'entity.bar' }, { title: 'Baz', field: 'entity.spec.lifecycle' }, ]; - const { getAllByRole } = await renderWrapped( - , - ); + await renderWrapped(); - const columnHeader = getAllByRole('button').filter( - c => c.tagName === 'SPAN', - ); + const columnHeader = screen + .getAllByRole('button') + .filter(c => c.tagName === 'SPAN'); const columnHeaderLabels = columnHeader.map(c => c.textContent); expect(columnHeaderLabels).toEqual(['Foo', 'Bar', 'Baz', 'Actions']); }, 20_000); it('should render the default actions of an item in the grid', async () => { - const { getByTestId, findByTitle, findByText } = await renderWrapped( - , - ); - fireEvent.click(getByTestId('user-picker-owned')); - expect(await findByText(/Owned \(1\)/)).toBeInTheDocument(); - expect(await findByTitle(/View/)).toBeInTheDocument(); - expect(await findByTitle(/Edit/)).toBeInTheDocument(); - expect(await findByTitle(/Add to favorites/)).toBeInTheDocument(); + await renderWrapped(); + fireEvent.click(screen.getByTestId('user-picker-owned')); + expect(await screen.findByText(/Owned \(1\)/)).toBeInTheDocument(); + expect(await screen.findByTitle(/View/)).toBeInTheDocument(); + expect(await screen.findByTitle(/Edit/)).toBeInTheDocument(); + expect(await screen.findByTitle(/Add to favorites/)).toBeInTheDocument(); }, 20_000); it('should render the custom actions of an item passed as prop', async () => { @@ -245,41 +241,35 @@ describe('DefaultCatalogPage', () => { }, ]; - const { getByTestId, findByTitle, findByText } = await renderWrapped( - , - ); - fireEvent.click(getByTestId('user-picker-owned')); - expect(await findByText(/Owned \(1\)/)).toBeInTheDocument(); - expect(await findByTitle(/Foo Action/)).toBeInTheDocument(); - expect(await findByTitle(/Bar Action/)).toBeInTheDocument(); - expect((await findByTitle(/Bar Action/)).firstChild).toBeDisabled(); + await renderWrapped(); + fireEvent.click(screen.getByTestId('user-picker-owned')); + expect(await screen.findByText(/Owned \(1\)/)).toBeInTheDocument(); + expect(await screen.findByTitle(/Foo Action/)).toBeInTheDocument(); + expect(await screen.findByTitle(/Bar Action/)).toBeInTheDocument(); + expect((await screen.findByTitle(/Bar Action/)).firstChild).toBeDisabled(); }, 20_000); // this test right now causes some red lines in the log output when running tests // related to some theme issues in mui-table // https://github.com/mbrn/material-table/issues/1293 it('should render', async () => { - const { findByText, getByTestId } = await renderWrapped( - , - ); - fireEvent.click(getByTestId('user-picker-owned')); - await expect(findByText(/Owned \(1\)/)).resolves.toBeInTheDocument(); - fireEvent.click(getByTestId('user-picker-all')); - await expect(findByText(/All \(2\)/)).resolves.toBeInTheDocument(); + await renderWrapped(); + fireEvent.click(screen.getByTestId('user-picker-owned')); + await expect(screen.findByText(/Owned \(1\)/)).resolves.toBeInTheDocument(); + fireEvent.click(screen.getByTestId('user-picker-all')); + await expect(screen.findByText(/All \(2\)/)).resolves.toBeInTheDocument(); }, 20_000); it('should set initial filter correctly', async () => { - const { findByText } = await renderWrapped( - , - ); - await expect(findByText(/All \(2\)/)).resolves.toBeInTheDocument(); + await renderWrapped(); + await expect(screen.findByText(/All \(2\)/)).resolves.toBeInTheDocument(); }, 20_000); // this test is for fixing the bug after favoriting an entity, the matching // entities defaulting to "owned" filter and not based on the selected filter it('should render the correct entities filtered on the selected filter', async () => { - const { getByTestId } = await renderWrapped(); - fireEvent.click(getByTestId('user-picker-owned')); + await renderWrapped(); + fireEvent.click(screen.getByTestId('user-picker-owned')); await expect(screen.findByText(/Owned \(1\)/)).resolves.toBeInTheDocument(); // The "Starred" menu option should initially be disabled, since there // aren't any starred entities. @@ -308,10 +298,12 @@ describe('DefaultCatalogPage', () => { it('should wrap filter in drawer on smaller screens', async () => { mockBreakpoint({ matches: true }); - const { getByRole } = await renderWrapped(); - const button = getByRole('button', { name: 'Filters' }); - expect(getByRole('presentation', { hidden: true })).toBeInTheDocument(); + await renderWrapped(); + const button = screen.getByRole('button', { name: 'Filters' }); + expect( + screen.getByRole('presentation', { hidden: true }), + ).toBeInTheDocument(); fireEvent.click(button); - expect(getByRole('presentation')).toBeVisible(); + expect(screen.getByRole('presentation')).toBeVisible(); }, 20_000); }); diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index bb2f302a37..652c9f3873 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -29,7 +29,7 @@ import { UserListFilter, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; -import { act, fireEvent } from '@testing-library/react'; +import { act, fireEvent, screen } from '@testing-library/react'; import * as React from 'react'; import { CatalogTable } from './CatalogTable'; @@ -66,7 +66,7 @@ describe('CatalogTable component', () => { }); it('should render error message', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( @@ -78,14 +78,14 @@ describe('CatalogTable component', () => { }, }, ); - const errorMessage = await rendered.findByText( + const errorMessage = await screen.findByText( /Could not fetch catalog entities./, ); expect(errorMessage).toBeInTheDocument(); }); it('should display entity names when loading has finished and no error occurred', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( { }, }, ); - expect(rendered.getByText(/Owned \(3\)/)).toBeInTheDocument(); - expect(rendered.getByText(/component1/)).toBeInTheDocument(); - expect(rendered.getByText(/component2/)).toBeInTheDocument(); - expect(rendered.getByText(/component3/)).toBeInTheDocument(); + expect(screen.getByText(/Owned \(3\)/)).toBeInTheDocument(); + expect(screen.getByText(/component1/)).toBeInTheDocument(); + expect(screen.getByText(/component2/)).toBeInTheDocument(); + expect(screen.getByText(/component3/)).toBeInTheDocument(); }); it('should use specified edit URL if in annotation', async () => { @@ -124,7 +124,7 @@ describe('CatalogTable component', () => { }, }; - const { getByTitle } = await renderInTestApp( + await renderInTestApp( @@ -137,7 +137,7 @@ describe('CatalogTable component', () => { }, ); - const editButton = getByTitle('Edit'); + const editButton = screen.getByTitle('Edit'); await act(async () => { fireEvent.click(editButton); @@ -156,7 +156,7 @@ describe('CatalogTable component', () => { }, }; - const { getByTitle } = await renderInTestApp( + await renderInTestApp( @@ -169,7 +169,7 @@ describe('CatalogTable component', () => { }, ); - const viewButton = getByTitle('View'); + const viewButton = screen.getByTitle('View'); await act(async () => { fireEvent.click(viewButton); @@ -278,7 +278,7 @@ describe('CatalogTable component', () => { ])( 'should render correct columns with kind filter $kind', async ({ kind, expectedColumns }) => { - const { getAllByRole } = await renderInTestApp( + await renderInTestApp( { }, ); - const columnHeader = getAllByRole('button').filter( - c => c.tagName === 'SPAN', - ); + const columnHeader = screen + .getAllByRole('button') + .filter(c => c.tagName === 'SPAN'); const columnHeaderLabels = columnHeader.map(c => c.textContent); expect(columnHeaderLabels).toEqual(expectedColumns); }, @@ -316,7 +316,7 @@ describe('CatalogTable component', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -329,7 +329,7 @@ describe('CatalogTable component', () => { }, ); - expect(getByText('Should be rendered')).toBeInTheDocument(); + expect(screen.getByText('Should be rendered')).toBeInTheDocument(); }); it('should render the label column with customised title and value as specified', async () => { @@ -347,7 +347,7 @@ describe('CatalogTable component', () => { }; const expectedColumns = ['Name', 'Category', 'Actions']; - const { getAllByRole, getByText } = await renderInTestApp( + await renderInTestApp( @@ -360,13 +360,13 @@ describe('CatalogTable component', () => { }, ); - const columnHeader = getAllByRole('button').filter( - c => c.tagName === 'SPAN', - ); + const columnHeader = screen + .getAllByRole('button') + .filter(c => c.tagName === 'SPAN'); const columnHeaderLabels = columnHeader.map(c => c.textContent); expect(columnHeaderLabels).toEqual(expectedColumns); - const labelCellValue = getByText('generic'); + const labelCellValue = screen.getByText('generic'); expect(labelCellValue).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/DependencyOfComponentsCard/DependencyOfComponentsCard.test.tsx b/plugins/catalog/src/components/DependencyOfComponentsCard/DependencyOfComponentsCard.test.tsx index b3f8262076..94a9a86b70 100644 --- a/plugins/catalog/src/components/DependencyOfComponentsCard/DependencyOfComponentsCard.test.tsx +++ b/plugins/catalog/src/components/DependencyOfComponentsCard/DependencyOfComponentsCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import React from 'react'; import { DependencyOfComponentsCard } from './DependencyOfComponentsCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -64,9 +64,9 @@ describe('', () => { }, ); - expect(getByText('Dependency of components')).toBeInTheDocument(); + expect(screen.getByText('Dependency of components')).toBeInTheDocument(); expect( - getByText(/No component depends on this component/i), + screen.getByText(/No component depends on this component/i), ).toBeInTheDocument(); }); @@ -99,7 +99,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -113,8 +113,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Dependency of components')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Dependency of components')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.test.tsx b/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.test.tsx index 57106015b2..657f95c60f 100644 --- a/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.test.tsx +++ b/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { DependsOnComponentsCard } from './DependsOnComponentsCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -64,9 +64,9 @@ describe('', () => { }, ); - expect(getByText('Depends on components')).toBeInTheDocument(); + expect(screen.getByText('Depends on components')).toBeInTheDocument(); expect( - getByText(/No component is a dependency of this component/i), + screen.getByText(/No component is a dependency of this component/i), ).toBeInTheDocument(); }); @@ -99,7 +99,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -113,8 +113,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Depends on components')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Depends on components')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.test.tsx b/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.test.tsx index bd52bfeeb8..5a77cd6ab4 100644 --- a/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.test.tsx +++ b/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { DependsOnResourcesCard } from './DependsOnResourcesCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -64,9 +64,9 @@ describe('', () => { }, ); - expect(getByText('Depends on resources')).toBeInTheDocument(); + expect(screen.getByText('Depends on resources')).toBeInTheDocument(); expect( - getByText(/No resource is a dependency of this component/i), + screen.getByText(/No resource is a dependency of this component/i), ).toBeInTheDocument(); }); @@ -99,7 +99,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -113,8 +113,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Depends on resources')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Depends on resources')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.test.tsx b/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.test.tsx index f9d7ca216f..f7220cb1ea 100644 --- a/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.test.tsx +++ b/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.test.tsx @@ -63,7 +63,7 @@ describe('ComponentContextMenu', () => { it('check Unregister entity button is disabled', async () => { const mockCallback = jest.fn(); - const { getByText } = await render( + await render( { const unregister = screen.getByText('Unregister entity'); expect(unregister).toBeInTheDocument(); - const unregisterSpanItem = getByText(/Unregister entity/); + const unregisterSpanItem = screen.getByText(/Unregister entity/); const unregisterMenuListItem = unregisterSpanItem?.parentElement?.parentElement; expect(unregisterMenuListItem).toHaveAttribute('aria-disabled'); diff --git a/plugins/catalog/src/components/EntityContextMenu/UnregisterEntity.test.tsx b/plugins/catalog/src/components/EntityContextMenu/UnregisterEntity.test.tsx index f1cd8c2546..fd57b79431 100644 --- a/plugins/catalog/src/components/EntityContextMenu/UnregisterEntity.test.tsx +++ b/plugins/catalog/src/components/EntityContextMenu/UnregisterEntity.test.tsx @@ -60,7 +60,7 @@ describe('ComponentContextMenu', () => { it('check Unregister entity button is disabled', async () => { const mockCallback = jest.fn(); - const { getByText } = await render( + await render( { const unregister = screen.getByText('Unregister entity'); expect(unregister).toBeInTheDocument(); - const unregisterSpanItem = getByText(/Unregister entity/); + const unregisterSpanItem = screen.getByText(/Unregister entity/); const unregisterMenuListItem = unregisterSpanItem?.parentElement?.parentElement; expect(unregisterMenuListItem).toHaveAttribute('aria-disabled'); diff --git a/plugins/catalog/src/components/EntityLayout/EntityLayout.test.tsx b/plugins/catalog/src/components/EntityLayout/EntityLayout.test.tsx index 548fff3ff1..980ce13109 100644 --- a/plugins/catalog/src/components/EntityLayout/EntityLayout.test.tsx +++ b/plugins/catalog/src/components/EntityLayout/EntityLayout.test.tsx @@ -32,7 +32,7 @@ import { renderInTestApp, TestApiRegistry, } from '@backstage/test-utils'; -import { act, fireEvent } from '@testing-library/react'; +import { act, fireEvent, screen } from '@testing-library/react'; import React from 'react'; import { EntityLayout } from './EntityLayout'; @@ -52,7 +52,7 @@ const mockApis = TestApiRegistry.from( describe('EntityLayout', () => { it('renders simplest case', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( @@ -69,9 +69,9 @@ describe('EntityLayout', () => { }, ); - expect(rendered.getByText('my-entity')).toBeInTheDocument(); - expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument(); - expect(rendered.getByText('tabbed-test-content')).toBeInTheDocument(); + expect(screen.getByText('my-entity')).toBeInTheDocument(); + expect(screen.getByText('tabbed-test-title')).toBeInTheDocument(); + expect(screen.getByText('tabbed-test-content')).toBeInTheDocument(); }); it('renders the entity title if defined', async () => { @@ -83,7 +83,7 @@ describe('EntityLayout', () => { }, } as Entity; - const rendered = await renderInTestApp( + await renderInTestApp( @@ -100,13 +100,13 @@ describe('EntityLayout', () => { }, ); - expect(rendered.getByText('My Entity')).toBeInTheDocument(); - expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument(); - expect(rendered.getByText('tabbed-test-content')).toBeInTheDocument(); + expect(screen.getByText('My Entity')).toBeInTheDocument(); + expect(screen.getByText('tabbed-test-title')).toBeInTheDocument(); + expect(screen.getByText('tabbed-test-content')).toBeInTheDocument(); }); it('renders default error message when entity is not found', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( @@ -123,14 +123,14 @@ describe('EntityLayout', () => { }, ); - expect(rendered.getByText('Warning: Entity not found')).toBeInTheDocument(); - expect(rendered.queryByText('my-entity')).not.toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-title')).not.toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument(); + expect(screen.getByText('Warning: Entity not found')).toBeInTheDocument(); + expect(screen.queryByText('my-entity')).not.toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-title')).not.toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-content')).not.toBeInTheDocument(); }); it('renders custom message when entity is not found', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( { ); expect( - rendered.getByText('Oppps.. Your entity was not found'), + screen.getByText('Oppps.. Your entity was not found'), ).toBeInTheDocument(); - expect(rendered.queryByText('my-entity')).not.toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-title')).not.toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument(); + expect(screen.queryByText('my-entity')).not.toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-title')).not.toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-content')).not.toBeInTheDocument(); }); it('navigates when user clicks different tab', async () => { - const rendered = await renderInTestApp( + await renderInTestApp( @@ -181,23 +181,23 @@ describe('EntityLayout', () => { }, ); - const secondTab = rendered.queryAllByRole('tab')[1]; + const secondTab = screen.queryAllByRole('tab')[1]; act(() => { fireEvent.click(secondTab); }); - expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument(); + expect(screen.getByText('tabbed-test-title')).toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-content')).not.toBeInTheDocument(); - expect(rendered.getByText('tabbed-test-title-2')).toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument(); + expect(screen.getByText('tabbed-test-title-2')).toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-content-2')).toBeInTheDocument(); }); it('should conditionally render tabs', async () => { const shouldRenderTab = (e: Entity) => e.metadata.name === 'my-entity'; const shouldNotRenderTab = (e: Entity) => e.metadata.name === 'some-entity'; - const rendered = await renderInTestApp( + await renderInTestApp( @@ -228,8 +228,8 @@ describe('EntityLayout', () => { }, ); - expect(rendered.queryByText('tabbed-test-title')).toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-title-2')).not.toBeInTheDocument(); - expect(rendered.queryByText('tabbed-test-title-3')).toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-title')).toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-title-2')).not.toBeInTheDocument(); + expect(screen.queryByText('tabbed-test-title-3')).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/EntityLinksCard/EntityLinksCard.test.tsx b/plugins/catalog/src/components/EntityLinksCard/EntityLinksCard.test.tsx index 8972cd2f9e..6316bdbc85 100644 --- a/plugins/catalog/src/components/EntityLinksCard/EntityLinksCard.test.tsx +++ b/plugins/catalog/src/components/EntityLinksCard/EntityLinksCard.test.tsx @@ -17,6 +17,7 @@ import { Entity, EntityLink } from '@backstage/catalog-model'; import { EntityProvider } from '@backstage/plugin-catalog-react'; import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { EntityLinksCard } from './EntityLinksCard'; @@ -43,7 +44,7 @@ describe('EntityLinksCard', () => { it('should render a link', async () => { const links: EntityLink[] = [createLink()]; - const { queryByText } = await renderWithEffects( + await renderWithEffects( wrapInTestApp( @@ -51,12 +52,12 @@ describe('EntityLinksCard', () => { ), ); - expect(queryByText('admin dashboard')).toBeInTheDocument(); - expect(queryByText('derp')).not.toBeInTheDocument(); + expect(screen.queryByText('admin dashboard')).toBeInTheDocument(); + expect(screen.queryByText('derp')).not.toBeInTheDocument(); }); it('should show empty state', async () => { - const { queryByText } = await renderWithEffects( + await renderWithEffects( wrapInTestApp( @@ -65,8 +66,8 @@ describe('EntityLinksCard', () => { ); expect( - queryByText(/.*No links defined for this entity.*/), + screen.queryByText(/.*No links defined for this entity.*/), ).toBeInTheDocument(); - expect(queryByText('admin dashboard')).not.toBeInTheDocument(); + expect(screen.queryByText('admin dashboard')).not.toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/EntityLinksCard/IconLink.test.tsx b/plugins/catalog/src/components/EntityLinksCard/IconLink.test.tsx index 9c1b021071..164e54a2eb 100644 --- a/plugins/catalog/src/components/EntityLinksCard/IconLink.test.tsx +++ b/plugins/catalog/src/components/EntityLinksCard/IconLink.test.tsx @@ -17,13 +17,13 @@ import { lightTheme } from '@backstage/theme'; import { ThemeProvider } from '@material-ui/core'; import CloudIcon from '@material-ui/icons/Cloud'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import React from 'react'; import { IconLink } from './IconLink'; describe('IconLink', () => { it('should render an icon link', () => { - const rendered = render( + render( { , ); - expect(rendered.queryByText('I am Link')).toBeInTheDocument(); + expect(screen.queryByText('I am Link')).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/EntityNotFound/EntityNotFound.test.tsx b/plugins/catalog/src/components/EntityNotFound/EntityNotFound.test.tsx index 4f16078eb9..6f214dc1e0 100644 --- a/plugins/catalog/src/components/EntityNotFound/EntityNotFound.test.tsx +++ b/plugins/catalog/src/components/EntityNotFound/EntityNotFound.test.tsx @@ -16,13 +16,16 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import { EntityNotFound } from './EntityNotFound'; describe('', () => { it('renders without exploding', async () => { - const { getByText } = await renderInTestApp(); - expect(getByText(/entity was not found/i)).toBeInTheDocument(); - expect(getByText(/getting started documentation/i)).toBeInTheDocument(); - expect(getByText(/docs/i)).toBeInTheDocument(); + await renderInTestApp(); + expect(screen.getByText(/entity was not found/i)).toBeInTheDocument(); + expect( + screen.getByText(/getting started documentation/i), + ).toBeInTheDocument(); + expect(screen.getByText(/docs/i)).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/EntityOrphanWarning/EntityOrphanWarning.test.tsx b/plugins/catalog/src/components/EntityOrphanWarning/EntityOrphanWarning.test.tsx index f00474868f..6071d15166 100644 --- a/plugins/catalog/src/components/EntityOrphanWarning/EntityOrphanWarning.test.tsx +++ b/plugins/catalog/src/components/EntityOrphanWarning/EntityOrphanWarning.test.tsx @@ -15,8 +15,8 @@ */ import { catalogApiRef, EntityProvider } from '@backstage/plugin-catalog-react'; - import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { rootRouteRef } from '../../routes'; import { EntityOrphanWarning } from './EntityOrphanWarning'; @@ -39,7 +39,7 @@ describe('', () => { }, }; - const { getByText } = await renderInTestApp( + await renderInTestApp( ', () => { }, ); expect( - getByText( + screen.getByText( 'This entity is not referenced by any location and is therefore not receiving updates. Click here to delete.', ), ).toBeInTheDocument(); diff --git a/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.test.tsx b/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.test.tsx index 2297493a7a..cfe3022261 100644 --- a/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.test.tsx +++ b/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.test.tsx @@ -23,6 +23,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { EntityProcessingErrorsPanel } from './EntityProcessingErrorsPanel'; @@ -100,7 +101,7 @@ describe('', () => { rootEntityRef: stringifyEntityRef(entity), items: [{ entity, parentEntityRefs: [] }], }); - const { getByText, queryByText } = await renderInTestApp( + await renderInTestApp( @@ -109,14 +110,14 @@ describe('', () => { ); expect( - getByText( + screen.getByText( 'Error: Policy check failed; caused by Error: Malformed envelope, /metadata/labels should be object', ), ).toBeInTheDocument(); - expect(getByText('Error: Foo')).toBeInTheDocument(); - expect(queryByText('Error: This should not be rendered')).toBeNull(); + expect(screen.getByText('Error: Foo')).toBeInTheDocument(); + expect(screen.queryByText('Error: This should not be rendered')).toBeNull(); expect( - queryByText('The error below originates from'), + screen.queryByText('The error below originates from'), ).not.toBeInTheDocument(); }); @@ -204,7 +205,7 @@ describe('', () => { { entity: parent, parentEntityRefs: [] }, ], }); - const { getByText, queryByText } = await renderInTestApp( + await renderInTestApp( @@ -218,12 +219,14 @@ describe('', () => { ); expect( - getByText( + screen.getByText( 'Error: Policy check failed; caused by Error: Malformed envelope, /metadata/labels should be object', ), ).toBeInTheDocument(); - expect(getByText('Error: Foo')).toBeInTheDocument(); - expect(queryByText('Error: This should not be rendered')).toBeNull(); - expect(queryByText('The error below originates from')).toBeInTheDocument(); + expect(screen.getByText('Error: Foo')).toBeInTheDocument(); + expect(screen.queryByText('Error: This should not be rendered')).toBeNull(); + expect( + screen.queryByText('The error below originates from'), + ).toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx b/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx index 4ee9d9c820..d2c51465b5 100644 --- a/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx +++ b/plugins/catalog/src/components/EntitySwitch/EntitySwitch.test.tsx @@ -19,7 +19,7 @@ import { AsyncEntityProvider, EntityProvider, } from '@backstage/plugin-catalog-react'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import React from 'react'; import { isKind } from './conditions'; import { EntitySwitch } from './EntitySwitch'; @@ -54,9 +54,9 @@ describe('EntitySwitch', () => { , ); - expect(rendered.queryByText('A')).toBeInTheDocument(); - expect(rendered.queryByText('B')).not.toBeInTheDocument(); - expect(rendered.queryByText('C')).not.toBeInTheDocument(); + expect(screen.queryByText('A')).toBeInTheDocument(); + expect(screen.queryByText('B')).not.toBeInTheDocument(); + expect(screen.queryByText('C')).not.toBeInTheDocument(); rendered.rerender( @@ -68,9 +68,9 @@ describe('EntitySwitch', () => { , ); - expect(rendered.queryByText('A')).not.toBeInTheDocument(); - expect(rendered.queryByText('B')).toBeInTheDocument(); - expect(rendered.queryByText('C')).not.toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); + expect(screen.queryByText('B')).toBeInTheDocument(); + expect(screen.queryByText('C')).not.toBeInTheDocument(); rendered.rerender( @@ -82,9 +82,9 @@ describe('EntitySwitch', () => { , ); - expect(rendered.queryByText('A')).not.toBeInTheDocument(); - expect(rendered.queryByText('B')).not.toBeInTheDocument(); - expect(rendered.queryByText('C')).toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); + expect(screen.queryByText('B')).not.toBeInTheDocument(); + expect(screen.queryByText('C')).toBeInTheDocument(); rendered.rerender( @@ -94,9 +94,9 @@ describe('EntitySwitch', () => { , ); - expect(rendered.queryByText('A')).not.toBeInTheDocument(); - expect(rendered.queryByText('B')).not.toBeInTheDocument(); - expect(rendered.queryByText('C')).toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); + expect(screen.queryByText('B')).not.toBeInTheDocument(); + expect(screen.queryByText('C')).toBeInTheDocument(); }); it('should switch child when filters switch', () => { @@ -113,8 +113,8 @@ describe('EntitySwitch', () => { , ); - expect(rendered.queryByText('A')).toBeInTheDocument(); - expect(rendered.queryByText('B')).not.toBeInTheDocument(); + expect(screen.queryByText('A')).toBeInTheDocument(); + expect(screen.queryByText('B')).not.toBeInTheDocument(); rendered.rerender( @@ -127,15 +127,15 @@ describe('EntitySwitch', () => { , ); - expect(rendered.queryByText('A')).not.toBeInTheDocument(); - expect(rendered.queryByText('B')).toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); + expect(screen.queryByText('B')).toBeInTheDocument(); }); it('should switch with async condition that is true', async () => { const entity = { metadata: { name: 'mock' }, kind: 'component' } as Entity; const shouldRender = () => Promise.resolve(true); - const rendered = render( + render( @@ -146,15 +146,15 @@ describe('EntitySwitch', () => { , ); - await expect(rendered.findByText('A')).resolves.toBeInTheDocument(); - expect(rendered.queryByText('B')).not.toBeInTheDocument(); + await expect(screen.findByText('A')).resolves.toBeInTheDocument(); + expect(screen.queryByText('B')).not.toBeInTheDocument(); }); it('should switch with sync condition that is false', async () => { const entity = { metadata: { name: 'mock' }, kind: 'component' } as Entity; const shouldRender = () => Promise.resolve(false); - const rendered = render( + render( @@ -165,15 +165,15 @@ describe('EntitySwitch', () => { , ); - await expect(rendered.findByText('B')).resolves.toBeInTheDocument(); - expect(rendered.queryByText('A')).not.toBeInTheDocument(); + await expect(screen.findByText('B')).resolves.toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); }); it('should switch with sync condition that throws', async () => { const entity = { metadata: { name: 'mock' }, kind: 'component' } as Entity; const shouldRender = () => Promise.reject(); - const rendered = render( + render( @@ -185,8 +185,8 @@ describe('EntitySwitch', () => { , ); - await expect(rendered.findByText('C')).resolves.toBeInTheDocument(); - expect(rendered.queryByText('A')).not.toBeInTheDocument(); - expect(rendered.queryByText('B')).not.toBeInTheDocument(); + await expect(screen.findByText('C')).resolves.toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); + expect(screen.queryByText('B')).not.toBeInTheDocument(); }); }); diff --git a/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.test.tsx b/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.test.tsx index 77fdb74f2f..9d46f2ed42 100644 --- a/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.test.tsx +++ b/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { HasComponentsCard } from './HasComponentsCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -64,9 +64,9 @@ describe('', () => { }, ); - expect(getByText('Has components')).toBeInTheDocument(); + expect(screen.getByText('Has components')).toBeInTheDocument(); expect( - getByText(/No component is part of this system/i), + screen.getByText(/No component is part of this system/i), ).toBeInTheDocument(); }); @@ -99,7 +99,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -113,8 +113,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Has components')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Has components')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/HasResourcesCard/HasResourcesCard.test.tsx b/plugins/catalog/src/components/HasResourcesCard/HasResourcesCard.test.tsx index 20830d2ccd..364d503a96 100644 --- a/plugins/catalog/src/components/HasResourcesCard/HasResourcesCard.test.tsx +++ b/plugins/catalog/src/components/HasResourcesCard/HasResourcesCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { HasResourcesCard } from './HasResourcesCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -59,9 +59,9 @@ describe('', () => { , ); - expect(getByText('Has resources')).toBeInTheDocument(); + expect(screen.getByText('Has resources')).toBeInTheDocument(); expect( - getByText(/No resource is part of this system/i), + screen.getByText(/No resource is part of this system/i), ).toBeInTheDocument(); }); @@ -94,7 +94,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -108,8 +108,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Has resources')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Has resources')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.test.tsx b/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.test.tsx index 7fd0273939..b76a0a68ec 100644 --- a/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.test.tsx +++ b/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { HasSubcomponentsCard } from './HasSubcomponentsCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -64,9 +64,9 @@ describe('', () => { }, ); - expect(getByText('Has subcomponents')).toBeInTheDocument(); + expect(screen.getByText('Has subcomponents')).toBeInTheDocument(); expect( - getByText(/No subcomponent is part of this component/i), + screen.getByText(/No subcomponent is part of this component/i), ).toBeInTheDocument(); }); @@ -99,7 +99,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -113,8 +113,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Has subcomponents')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Has subcomponents')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.test.tsx b/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.test.tsx index cd11e4d3f3..634fe57e82 100644 --- a/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.test.tsx +++ b/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.test.tsx @@ -22,7 +22,7 @@ import { entityRouteRef, } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { HasSystemsCard } from './HasSystemsCard'; @@ -51,7 +51,7 @@ describe('', () => { relations: [], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -64,8 +64,10 @@ describe('', () => { }, ); - expect(getByText('Has systems')).toBeInTheDocument(); - expect(getByText(/No system is part of this domain/i)).toBeInTheDocument(); + expect(screen.getByText('Has systems')).toBeInTheDocument(); + expect( + screen.getByText(/No system is part of this domain/i), + ).toBeInTheDocument(); }); it('shows related systems', async () => { @@ -97,7 +99,7 @@ describe('', () => { ], }); - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -111,8 +113,8 @@ describe('', () => { ); await waitFor(() => { - expect(getByText('Has systems')).toBeInTheDocument(); - expect(getByText(/target-name/i)).toBeInTheDocument(); + expect(screen.getByText('Has systems')).toBeInTheDocument(); + expect(screen.getByText(/target-name/i)).toBeInTheDocument(); }); }); }); diff --git a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.test.tsx b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.test.tsx index 18b45a84d4..dc8d68e2b2 100644 --- a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.test.tsx +++ b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.test.tsx @@ -22,6 +22,7 @@ import { } from '@backstage/plugin-catalog-react'; import { Entity, RELATION_PART_OF } from '@backstage/catalog-model'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; import React from 'react'; import { SystemDiagramCard } from './SystemDiagramCard'; @@ -53,7 +54,7 @@ describe('', () => { relations: [], }; - const { queryByText } = await renderInTestApp( + await renderInTestApp( @@ -66,9 +67,9 @@ describe('', () => { }, ); - expect(queryByText(/System Diagram/)).toBeInTheDocument(); - expect(queryByText(/namespace2\/system2/)).toBeInTheDocument(); - expect(queryByText(/namespace\/entity/)).not.toBeInTheDocument(); + expect(screen.queryByText(/System Diagram/)).toBeInTheDocument(); + expect(screen.queryByText(/namespace2\/system2/)).toBeInTheDocument(); + expect(screen.queryByText(/namespace\/entity/)).not.toBeInTheDocument(); }); it('shows related systems', async () => { @@ -108,7 +109,7 @@ describe('', () => { ], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -121,9 +122,9 @@ describe('', () => { }, ); - expect(getByText('System Diagram')).toBeInTheDocument(); - expect(getByText('namespace/system')).toBeInTheDocument(); - expect(getByText('namespace/entity')).toBeInTheDocument(); + expect(screen.getByText('System Diagram')).toBeInTheDocument(); + expect(screen.getByText('namespace/system')).toBeInTheDocument(); + expect(screen.getByText('namespace/entity')).toBeInTheDocument(); }); it('should truncate long domains, systems or entities', async () => { @@ -163,7 +164,7 @@ describe('', () => { ], }; - const { getByText } = await renderInTestApp( + await renderInTestApp( @@ -176,8 +177,8 @@ describe('', () => { }, ); - expect(getByText('namespace/alongdomai...')).toBeInTheDocument(); - expect(getByText('namespace/alongsyste...')).toBeInTheDocument(); - expect(getByText('namespace/alongentit...')).toBeInTheDocument(); + expect(screen.getByText('namespace/alongdomai...')).toBeInTheDocument(); + expect(screen.getByText('namespace/alongsyste...')).toBeInTheDocument(); + expect(screen.getByText('namespace/alongentit...')).toBeInTheDocument(); }); }); From a63e2df5598ec7f7138064010291692bfa229140 Mon Sep 17 00:00:00 2001 From: Alex Rybchenko Date: Fri, 18 Nov 2022 13:33:11 +0100 Subject: [PATCH 29/68] pass headerOptions to TemplatePage Signed-off-by: Alex Rybchenko --- .changeset/sour-plums-grow.md | 5 +++++ plugins/scaffolder/src/components/Router.tsx | 1 + .../components/TemplatePage/TemplatePage.tsx | 17 +++++++++++++---- 3 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 .changeset/sour-plums-grow.md diff --git a/.changeset/sour-plums-grow.md b/.changeset/sour-plums-grow.md new file mode 100644 index 0000000000..4070dc5e50 --- /dev/null +++ b/.changeset/sour-plums-grow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +fixed `headerOptions` not passed to `TemplatePage` component diff --git a/plugins/scaffolder/src/components/Router.tsx b/plugins/scaffolder/src/components/Router.tsx index 097eddf6cd..9f46b427a0 100644 --- a/plugins/scaffolder/src/components/Router.tsx +++ b/plugins/scaffolder/src/components/Router.tsx @@ -163,6 +163,7 @@ export const Router = (props: RouterProps) => { } diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index 87678d2a83..522f37dfc9 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -51,13 +51,21 @@ const useTemplateParameterSchema = (templateRef: string) => { return { schema: value, loading, error }; }; +type Props = { + customFieldExtensions?: FieldExtensionOptions[]; + layouts?: LayoutOptions[]; + headerOptions?: { + pageTitleOverride?: string; + title?: string; + subtitle?: string; + }; +}; + export const TemplatePage = ({ customFieldExtensions = [], layouts = [], -}: { - customFieldExtensions?: FieldExtensionOptions[]; - layouts?: LayoutOptions[]; -}) => { + headerOptions, +}: Props) => { const apiHolder = useApiHolder(); const secretsContext = useContext(SecretsContext); const errorApi = useApi(errorApiRef); @@ -136,6 +144,7 @@ export const TemplatePage = ({ pageTitleOverride="Create a New Component" title="Create a New Component" subtitle="Create new software components using standard templates" + {...headerOptions} /> {loading && } From 81b1e7b0fed9c27c6777fa5995c85a72ccbdd9c5 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Fri, 18 Nov 2022 14:24:23 +0100 Subject: [PATCH 30/68] Simplify base classes by using stream _construct methods Signed-off-by: Eric Peterson --- .changeset/search-om-manniskan-ginge.md | 5 +++ .../src/indexing/BatchSearchEngineIndexer.ts | 43 ++++++------------- .../src/indexing/DecoratorBase.ts | 38 ++++++---------- 3 files changed, 33 insertions(+), 53 deletions(-) create mode 100644 .changeset/search-om-manniskan-ginge.md diff --git a/.changeset/search-om-manniskan-ginge.md b/.changeset/search-om-manniskan-ginge.md new file mode 100644 index 0000000000..c36972f191 --- /dev/null +++ b/.changeset/search-om-manniskan-ginge.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-node': patch +--- + +Updated indexer and decorator base classes to take advantage of features introduced in Node.js v16; be sure you are running a [supported version of Node.js](https://backstage.io/docs/releases/v1.8.0#node-16-and-18). diff --git a/plugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts b/plugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts index 043e2c1953..b41dd524a9 100644 --- a/plugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts +++ b/plugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts @@ -34,26 +34,10 @@ export type BatchSearchEngineOptions = { export abstract class BatchSearchEngineIndexer extends Writable { private batchSize: number; private currentBatch: IndexableDocument[] = []; - private initialized: Promise; constructor(options: BatchSearchEngineOptions) { super({ objectMode: true }); this.batchSize = options.batchSize; - - // @todo Once node v15 is minimum, convert to _construct implementation. - this.initialized = new Promise(done => { - // Necessary to allow concrete implementation classes to construct - // themselves before calling their initialize() methods. - setImmediate(async () => { - try { - await this.initialize(); - done(undefined); - } catch (e) { - assertError(e); - done(e); - } - }); - }); } /** @@ -73,6 +57,20 @@ export abstract class BatchSearchEngineIndexer extends Writable { */ public abstract finalize(): Promise; + /** + * Encapsulates initialization logic. + * @internal + */ + async _construct(done: (error?: Error | null | undefined) => void) { + try { + await this.initialize(); + done(); + } catch (e) { + assertError(e); + done(e); + } + } + /** * Encapsulates batch stream write logic. * @internal @@ -82,13 +80,6 @@ export abstract class BatchSearchEngineIndexer extends Writable { _e: any, done: (error?: Error | null) => void, ) { - // Wait for init before proceeding. Throw error if initialization failed. - const maybeError = await this.initialized; - if (maybeError) { - done(maybeError); - return; - } - this.currentBatch.push(doc); if (this.currentBatch.length < this.batchSize) { done(); @@ -111,12 +102,6 @@ export abstract class BatchSearchEngineIndexer extends Writable { */ async _final(done: (error?: Error | null) => void) { try { - const maybeError = await this.initialized; - if (maybeError) { - done(maybeError); - return; - } - // Index any remaining documents. if (this.currentBatch.length) { await this.index(this.currentBatch); diff --git a/plugins/search-backend-node/src/indexing/DecoratorBase.ts b/plugins/search-backend-node/src/indexing/DecoratorBase.ts index bcf5729674..9ed8dfe0a7 100644 --- a/plugins/search-backend-node/src/indexing/DecoratorBase.ts +++ b/plugins/search-backend-node/src/indexing/DecoratorBase.ts @@ -24,25 +24,8 @@ import { Transform } from 'stream'; * @public */ export abstract class DecoratorBase extends Transform { - private initialized: Promise; - constructor() { super({ objectMode: true }); - - // @todo Once node v15 is minimum, convert to _construct implementation. - this.initialized = new Promise(done => { - // Necessary to allow concrete implementation classes to construct - // themselves before calling their initialize() methods. - setImmediate(async () => { - try { - await this.initialize(); - done(undefined); - } catch (e) { - assertError(e); - done(e); - } - }); - }); } /** @@ -68,6 +51,20 @@ export abstract class DecoratorBase extends Transform { */ public abstract finalize(): Promise; + /** + * Encapsulates initialization logic. + * @internal + */ + async _construct(done: (error?: Error | null | undefined) => void) { + try { + await this.initialize(); + done(); + } catch (e) { + assertError(e); + done(e); + } + } + /** * Encapsulates simple transform stream logic. * @internal @@ -77,13 +74,6 @@ export abstract class DecoratorBase extends Transform { _: any, done: (error?: Error | null) => void, ) { - // Wait for init before proceeding. Throw error if initialization failed. - const maybeError = await this.initialized; - if (maybeError) { - done(maybeError); - return; - } - try { const decorated = await this.decorate(document); From 26404430bc61f15e884dab80bb7adbf56dcbd948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 18 Nov 2022 15:49:51 +0100 Subject: [PATCH 31/68] Use Json types from @backstage/types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/funny-numbers-compete.md | 5 +++++ plugins/scaffolder-backend/src/service/router.ts | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/funny-numbers-compete.md diff --git a/.changeset/funny-numbers-compete.md b/.changeset/funny-numbers-compete.md new file mode 100644 index 0000000000..604ffe7294 --- /dev/null +++ b/.changeset/funny-numbers-compete.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Use Json types from @backstage/types diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index eff7e95c50..634e344a2c 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -23,9 +23,10 @@ import { stringifyEntityRef, UserEntity, } from '@backstage/catalog-model'; -import { Config, JsonObject, JsonValue } from '@backstage/config'; +import { Config } from '@backstage/config'; import { InputError, NotFoundError, stringifyError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; +import { JsonObject, JsonValue } from '@backstage/types'; import { TaskSpec, TemplateEntityV1beta3, From a50eaa5ad75d97d0f623e3ed601568292a208b12 Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Fri, 18 Nov 2022 10:04:32 -0500 Subject: [PATCH 32/68] Fix the URL of Humanitec plugin Signed-off-by: Taras Mankovski --- microsite/data/plugins/humanitec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/humanitec.yaml b/microsite/data/plugins/humanitec.yaml index 5b3c459116..1b79270582 100644 --- a/microsite/data/plugins/humanitec.yaml +++ b/microsite/data/plugins/humanitec.yaml @@ -6,7 +6,7 @@ category: Deployment # A single category e.g. CI, Machine Learning, Services, Mo description: | Show workloads, environments and resources deployed by Humanitec Platform Orchestrator. Plugin includes an Entity ComponentCard, Backend API route and scaffolder actions. -documentation: https://github.com/thefrontside/backstage/tree/main/plugins/humanitec +documentation: https://github.com/thefrontside/playhouse/tree/main/plugins/humanitec iconUrl: img/humanitec-logo.png npmPackageName: '@frontside/backstage-plugin-humanitec' addedDate: '2022-06-22' From ef41a4f3778c9857f78f633e8374a1b835223466 Mon Sep 17 00:00:00 2001 From: Brian Fletcher Date: Fri, 18 Nov 2022 15:09:45 +0000 Subject: [PATCH 33/68] add cloudsmith plugin to backstage microsite Signed-off-by: Brian Fletcher --- microsite/data/plugins/cloudsmith.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 microsite/data/plugins/cloudsmith.yaml diff --git a/microsite/data/plugins/cloudsmith.yaml b/microsite/data/plugins/cloudsmith.yaml new file mode 100644 index 0000000000..5fc417eec1 --- /dev/null +++ b/microsite/data/plugins/cloudsmith.yaml @@ -0,0 +1,13 @@ +--- +title: Cloudsmith +author: Roadie +authorUrl: https://roadie.io +category: CI/CD +description: Show Cloudsmith Repository stats on your backstage homepage. +documentation: https://github.com/RoadieHQ/roadie-backstage-plugins/tree/main/plugins/frontend/backstage-plugin-cloudsmith +iconUrl: https://cloudsmith.com/img/cloudsmith-mini-dark.svg +npmPackageName: '@roadiehq/backstage-plugin-cloudsmith' +tags: + - dashboards + - monitoring +addedDate: '2022-11-18' From d8593ce0e6cd21c0ec61056dbb47d7a7c6b5ebf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 18 Nov 2022 17:06:18 +0100 Subject: [PATCH 34/68] Do not use deprecated LocationSpec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/clever-pillows-drive.md | 5 ++++ plugins/catalog-backend/api-report.md | 27 ++++++++++--------- plugins/catalog-backend/src/index.ts | 17 +++++++++++- .../src/ingestion/CatalogRules.ts | 2 +- .../modules/codeowners/CodeOwnersProcessor.ts | 3 ++- .../core/AnnotateLocationEntityProcessor.ts | 2 +- .../core/AnnotateScmSlugEntityProcessor.ts | 4 ++- .../core/BuiltinKindsEntityProcessor.ts | 2 +- .../src/modules/core/FileReaderProcessor.ts | 2 +- .../modules/core/LocationEntityProcessor.ts | 2 +- .../src/modules/core/PlaceholderProcessor.ts | 2 +- .../src/modules/core/UrlReaderProcessor.ts | 2 +- .../catalog-backend/src/modules/util/parse.ts | 2 +- .../DefaultCatalogProcessingOrchestrator.ts | 2 +- .../catalog-backend/src/processing/util.ts | 2 +- .../catalog-backend/src/util/conversion.ts | 2 +- 16 files changed, 51 insertions(+), 27 deletions(-) create mode 100644 .changeset/clever-pillows-drive.md diff --git a/.changeset/clever-pillows-drive.md b/.changeset/clever-pillows-drive.md new file mode 100644 index 0000000000..f4e60fb869 --- /dev/null +++ b/.changeset/clever-pillows-drive.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Do not use deprecated `LocationSpec` from the `@backstage/plugin-catalog-node` package diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index 10b9afc291..e5d464c29a 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -37,7 +37,7 @@ import { EntityRelationSpec } from '@backstage/plugin-catalog-node'; import { GetEntitiesRequest } from '@backstage/catalog-client'; import { JsonValue } from '@backstage/types'; import { LocationEntityV1alpha1 } from '@backstage/catalog-model'; -import { LocationSpec } from '@backstage/plugin-catalog-node'; +import { LocationSpec as LocationSpec_2 } from '@backstage/plugin-catalog-common'; import { Logger } from 'winston'; import { Permission } from '@backstage/plugin-permission-common'; import { PermissionAuthorizer } from '@backstage/plugin-permission-common'; @@ -86,9 +86,9 @@ export class AnnotateLocationEntityProcessor implements CatalogProcessor { // (undocumented) preProcessEntity( entity: Entity, - location: LocationSpec, + location: LocationSpec_2, _: CatalogProcessorEmit, - originLocation: LocationSpec, + originLocation: LocationSpec_2, ): Promise; } @@ -100,7 +100,7 @@ export class AnnotateScmSlugEntityProcessor implements CatalogProcessor { // (undocumented) getProcessorName(): string; // (undocumented) - preProcessEntity(entity: Entity, location: LocationSpec): Promise; + preProcessEntity(entity: Entity, location: LocationSpec_2): Promise; } // @public (undocumented) @@ -110,7 +110,7 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { // (undocumented) postProcessEntity( entity: Entity, - _location: LocationSpec, + _location: LocationSpec_2, emit: CatalogProcessorEmit, ): Promise; // (undocumented) @@ -284,7 +284,7 @@ export class CodeOwnersProcessor implements CatalogProcessor { // (undocumented) getProcessorName(): string; // (undocumented) - preProcessEntity(entity: Entity, location: LocationSpec): Promise; + preProcessEntity(entity: Entity, location: LocationSpec_2): Promise; } // @alpha @@ -409,7 +409,7 @@ export class FileReaderProcessor implements CatalogProcessor { getProcessorName(): string; // (undocumented) readLocation( - location: LocationSpec, + location: LocationSpec_2, optional: boolean, emit: CatalogProcessorEmit, parser: CatalogProcessorParser, @@ -431,7 +431,7 @@ export class LocationEntityProcessor implements CatalogProcessor { // (undocumented) postProcessEntity( entity: Entity, - location: LocationSpec, + location: LocationSpec_2, emit: CatalogProcessorEmit, ): Promise; } @@ -441,18 +441,19 @@ export type LocationEntityProcessorOptions = { integrations: ScmIntegrationRegistry; }; -export { LocationSpec }; +// @public @deprecated +export type LocationSpec = LocationSpec_2; // @public (undocumented) export function locationSpecToLocationEntity(opts: { - location: LocationSpec; + location: LocationSpec_2; parentEntity?: Entity; }): LocationEntityV1alpha1; // @public (undocumented) export function parseEntityYaml( data: Buffer, - location: LocationSpec, + location: LocationSpec_2, ): Iterable; // @alpha @@ -518,7 +519,7 @@ export class PlaceholderProcessor implements CatalogProcessor { // (undocumented) preProcessEntity( entity: Entity, - location: LocationSpec, + location: LocationSpec_2, emit: CatalogProcessorEmit, ): Promise; } @@ -574,7 +575,7 @@ export class UrlReaderProcessor implements CatalogProcessor { getProcessorName(): string; // (undocumented) readLocation( - location: LocationSpec, + location: LocationSpec_2, optional: boolean, emit: CatalogProcessorEmit, parser: CatalogProcessorParser, diff --git a/plugins/catalog-backend/src/index.ts b/plugins/catalog-backend/src/index.ts index 88696498d0..000dd94e8b 100644 --- a/plugins/catalog-backend/src/index.ts +++ b/plugins/catalog-backend/src/index.ts @@ -22,7 +22,6 @@ export type { DeferredEntity, - LocationSpec, EntityRelationSpec, CatalogProcessor, CatalogProcessorParser, @@ -48,3 +47,19 @@ export * from './processing'; export * from './search'; export * from './service'; export * from './util'; + +import { LocationSpec as NonDeprecatedLocationSpec } from '@backstage/plugin-catalog-common'; + +/** + * Holds the entity location information. + * + * @remarks + * + * `presence` flag: when using repo importer plugin, location is being created before the component yaml file is merged to the main branch. + * This flag is then set to indicate that the file can be not present. + * default value: 'required'. + * + * @public + * @deprecated use the same type from `@backstage/plugin-catalog-common` instead + */ +export type LocationSpec = NonDeprecatedLocationSpec; diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.ts index ce226bd09d..548c71d833 100644 --- a/plugins/catalog-backend/src/ingestion/CatalogRules.ts +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.ts @@ -17,7 +17,7 @@ import { Config } from '@backstage/config'; import { Entity } from '@backstage/catalog-model'; import path from 'path'; -import { LocationSpec } from '@backstage/plugin-catalog-node'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; /** * Rules to apply to catalog entities. diff --git a/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.ts b/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.ts index 63f3d406f5..186b732271 100644 --- a/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.ts +++ b/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.ts @@ -22,7 +22,8 @@ import { ScmIntegrations, } from '@backstage/integration'; import { Logger } from 'winston'; -import { CatalogProcessor, LocationSpec } from '@backstage/plugin-catalog-node'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; +import { CatalogProcessor } from '@backstage/plugin-catalog-node'; import { findCodeOwnerByTarget } from './lib'; const ALLOWED_KINDS = ['API', 'Component', 'Domain', 'Resource', 'System']; diff --git a/plugins/catalog-backend/src/modules/core/AnnotateLocationEntityProcessor.ts b/plugins/catalog-backend/src/modules/core/AnnotateLocationEntityProcessor.ts index a86525429e..d3329fa86c 100644 --- a/plugins/catalog-backend/src/modules/core/AnnotateLocationEntityProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/AnnotateLocationEntityProcessor.ts @@ -25,10 +25,10 @@ import { } from '@backstage/catalog-model'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { identity, merge, pickBy } from 'lodash'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessor, CatalogProcessorEmit, - LocationSpec, } from '@backstage/plugin-catalog-node'; /** @public */ diff --git a/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.ts b/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.ts index 41e75e5e8e..2c18ba1d7d 100644 --- a/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Entity } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { @@ -21,7 +22,8 @@ import { } from '@backstage/integration'; import parseGitUrl from 'git-url-parse'; import { identity, merge, pickBy } from 'lodash'; -import { CatalogProcessor, LocationSpec } from '@backstage/plugin-catalog-node'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; +import { CatalogProcessor } from '@backstage/plugin-catalog-node'; const GITHUB_ACTIONS_ANNOTATION = 'github.com/project-slug'; const GITLAB_ACTIONS_ANNOTATION = 'gitlab.com/project-slug'; diff --git a/plugins/catalog-backend/src/modules/core/BuiltinKindsEntityProcessor.ts b/plugins/catalog-backend/src/modules/core/BuiltinKindsEntityProcessor.ts index 6e05daf591..4dbb23addc 100644 --- a/plugins/catalog-backend/src/modules/core/BuiltinKindsEntityProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/BuiltinKindsEntityProcessor.ts @@ -48,10 +48,10 @@ import { UserEntity, userEntityV1alpha1Validator, } from '@backstage/catalog-model'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessor, CatalogProcessorEmit, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-node'; diff --git a/plugins/catalog-backend/src/modules/core/FileReaderProcessor.ts b/plugins/catalog-backend/src/modules/core/FileReaderProcessor.ts index af6fba52c0..e0cd11fa3b 100644 --- a/plugins/catalog-backend/src/modules/core/FileReaderProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/FileReaderProcessor.ts @@ -18,11 +18,11 @@ import fs from 'fs-extra'; import g from 'glob'; import path from 'path'; import { promisify } from 'util'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessor, CatalogProcessorEmit, CatalogProcessorParser, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-node'; diff --git a/plugins/catalog-backend/src/modules/core/LocationEntityProcessor.ts b/plugins/catalog-backend/src/modules/core/LocationEntityProcessor.ts index 3676b4f727..1db74b3b56 100644 --- a/plugins/catalog-backend/src/modules/core/LocationEntityProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/LocationEntityProcessor.ts @@ -17,11 +17,11 @@ import { Entity, LocationEntity } from '@backstage/catalog-model'; import { ScmIntegrationRegistry } from '@backstage/integration'; import path from 'path'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { processingResult, CatalogProcessor, CatalogProcessorEmit, - LocationSpec, } from '@backstage/plugin-catalog-node'; export function toAbsoluteUrl( diff --git a/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts b/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts index e6b5ee279d..ec432c5fdc 100644 --- a/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts @@ -19,10 +19,10 @@ import { Entity } from '@backstage/catalog-model'; import { JsonValue } from '@backstage/types'; import { ScmIntegrationRegistry } from '@backstage/integration'; import yaml from 'yaml'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessor, CatalogProcessorEmit, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-node'; diff --git a/plugins/catalog-backend/src/modules/core/UrlReaderProcessor.ts b/plugins/catalog-backend/src/modules/core/UrlReaderProcessor.ts index fe9f21ebb4..5e31dcbd1a 100644 --- a/plugins/catalog-backend/src/modules/core/UrlReaderProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/UrlReaderProcessor.ts @@ -20,6 +20,7 @@ import { assertError } from '@backstage/errors'; import parseGitUrl from 'git-url-parse'; import limiterFactory from 'p-limit'; import { Logger } from 'winston'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessor, CatalogProcessorCache, @@ -27,7 +28,6 @@ import { CatalogProcessorEntityResult, CatalogProcessorParser, CatalogProcessorResult, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-node'; diff --git a/plugins/catalog-backend/src/modules/util/parse.ts b/plugins/catalog-backend/src/modules/util/parse.ts index d3aa915e49..778c176323 100644 --- a/plugins/catalog-backend/src/modules/util/parse.ts +++ b/plugins/catalog-backend/src/modules/util/parse.ts @@ -17,10 +17,10 @@ import { Entity, stringifyLocationRef } from '@backstage/catalog-model'; import lodash from 'lodash'; import yaml from 'yaml'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessorParser, CatalogProcessorResult, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-node'; diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts index 79876f70d8..e95fede8b7 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts @@ -32,10 +32,10 @@ import { JsonValue } from '@backstage/types'; import { ScmIntegrationRegistry } from '@backstage/integration'; import path from 'path'; import { Logger } from 'winston'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { CatalogProcessor, CatalogProcessorParser, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-node'; import { diff --git a/plugins/catalog-backend/src/processing/util.ts b/plugins/catalog-backend/src/processing/util.ts index bd96763a54..cd93be3c8b 100644 --- a/plugins/catalog-backend/src/processing/util.ts +++ b/plugins/catalog-backend/src/processing/util.ts @@ -27,7 +27,7 @@ import { JsonObject, JsonValue } from '@backstage/types'; import { InputError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; import path from 'path'; -import { LocationSpec } from '@backstage/plugin-catalog-node'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; export function isLocationEntity(entity: Entity): entity is LocationEntity { return entity.kind === 'Location'; diff --git a/plugins/catalog-backend/src/util/conversion.ts b/plugins/catalog-backend/src/util/conversion.ts index 83302722d2..0b92b88d90 100644 --- a/plugins/catalog-backend/src/util/conversion.ts +++ b/plugins/catalog-backend/src/util/conversion.ts @@ -23,7 +23,7 @@ import { stringifyLocationRef, } from '@backstage/catalog-model'; import { createHash } from 'crypto'; -import { LocationSpec } from '@backstage/plugin-catalog-node'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; export function locationSpecToMetadataName(location: LocationSpec) { const hash = createHash('sha1') From d19bbc8eb24a9138b7c7a81a1c249381e1bbbd57 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 18 Nov 2022 17:11:42 +0100 Subject: [PATCH 35/68] vale: add pattison Signed-off-by: Patrik Oldsberg --- .github/vale/Vocab/Backstage/accept.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index 9af9ae6137..f2b3ac0b09 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -241,6 +241,7 @@ pageview parallelization parseable Patrik +pattison Peloton performant Performant From cabd58d2bd95b76cb017d3b25be2896059f63516 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Fri, 28 Oct 2022 16:21:59 -0500 Subject: [PATCH 36/68] Added SidebarSubmenu for all Kinds Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- .changeset/lovely-walls-guess.md | 90 +++++++++++++++++++ packages/app/src/components/Root/Root.tsx | 57 +++++++++++- .../packages/app/src/components/Root/Root.tsx | 57 +++++++++++- 3 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 .changeset/lovely-walls-guess.md diff --git a/.changeset/lovely-walls-guess.md b/.changeset/lovely-walls-guess.md new file mode 100644 index 0000000000..edf068c7e3 --- /dev/null +++ b/.changeset/lovely-walls-guess.md @@ -0,0 +1,90 @@ +--- +'@backstage/create-app': patch +--- + +Added `SidebarSubmenu` for all Kinds making it easier to discover them in the Catalog. + +To add this to an existing Backstage instance you will need to make the following to your `packages/app/src/components/Root/Root.tsx`: + +1. First add the following imports: + + ```ts + import ApiIcon from '@material-ui/icons/Extension'; + import ComponentIcon from '@material-ui/icons/Memory'; + import DomainIcon from '@material-ui/icons/Apartment'; + import ResourceIcon from '@material-ui/icons/Work'; + import SystemIcon from '@material-ui/icons/Category'; + import UserIcon from '@material-ui/icons/Person'; + ``` + +2. Then update the `@backstage/core-components` import like this: + + ```diff + import { + Sidebar, + sidebarConfig, + SidebarDivider, + SidebarGroup, + SidebarItem, + SidebarPage, + SidebarScrollWrapper, + SidebarSpace, + useSidebarOpenState, + Link, + + GroupIcon, + + SidebarSubmenu, + + SidebarSubmenuItem, + } from '@backstage/core-components'; + ``` + +3. Finally replace `` with this: + + ```ts + + + + + + + + + + + + + + + + ``` diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 41e5b15db9..7bec873d49 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -47,12 +47,21 @@ import { SidebarSpace, Link, useSidebarOpenState, + SidebarSubmenu, + SidebarSubmenuItem, } from '@backstage/core-components'; import { MyGroupsSidebarItem } from '@backstage/plugin-org'; import GroupIcon from '@material-ui/icons/People'; import { SearchModal } from '../search/SearchModal'; import Score from '@material-ui/icons/Score'; +import ApiIcon from '@material-ui/icons/Extension'; +import ComponentIcon from '@material-ui/icons/Memory'; +import DomainIcon from '@material-ui/icons/Apartment'; +import ResourceIcon from '@material-ui/icons/Work'; +import SystemIcon from '@material-ui/icons/Category'; +import UserIcon from '@material-ui/icons/Person'; + const useSidebarLogoStyles = makeStyles({ root: { width: sidebarConfig.drawerWidthClosed, @@ -93,7 +102,53 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( }> {/* Global nav, not org-specific */} - + + + + + + + + + + + + + + + ) => ( }> {/* Global nav, not org-specific */} - + + + + + + + + + + + + + + + From 65630a9bcbe54d3380569f04d7979a5e506e3ad9 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Fri, 11 Nov 2022 08:16:53 -0600 Subject: [PATCH 37/68] Changes based on feedback Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- .changeset/lovely-walls-guess.md | 20 +++++++------------ packages/app/src/components/Root/Root.tsx | 20 +++++++------------ .../packages/app/src/components/Root/Root.tsx | 20 +++++++------------ 3 files changed, 21 insertions(+), 39 deletions(-) diff --git a/.changeset/lovely-walls-guess.md b/.changeset/lovely-walls-guess.md index edf068c7e3..eab92d8e9e 100644 --- a/.changeset/lovely-walls-guess.md +++ b/.changeset/lovely-walls-guess.md @@ -44,45 +44,39 @@ To add this to an existing Backstage instance you will need to make the followin - - diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 7bec873d49..8cec54326a 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -106,45 +106,39 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - - diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index b6f883ab2f..0acaba7470 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -78,45 +78,39 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - - From 9d2c6a7db46edf2f1c114e17bed4d1b7f4bc5b10 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Fri, 18 Nov 2022 12:25:50 -0600 Subject: [PATCH 38/68] Removed from create-app and added to docs Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- .changeset/lovely-walls-guess.md | 84 ---------------- .../sidebar-submenu-example.png | Bin 0 -> 26763 bytes docs/getting-started/app-custom-theme.md | 92 ++++++++++++++++++ .../packages/app/src/components/Root/Root.tsx | 42 +------- 4 files changed, 93 insertions(+), 125 deletions(-) delete mode 100644 .changeset/lovely-walls-guess.md create mode 100644 docs/assets/getting-started/sidebar-submenu-example.png diff --git a/.changeset/lovely-walls-guess.md b/.changeset/lovely-walls-guess.md deleted file mode 100644 index eab92d8e9e..0000000000 --- a/.changeset/lovely-walls-guess.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Added `SidebarSubmenu` for all Kinds making it easier to discover them in the Catalog. - -To add this to an existing Backstage instance you will need to make the following to your `packages/app/src/components/Root/Root.tsx`: - -1. First add the following imports: - - ```ts - import ApiIcon from '@material-ui/icons/Extension'; - import ComponentIcon from '@material-ui/icons/Memory'; - import DomainIcon from '@material-ui/icons/Apartment'; - import ResourceIcon from '@material-ui/icons/Work'; - import SystemIcon from '@material-ui/icons/Category'; - import UserIcon from '@material-ui/icons/Person'; - ``` - -2. Then update the `@backstage/core-components` import like this: - - ```diff - import { - Sidebar, - sidebarConfig, - SidebarDivider, - SidebarGroup, - SidebarItem, - SidebarPage, - SidebarScrollWrapper, - SidebarSpace, - useSidebarOpenState, - Link, - + GroupIcon, - + SidebarSubmenu, - + SidebarSubmenuItem, - } from '@backstage/core-components'; - ``` - -3. Finally replace `` with this: - - ```ts - - - - - - - - - - - - - - ``` diff --git a/docs/assets/getting-started/sidebar-submenu-example.png b/docs/assets/getting-started/sidebar-submenu-example.png new file mode 100644 index 0000000000000000000000000000000000000000..0b30f0ca6ad4c2e3e8609fe2375aff94c19b4e1b GIT binary patch literal 26763 zcmeFZcT^PHzxJt!(ttn%B1v)-B&s0Lm4x_VzX0yCR;3*WX>92IrnV9VG?;+vDQXgUX0|eU^ zL**eyc2!h?`|N?(j*hHhq@Fk0VywEc_r;t{)L{skAcMcJ?%fJ`Q)0#vPmf)1>c+>r zv+E+*s)?mnfdzjjFUKCq}PSjQ1Zy5_+^Z@nkJudj*;_&pMN~FAq2Wn(6=D{q)sV$ZYfP z;Y06O(~#pe^963sdl-Ers`5)K`9d8wh5gdcaW%ZZ-Wu`Hx`Re2axwgN=F3-Y;XC}> z?;KVLe{C7N?NGQs2HmY;qABzG`STkr;CI-Kn}Oyx(7|sv!4DbuxpCuGOu!8c@P7jE zgUmqtuUBv8W!(Dj->0ArMODRRWWfJbjqFWKtR2j39PMsI{Q7JUj-@opSOdSAg!6Bqb)xNhqJRYyYpjq8+%g@EzxNH^ih%yguVjugvD8AETY>HY#}InL%Zs?) z{{QyM|2pFTc&Fz7cqjMM|M|}U@yh@8o$3xI_Tn~H;E;|&|Le;9=gt4~#s9n!!2zB5 z|LBQ--F*F5(9c3x2#){0Wst;I_#lN#HW0GvYQPm&)e ziW)A*c`QexHfeGj9uP)JFCE{=b;HkPK}(snsoj-4n?9;Hg{jy6`0%vn^6*>37`VI( zye8*WZ`X%EvKy8fcg2KF?1q@8*ahf1jhW0XsK3xPiDgneeD2DDkA`&=-|0PUop9f~ z?r3^~(tSr%VC(yWAzFn=&wcH0*1C6F)7-Yqy^dR`He%%1g9|6d{F&0-f2FyuJ*+ud zPIj(*I7I9%B9az&iI2?agq2%O3~zxwB|Zx$ecTR@H2?j1Y;EXEq52mMr#`-w(b*>7 z!j~0qcpO)il7+qM>;C?dlaLgvQN4@4*Bv{4HU81*T-=#_8@f?@VLEP`6=xR`^3}gIKNx(k?@5Zt^*UdmD#3-bB*7NxylZ!};GZvwNC`+P_`jQ?()V)C z_M(&8>Cs%|GF}wv8Q_N8q?Non^13qhJ0TcrxIEwx*ew5DpjGyIxZcCfV&to?bpB51 zQ;Xqpx`=1Zu18x|KazRBkv)Aw(uKXy5y@Ly_B@%#KeN{vhlG>*(L#~|+spD|QQtfM zOT5#lmU_??#l7iM&NEY|2kXSA7L(sg3U#V}E&oV1<)}Z-^te1(DY4Ic^mE1_km9u; zy9_Q(%E@jA)kNGwRme#0!t-)P*ityh;1AJ5N8)O>d4tuRE=dRF<_KpOw-3{=&YAK=lwa>J|2X`eemUqg zVf9UkfnVR>Z0hZZ?r^DzoDKV-=UQG)z(%9R*Q)mW9D0@&79*AE7aOJ}n~#)cMxrz3 zXeB7b!r5OB$o7U$c^pk0cG5*6Uoh#uwgg>Pgkzv!?Dx!^qgp>s-1ch4>$2l;!lps< zDf)V5D0`{4byZuRz&2uivdV#^49OC}Y>LfC3_h~N?XHm;=L)lCdhKU=NrHT5ipm|g zMKi<$aZBv#FAgV4mW;7c}n45UJ;p$Q;(7yNE0Ok7G-#>)4PnRcmaEofTsx?(NCiJgPUl>iZzbpcm zwaEhccPGR6M4UQ@c1a99B3Hz}e4Bf z>@lNKR?gKTlW=gw{}YLF!uaS56jJ#EFiI`QO1URu zJ*a~hl1$SuDYeuxn76G;dmm`I1aQ*wkT_@>b4yxnj#nHuT%9#YelK%9SaUiYGwndG z#i=S_N?_i+{G;f_;_&!!QluZo!bE6g;nD_SXRx%y?GV1)5M z+Y0{;{q9Y-rT7;FeKj7tZNxO_owcgqxe?PM5M9M{ySg|M=l48@pX@hbP@%srj$Oj= zE(ldq%MgM4nQFh*muN0qo$w1$T8T;{>QkKapt0-a@zf2cI(;@=TN1bES z(+T8aqvm~mi6nQ*wOh~gG4D|-$ouWrSXCvh+xPGIB7ML3y)ACnD}_+5M+qF0P$0mMy}GO%i|8=fC9ngWIfLl6FTuo)5|&UN!u2j!;5V%VK#c$uWiqx zny5h$3B{9TM#BV5MWMq{qmR$i>12m*9=TNw73t3qJENRw7;dK@x>QN{kBpoM_%QZR zp=lB3p}fV+P;;UZD=G7B`9nNK<_#A0H3L!Tp^jm;w9~S+G*KiMyWR0pM8aRYPtQ|f zFEm2mHJHnO5ckwRmfT8imZshf6I`C?q-|A2 zK3mwPp?jj4_AdSplB!f}R=}$!q*~mV08_18z;CTd@XYV7@Gu=2aj&aDZG=~`rH@3p zUVaaI(17#lqJK~qGD8zKyL&u|=b&ml&+tK?=5M>8O05xspi7WMVU|QsK@HyAZRHh% z>1(r+dXk>&l3YM8=<4XGsu@wK6%Av5S9@9r^`{w}cJ1Fp6GxM$ycrrX|+Q)id)hTfmIyv`%BXqZ`bZC z9Ags3@WQ`^lk>K>PMrOA+k&a(NuLA#hPgc~w>eDF+*d`90L?fq-?$0!MMI`KIg;R+8 zh#Q7EuQKh8=5J0^ZWBLP^))K9bssw^LK+3=IzHOHSl#a|J?uYRtytB%+~nd-*xT#0V6sDhuL zB8A9?=TfOrib3x5=KP}fGqh#s~<%jLxx#=xT9LvC`? z_iho0%0FOM40F*f_osGH9O3Xf>4E=3T%GTSzbE(U2ya8!i6u~>zp)H$nATAjH-Fpd zJ%N0EW)wlTH9gLcH=gji`bYL*Z?*Bo;mOPuRgWCTL^4Y^iHYn&p@s<|>mqTN8L#%z z)u}E6MESr;d90W~`P^nZc3ddgdY#p?SuIz=akk%w-`uqVmD9MWo2L~UtYnBbd?O!^ z-(UZ|3edps>%g+BrOTL#gi%SRv}Xe-fhw=BRwLo}%W55%K=GygX{an}%7HZ2|>_qZpBa zlSW-KqUYu>Bxu=zAf0a{q=Cz&t#`P?(?}0o?o;D6NiZMHcwPPEH4N&E9-NC5G-PLW z_1)99sUs4L@gqo*%4BQrK@cuvJU>GFbjEAim-3UzXx6{jPI&Yd9s-`YL#_d1QtT$- zvD7TjPjC>)7_VeaE>s`l*q8Mj+F)Ya?io2V^@>c`=T6C*O5S^`Cp6|(PN5LDdtWfP zh6F`QjJ?Lt9bweKxjZoGuA8|lal3zF|Ls<^M92Dit<_9I?xwNP!7j2x+AV(U{^p5i zYnkk@SGmHuXK7(vZ#r&txQ8Shj;F9}e9krFx1y4U-@T7S<35uA&#+Ga|PX6lh$M2q#QQo#iKl6g50si_A+Wb!+iBt|ZOmf|AGJWg)C#{YOM^vR@0 zn=x;G%rqTm-al88Do;&M(CQ06nmXCmLY6G5LmZJ32fKO7-|V`jF45w;@ijf_B5Q4( zTXfDRE|=X1)5=SLYT0B}SQ+;7o74;`U3)OU-i~G@rwgf2hqfaXVe0p9kw(%m-}VuP z2hyo(;gz2)L!}wXOrIduH&4FLMR)G3y=?P;rdrtIFXbM@sZ90q+dldZ){f>cF7$vL zA13bQv~G(?C31rfWPr$C2rmIIJSXJW^1}LVl9GOAj#2CTr4f%V*H+Xh8|90^#GaO# zW^PicayGUXxZW&84+A>m&z+g(Y*y&%iHbB=nKpWyN?r{p9-=TkRhCj?nVSEH1KyLtRNCQOoIU1>!fZ0_RD1fPA=o8?IeUZW?sDD^OUP)<<+6r0r} zCVt$$Uy)kw6jwUR6M72Uv`Sj&J9lM1Q-^hIznpSiFBEw4oFt}KG)C6Aib*M();|N; z;>NDvxp&$}H|n{!vbfwVqNVUIrJOHG`Tk9#n*MdQJ3_);zXC(k-89SlpTW5~I{4FV z3|vgZFATj_lZ#0$=g;vC6DjtMi; z5x(C&*40tDT@z>4$Lck$qq_IH*Uit43HnK01PSgIqi94?Tz)>6@CSa+_+KoD%^Hfi zP!W5yFtC(bD!wj{Y>TARa?$VK4LYRK-R`+mV2;_PC)Dmf5ptoZ3l-tP+zAsyuxMQp zjtYruN-X@1{pv=!o4#9)Dw%#ir9E%l9h}JOrO?c-O%Y&lJ?oC?VbQ${Ol>yS?$SUTp)+9Rb2;U!i!C1{wE;HaLi z;@U#C*Ov$fpUSD}twVI;y-2*P%ByhK=j@VAa#%0^P86;2aXAnyA|tAJ+lHLJlcg75 z4$VYVX0HHo9PZH|!Pqsv-~M3iRVh)dG((XrEfkK5JmdL6~7kB6FC zRimw(MkIHd-KtSxF7AW}La*}POqol%+QYpeAz}>Z@r9psg~w875)OL^a5M*FIpEz= zY`gu+RMBwBg>*pZ)BL?VgeNVdTq3U`WE$B<^hIrETN31{2%U}}sbQ*BNpry!f>JVI zZdJ#{>`6JkFW%9-+-2%67WFEXyb;|LgBeOCCHz&3Tod1sKAOeHH&-{;9!*_{sg3gc z&tps+yZNl;ef2myn-CK`K8cp+1@D&vZhY)FTjk;pF(xJ(;C^|HmE1$k%TwVKdy;pbV=g}Pc6HQ!{m854DWwQI_pUOsxC?K(Vg$*21C9w21kHVFosGRU*gL-dR`3L79f9iHXnhJaj$0 zeHd(y=K|jg9QF&H^`g3v)Kr+#S$n&=X7aR3GU($K7N$hOgT)3dEP1?7>Coa80o^>2 zAIgL+Qh1jbb=9?#WvG%n;43hLB&0p})t)!AQW3y)4qG6x{#SZ6fd#iwbBj9g5|jnNX>Lp;1L!Ov>^ z)RV*ScfO}qBI?n_CMeH{PtGbr{e z-B0#*@&^NkH2Yx2Q?X)+UmGq@i(60%?6N3TsPsqadK?V#-O7xR0L2?Mq89#0zujTp z|Jy>3Kv~23a3MD3Ju7NlY-09u9C9Ab2G0vifMzP~79=4kN8vnaHw*}?cG>IjABkKo z?cwCa)rI;E^{IC4q=)mN90A+_8sQx1fdWAQbQunfevMOM-QlQO6es^0h{1Z@s(o;pLwq_`5&w+EBJj2oN&L!zTVR zdBKbYZ&To5)$I2Pv|xX6-UtF0qdfve?>8-i}_Wei2AD&vg zjAeWd0U1dcZiy@4b{nbA(9KB1@5;6AkjC7$VNPy4=PUE{2MG4xLMl6-t^^*ZcU{iz-EU4 zlZ*~XQCY!gE&$xk{N6n)YhbJOeNpCoREEI8K6I z=;E*HeP7!rZw8tD?w-_||7KjDu5*J6Avh3ey9*sMq)Knv04Cnq&uRP=0R!ggj zihseFYURFrEVcFXqj&+?)oP^h<#&(0E~b*VV|vGPcwT01TU9wzb{!Nn0co3;L;Aaf zJ3Ewmv4LlkzO-6scp)#!bxf3+RGT!u>X4$}v|)UMk|Ys}{~DcW2h0Hs{Im1@{+3b2 zXLEj8KP;zf`Tp>*2La;sCRM<>@)qthYLsz7@>2_qU;N`y9%xzzS?)*fdfmcIu8Q|? z<<-8{zRl{;14O;@Y^x>^z5vSEH$}C3Ar%`q&W0pZeH!DUzG!DIvT;ZOky#VAJ3*$N^i`3qHeK@nxJBI##ccIis8uLVO^Yc z{9FGRT_?lr7EMS<3`ZH;%ZUCzBp)kvou{6v&DA+?PtRSYZsSRoqBS?`ztN1(_EI5 zqVyDAQ(UdM_XAS6q*lYM3OP2|S}sU2IGOe2Qf5q?v!XJGDoqy#yN*mQ$urwmBm%S@ znqBO1OT+-8-g}`miLs5qu6=nog_Ce8+RrIrw;qVSI-$Kc&G{gnrvPD zu;9w;YWvFn#<3lX* zIlW&-ISIQQ^kJR607vbPSW^AJ5J_8pXXfX|BHA7><(JNzZbabmcU>gd2* z?}ilLS(fhG=?u0Rar1 zv{^~fW~a2xz!k?allT5Kwp6zRj(kV_cc{66HD=!lM=EjR@%iCRTp~^uV`=UvCf7k(;(pSrzwBQNfeOM)rw!gF#f@>4Oks^=o6d*F_%Tko8))PM>$ucod$gGkVn|rtU zUbtV>ue7|z)^G8-!i7C--YLOx+fHgJ?y1=@YY!Q^T6i6RpUJP6c{wJ-SyK(M& z3CU+Vj_385l{A{dXJh@%L-#j12fE$wcM~ZbF)jy2Ev}V!pjr(dBkWQ_dYP zz%Fy2pqI?Thht{|7II!KX()fj{ABf2DF}Js7xKBU#lcN9_M(zb9LG zMOyM(kR&+BJQY%(^o&s>v~5w&lDUb5r|bGxWLwNk4iUtXLE-eUW-k)L#+7t0&rXqM zIz!LZ`m-%;ABmV-ne8d}IKGNup3%4qr4x|_pS5^AFy^MP@1&u*+46h1N>PsSpFkz^ zPB~YTdL#a2QpI?Onk6fZ`%ef-8#(V4H^WR}Z7q7$}CCBFBZ|{#3XwfO-`aR-b zXiUh4zSDmaKdfhABS9^Q#z3d?$3WQ}Ki3tA=zP-xO00B|d71d;8IDcwe|j1psC1$x z(LwfW1z$n{TIBtc^i;Z^wJr_< z8^tYRbY-u9l(vNuZ|nHxv!Q8GD?uKy)~ci~xJ-*58X=WNj@Hu>h!^QGo#Y>YL;lkD z4vr;IAds3!HJ5!W@d^X&X~Xe+D22uwe{d~_uCp0K&U;pPQ#H=t`ceftPK}b@K7QRB zR4C{C5t!9Z&6rG@U{JSOXb;!OQJ}as(ZLQN)$Ewes#*^zXdtM5pXA+5h&j*T%Zov5 zjQ>%gRVsV^Jk(XWe!IX(n^EJFB#yv-?_;a!S{J7gUFWg%%fC!#XERrnAIP4VDKS1z z$Sd2fKYIxjQ+L7dz+d!HV_s(FrN8t-MMMdJf~2#2526GH8wExaEtMe}vrqBo zwM&gy=wW=S)ovn{NFDTkLfBF9l@Ea-Mv4m4k&6L8MS z-PQ}gSpt8J2etFch|Cc9*8x%ov%Yu%r{z7Uha9&Cm^?t@W2@Zhl; zAAH@PW>r*wnor?%z8la{?X>w4sGAY7vnL4!+oK{CQM?dTXPQaj{WKtg%W-J`p&u7P*?{5c8{on}K~lj~yV9}*!~jNt<+K6BOl6_=w>!Ik zhQ7Q9bEI1dNtQ_p27p4u>I9Ma7|t7C08z8)(z;c?kpr1k^vtNg7X z@S{DF$_DhMHJrBe9%Sp+HbJGq%W^XVqmGEDF=EniQ8<1Xmr*;({_C)}0Lw^fcb&c# zHa?LMNmKKrzzdf|XNhkFse(;!49eU=p1KlzI!ScJpenZ3AU=A;$8R&90x)btZ&P1p z_midfqRjA^oHW<&aiFab49mrEUO!`yB+&pa$Itd@w{40TPNCD)Pd_O#eH-0(nlR?} zoBgpjo3T0W(ugAO19Ph!3ZfV!y$vUOiZxBjNC4dNBP(r^F#Vn8YT zO~YZ8cwoW`BFOX^@l#^pj(JRCG02Lvog~b{rA#3=3T4#&emvWhJ@M^Z-q9okC<1C+ ziM(0ppz2r~oCrasBI<{KXe;KjU;c}kG~uV9NaZXN&;3f;GMa@bJ~}ZEzYR){zsg2w52xYuYt**Z zR9@$Hqy~EjM0Gh6&JvsEZbM+eRcF($U`OMliIUH9=Y$Q?1U{IjYHtTp&8J#B9a-*4%}#K)!zj|ijg)&e&wkn`h6cEQZu z-V}T-H`s#!jP}I5z_~7E;WaIPoQLOmx!dMD7+sS^-}AoVphP(@_k@yr zJ#$|mxSP|tm5>#-q-)nk)Z`|ngj#ET+>cQGEHr&Tm?+DyhDHbV0xCQm46@4U4CC6Q z8(&=pqlXBe&*6z?B_8nuv&(mf=Cw`z$?Hy8dVE@nFS*-}<0@nMOiEVC*m`z1s@FO` zQX(@JVQNE!Oo_4c@tvK27tp;x6otoWI@_w*S1}$butK7kOeq;|D$`Y(+1SZngOeIC;>U+QECBW`Iy4mVS zAB>bF;rKxGK3D#nfBbCiOOX?t8Ns^j_5J4itPmt7DU48tI1hI3;+Lce?4DKj1n*np zF(#$`hS!$qS?*a^E@Ruef&gr(VS0xsEF$#r6n*%Z!B8`pt9)sfGlPj~FZ>%Y8mDTu zr%Ki6`BN3fPOu}HEZL)U5jVYRbm^E^xFJWVP*>Xt+?NvUO?yzfIMV=R767ib*ha)5^x_w44oQQNP_?zN2@L^C1-RU z+9QBMdnLENPF=SEREuWG`jlOA9oh>9{4Rd<=E&A{iw27D{TleCc^%qog(7?#?<}0J zTTxJi?~j>z0R-ig4AJ)_vv3hRA`1+J*8~rKlPQXr*8kbXNxv5^2WC7KSsj!S1aqEF zmQKdG61B#A9s6Lf!J2>K1(UGzkLpkSPanktx8BW+Tr5ysjT$6tKvD(@`gy0gsC1!s z574RY<)#EoUw?onn2X3P76NABdju>@ADtmm$ow2li#Sj1b~j!}A50xYPc{Jz!7KvG z7Zp2q0SUKt3>-4G*L6bwcD#t^UC<{JeJ@((P|u`S89aW4cMp#HO?cLP|9vbycxS#e zgpv$$e&c_UiDgV0`SQjPOfhB9xCMp6w2NPP3+}cO9`-)&_p_b=_>FhQz3h1!VhozJ z1Qx;`TY{;|sbBDD6a>=D!91G!c58BsWV`U&^cyHxbAbD7_6Ah(6@b}u- zBiybw$;7;1J;R0Co87EM<$MmyqgyrG)?iX8qY)1n^}5_o*PH@DxzXFiI&kC~Q1Rcu zR5=02fHi<^fd^Yt6I<2mU#V@?GT3a$Io?1B4TBQZ$X>ll8!O!w_2&B6F$A61(X#y2 zvXk#J9s%q#Oe^*qg#C7^0PY#il8J>!h-Vb{3tvqNo$s_zKeL*s#92Sy2R+gz<`O5G z^;t2Uva$^43m|#wMS6mk6XoWrup@t*iOZcNelVTi?Wxz>hI*VDO@M%zG};vehc;pr zg@W;+@>V_z%>2Z|gq5Kmyd)h#))LNRHTj)O1HB*H$Qob}=y;L@AO_2)@;>Gkau zgKkf&vLqOb=N%HFRvCe%h3X{)0mepLzD4*%sY$3H7?}?{gfF=kXb_;bs$BnCGYGPAoMQNZ4`uB%;5NcKDGkkk} zd1#{^0*&!`QB4sYE9gdl+TtO`bjhmy&CP&-mp;I|U_kw>=*gC9ln4<#0xs>ZPag%@ z>Ct3Un6C#q5i)e0uuS!xjLFI5c{{z!J*z+U`{+d0-!uW(wxg?&M`sBIgK4RBJ_UZ~ zW3-@q9A2NZ8XotqavQdpQs$<7-Hl5C8alvKKZdktp%F+-3eSi;O)O81u3UIl0EBv5 zBf~)99<0XTt@fBU$_BtB7J~G$K7Q6uACFcC=z6W{03@5MN<_?*g6yl)pt95$-%1K$ z+VOUOLvOAxT0yIWP{Y;5_Cy%zV|tZ%$8+!>bd?`LBK2@vyl#mQyOZiwlHD`{7qx-6 z1A7A|X?DRFZK_(Ye|#XCXpQs`0!Y5{>heq*R1(cLg?7FO;rJGtlHK1=&nHvX7pS+$A%@0iEJkq9hy* zL5;U^xcuHMc19aa^d>#_-BEha_v|PNE{PEHi)HF7raDifz)N_&fif!{gfUOPCu52y z@b;mJ%IA$U3sfO`h`go9G8XAPzxyhs_C1^SePrv|f^i?_gUFy*-T>nH0Hy%q`#%Vf z@Ve*S_}pP+>E?K!6l*hb=hSz5My&^aQm<L13^LXh3#2|6Qz8;g1Jc)jv zFo%9U;!!o{v8z|ZMrn_aX{>xJJatb@Z-5GZ!1HPQQv6eWME^9U2d$&>!ecyFr_iG9 z^*S76&`kV3ar69HO2OA}_#RAlew;I4;=Sh=Q9RG^UWY*J{eb;T1qD!n+4GFkqTQCc zVG{(`P|I@Hbqt)ZV_a)33QdS)U%MG9YxYsR$cKR2>+u88Q&7qM&Upe7H?&?pOIfXv%`Ea(>M@1D8{|wxS)qp1U-)y)D@S*|P=>`+z5qI? z^b`n%%*Hr+%wt?qZu;I58uy^bVa9p*KO-qWx65QBGiG77_F?s-H;>{pw|fc{nFgg! zP;_?hh-scKClin|nOT}z?_}e&Zh=U)TW9AdML(M8XtSm|85CO{Ox~T`kHJNP@Y#G% z)@c$al~LQ5uyhW&if|P+CdGWU$lAl2RZMCzc4Qjo93^2ek>!5B0Jd)|N70_8Y3oDf z8kBh#ddMD(FmEY|%rIHr_O}(G!5&|k<9dSY$(sqrdoBGP@A+Yy4KU;68;TO02~|@5 z%qRA0t))+K59rYTfWk6cPJT%uBL-%yn#E$?`9>1GR6%6SLrdx_E+mD4na==5kodUn ze`daMDsbr6JBL30n>q8rgqRnj!uQWhF97qX2}KqEf6V-Nu&KHV3)4TP@)p3w0=gH0 z|3pw8bYxlCrGK+KI3#WS+r>4dYZXYX4mMSj$xHcnjA6{h^P6So!&nW%zEdtMsdLU| zr3#`H|GfQ02JaL%6?UbcqQ%Dl_DnmAZqK5kTjO+Kx2k)+KM`~%>ZqZdjD?47*sABI zo_4>(mw+WLO(TMld6Y?;k&{6@G&k6%hVNbQLxx6j;HO^cMR3Udp4!AOG5^jwj?z0N z;7k_p+u`*JAS5-kbjZ;xui)P&)W9@mn|wc1C;M{@ED12Xb?0s|7@fy~t7I8U%n=dK zzZ9psal8;2>G9eCK%Ya9%pC>VkF7Q|(fMw&h&2ze)G{e`Y=zSg@i($(SG0b75uVDCJUNUFW{nQWq-2SzZBC1n;uwF*GbH@p| z#T;M}1hALNJHf+jl`P==wNR_99k>Tx0*g`NmVok98noOInDZkPiy!zTH39ZrTRkm7 zO^5dU!;`MuG?rkIhxnbJEQK}0Yg5gcZ`sk`+4T-uk6sLz?%&U$HrS|{1P$_ z@U?Y6Vqe};9U#H*>L3y^BAYt5qe{<<(=o6B;WcaqtTEXD{Q~p3udk8*@22|26cx*t zXZFE`UtBVayF?sM@QE5ZuXV}~fDS(JSXGO&xVzW*9We*askdo^IcNg_qH<7=jAL`u zzJlqb3wb~u=&r=UwSGlXi=$~BWs#VER!pGQN&GsLr`iD(jM{as70{|A;BUQvtlPVD z^OOa-b~D|;9xVh2bQlyY)GB@lxx_Y>Oku|dBl;5n*BFBJPOga~hV1`=>`Hu4jpX^* zhMy8&kN6|sBSO5p-ub|`ex5Eg1r{_BLs}cQf3Pw38z9!#kiijc96`;mSZv%yaJC|R zMJnck(om+$^KoHW?g3rGlPu^~`8<_>XlM98_iVg4wFRv|XSV%go6MsV}9+{ZCpzGga$o{VdXGPS5 z7FP7B|KHQ_2fy=!Gy}QD->wyU)vN^*28-V#u|GxTZ&BhoC|moT!#1O0F9r|p!h zaE(s^LM3(q_?{*pg}oj92%_M7U?~)+x&d~MQjIo@c(}%ZKP<d(?^ZxOtA&(bkmJ@y=W&TfA_uGMj$A4q7~|I1yCBdxyY` zpJ!zn##t%_chkRoU*LJ}ieT6PRU2zru+lVeTzsT~oBc4V8DqRPmLJfUO;B}8$>dNGKe_Q4;JkC7jJg|)GVzzFyi+E7lf6iu(EdABclxLbzC8Cymv9+$ zS^q}R_13kVTX^RHvdNl^RHWaA=4Edx5gdJV6`ENziLq6CmbdDX==L&T6q7q$(?%~9 zc_#((GZ{*l%yyYdktyxEJLvf=c4&$Z^%CcWrCK~5oqd`8_y!{+;@xsq$X3*wK-r`l zhHmxSe7m;Gi} zPun>1jgutuB(P8ik^Ruc1)v;#9RoJ!k0)Jwg)S8NEOYR z&O4M^K+A{1sn{>c;`@ScSjUX`8BG53T z5nTbwPsBR+KugLA(aq(xV}Hx$Nbpc&eQXUGA3U6A$gtJlrPSWk!X$WQ{Rgb*!rNCt zJq<=ABQ=?whqX-G2$;=sXj|6jnBE){-LM2A9EwI@M6#T+-T<@qduTX{o5ME*HF*Va zLjtMp;;_mLB z>{4_4jT{%2sX+@KMF&wwd|7*IdnwP+{q%FJfnTx{G5*blO3cOYpcpa@(!1w6Bu?5a zT0`|)mq!YCZKfY|J8n(#!JwkbM@o_c!R^KPn#Bkc=Z~T?fST%Yf$9PbXbHQj3@Qip zaDry{0XP+H>q=KFQwQ$NdBTea6H%{j+R=UV(XR#34PU5^ZpRCAUF8rZDrB(-9ZM_R zI9~UQAxf6Tm6jiH`{(7=z5FWeCwU(rl99l!OE%HIU7%~<#n4n{Z^Kf_<$t@D#S=ey z=G}sXN;?1}uK=Q!Il+)oU6qu;3IY}AJKTf-7PLJ0PO^*YL6BMOPDZFI%f7N7{;q8% znuGwXH&Y^1jNg@!R-&B>trJ0fq-KznQ>0hN|7czD4@B~&xkf07xo-MBRK;T5VVdgMo1yS|yFzvweMXycdd zM)X#0dgr4a#hCtS+tvevl<{txn-)y%vtmln_7`p;hkgjP`C=?#-j5&nI?jwht5(e4 ziAa)sgl3LU9Y%Z*gD7~fL8ryO!Fxk4qx7y2UQMj@(wyi(6p~sMO_@t}STd74c$nc! z(E)ALbFkRd8;7aRLho*f15g}0F{yie+@LwwgwEj20CZgb#@0*^kFa(9?)I#(BBZ#8mok`LM+cPr?{C~2_ zIYCrx$UOdq!N0+)nK7!&R=*nU_Oc_Qs9ER{S>3fpxq)uKEW>vRMdhTZJQk^(QQH8h zjZ==`hMNKB4>EE^2|7Q3fYo0xW4U11NKY$^Ue1{Pgr<=wA~e3EEO;KM?}b6f;^7dDP%;^8ka9n6$jVJPBeLkG66tl zm8a24ibDjDUyQxcZ&!ad^$8y7e0fonRtGX>AA3($(rqxP+>1eEsCBtN-3COdc&s_= z!6AL#L5tHWEyuXs=KP6$oTZcf2&p{&4mbwnT(dQ5v>AfVD(ShWG_uNHuoPLwg?fT8 zhXHo{k2%N*>jq&%1AX^he=tB!T7zX$oKyi)Lj_uoTYhIwL%IQx4Q7-hx)-N6ZqR># zz6;>CVgXpIT#4G9|A|XE`d?ZI-W1RJBEa*n)IW9Z0WsP!csA+@SQLlz1r*d)V1ZR7 zFl5{Av+Izchu{R^P7_cP&DB&|P29l;a_%LZx%)1a5TaI-5h<-1nH;%W0J{ltg+t&< zcD()%mAivp>h##=d^C|xwnIfmT`@;6hKUoA4z9l6(lI*LRqrA2AOpFLF&Ik$^j6Gi$E50^jAuVk`NZuPyVF7MD$24(GkwwINbq%kZzLC};2#zssNIQ3C2G|H2sZ^9Yffg{}fDHR||JhzopXUoY@V zR06CKnZ?~rf{cdPQ zQnN#%i3qkH2|b`$iW%cP+cXx=WqTX-j#j3gX>?;hYJoG&S$ZZNWJ{OD3*77LSion> zw4v5u!cA}!;vKzEj;P9mDg+SDB#3#m+?Psj4U-&%(<9fw(&{ItZJlWDz}D>XcxTO= zE6gC10cID5s+n|}antu(cKr8mO`qy!H;=OMJ+{3a`#5Lc(-%@FPc zelA#A>PPNpF0NQUdNW$XU9PdE3yt;~P7h14oG6t`paay3$os`VJwDM3TYOZ*Q8t&K z@Nd+CQr+>t28QdeT_JDC6&k(y+xFUqfwI}Mf6OfOuUn0fE41;>{5$mAD?u^xY{&A7 zqU+Wx(7Nr^_(TVKB$=TwxC;OH_>1cnCupGwydwJw9WV}xbU&o9+WZ2w?va3&Hw7oo zwaq09*<5iEm51uk^~DMUtw!9$fNPs88?w1%rL@ofeX#>%fxW_zZ@IR)ptw6r_NPLR z>lPYhffe8SJa>IU{|}#@_lMg&deGT{3JFZ4tg8E9WMkdeqWV8&ySvb>zt08uu|ePm zbHpMWwulY-rTzhCxHx)#&^tFJC@FB1rs)iB6!Pu*uOZbh%4myKAzu!p9>|;)7b1nH z^C(nIfcR`hhtpxI81lSKZa~kkupG0u_!=NrqLbnN07g_4O(%8A0+z}Q;6v-z z<2MK?35w+J;)6#(Q&t@WLa(#n1sjmv)5FnF4nmw0XWI=n;PoJ@-###7S!$-Vdzp)9 zL_}HEQ`P<#&;De^8qWJGb=7qL-O1O>!{6c5`+WBNLQVV~P;O;l(wKnEnd$|H-=Cjv zeqV?vn{t|XPMnlPi9Wg6M^1lh5tLc5<6hV9^os9D!O8e1!wh zfx~qN!`dH($U!XB1X?~nXGi-Ej1pyFP3zQfkv`QLF4>b#M#Z!T!)_-IeS95I^jYZ= ztk1{o>7eqeg{pQv*FOxLJNx`|zL(%3SY-1C zBda#3_%=X=iDP6rRb4@IpUQp5`vORT5->eZkea3#fNyHxsiYBu{RYMQJ#>TFMsLxS z+%Omo2?#s-*olTZgY?YoX#GM3sJ@vFNohXG18C9w1=weEV$Pr*qd_0bQKS;sX}slo z#Or=+J5lRWmg+QaF8Y(CziK7T0+15ig_Nfj36MVo1cM1QFRLmFJ`JK-cmUQs!*XbK zO7oeZEU&oxc}vyEV$G`9Vsoo&x6=K{kWFRfeOFgvkYuO0S(^cs!gn)(XVIkn;fZ-n z9Vn&@DAIbspy(@IVhB)+6PQxTK6)T%{R}V9PnrZsAHSAQ>=5JJ(S!*=`37{<|#wnG7RZ z8}gl{h+i8H#~O&SRzODUw95*Gc+^hfvw<2rpR3dLq88U?Q=nsT_Z5KDbWA*Q`8yyb zUI(ISF7cxR>xo}vjW4GnthFoNs2{HyG#O9hEf{M%raW>Bpe61BHV2N8;Ox6w&Cj$t zT#yZjiAP<4 zufHe}zTAyG1kx(!xA9980H=C)vgfgrSLNPo7_mG;`#-w7@^7gBe;-)}$;==k+t?xz zmB<={QL>b!K4h#>QAjnGNCsoe64A$!q_U+*k}YZMTOL@JoCBdCF+UjFzEb0+}PG#!P437cBK(>(n^3RCFd9 z(yc+2e#^y!c^Y%w&)If4?zg;N2v+W~)s>mkymG@~NDxNgWYAL2Leic<+xXzQ`A!`j zlFBvUr|zPe>K3YJu6Lr9?*y;BzZb655v^1sSf!{Z2$du{`h6jh!w^o^qP|D6Yz9qId>%mgAQKTI`tD#>s%CH zug!VUsL8vWd=N)EdJA!zraGBDj^ZKRZPjcz*0RmpnfW(_cDOg%C_9Nqi)xo;Wy?AQ z9%idv*Yp<%(UZQBQi{a3xenkt-;6nRu~J^;B5PMaQF7D_p8$#cWjWJ(n1AdDn`k6A z4D{4}bB(g#6G!lRW5hNBbec$Sf~oxwcXv+K`^=OjUy{(*$`!y5101sHXs&}}(yzA6 zU7x7@IV4Pj%A`+vT6orjKMN!IEW7!4$~Ie>$XxNBE}r~S@bd0tM&!w&T+x6Z#Uobh zvk^MmnDq(hE}+1!CB?ocKUZ< z;;SzkkrMNS>Y-&P94Hp)hR>td3m1pH>9h&$|V3SxA z(78r)U9dmMQlfa`+oRj7RaP=M+D|p&Es|tSyZ`Ho`Drul-!ABbWLAr@s9W#SK}f0~ zgpY7FB#VpgIJ7761DVcBaLFq*UWM#elCG;}+EbN&ikOb<8kFz61o2e;!K6R&mVhj= zQ)AlS2k(2`uhl}r#8UB-n!3&I2TGXY{4*(52{@*kyodF|ghlv8%j(lo9@=G3(`n;6 zm*ZFr{pDlDH&E84>WvYP(LFoe@};{d<&vyB$>gYr``*l55~#*I(G{DeXSvkQrWzh5la*;Lzzsetj7rb z3>XoW!zWyk+8^&qmd48cR5Z#(!UwZX9)mM<&@v*VP)@X*S}5##Bhh}x*mmWOW{b{xLEuhHxQ{)y8_tY+y0Z>430qo z5QZy9X~ka#`5tIg*U!SABQPQ!0S*cw)`yS{arko3s30->SEB+1kSzV~`zHH?7rzce z7w+bTGxl)@ut^EaKc5-<%zq3xpXHSb$7rx7#Bj#y(C1c4)!6 zUy)=?KpTdxv{~vi0Om26`Dx}S7a0@imIWd1-re(zeYhY|*<)EBV*hs!^5FZY*~nCr zk2rueEx9YEA03h{0|@8)+k%W%H|8tfdz!zFp7Q|8hlTHFYxLW!BaHb0JCkq}Z-7M7 z;WLM1fR(m~vi&{Lz(C4l`2T|SCNAcK04i?_Z0!4--7i|8UgVOxHfY18S>)?%jWR01s{U7x$ zHgM(#HbcJ9TviTmH#dYI>kBH*OtJM%dwT3^_0{SA-g0keShyu4bXG>b{2pc+Vw>|( z`VI(i`w-t|Z(fGv4K;e5T@r|4J63-ToC=}PkA4I@_yxp^k5EwvmiqPm!*j$)FCiA+T(8KG))b2iQf5fkk4%^r;xOThEfuiUuH8Jt9A2vh_l^8}csGBBj|fxv$b$!#Gi&JPoorYu-2_}nB6QG7F` z<5h?YeP1mCA+J%Rr%}n81>{E4JqF@?nJq3w(%j3H&M0vz9|q!e8nSTDzP*3k*bril zsG~pyp0E5-attk3(1%Nl95LA@j%4?U>)Gt30@t?f^Cct?O!M+MTgu7tRmnV_PVzOqL9uVE7 z#Gd2|(-)V;(U_B)Y@;}_dIqQ4Px5!@BpLpz!=mQB|06l&ORM%_VrR2XP+E%IOf~qv z-dv~)J?cWaDJ)S7XL|tFAsEdfAk4bixP|zrlZc1!W_Z>%aQBgj2+jgX+)jd(Z~;U- zMkQM*XdH!bvA?t#;<_jeq|^@5x!Zo3YW?aFH-XDmg(x~G`O!v*s|MWmS3)DQnhSIN zhaD3*+I?*S`3*a9yMrc}CUqvN?ALZ|&%`~J;sIU??>TQhFdwhmYrxqR>2FTC&6P6$=CWQPTBhuz5sv1&pve3ULy*B2 zcJ`=^egqPhe;sumS_&XDeJ;hj>qmOaEMyU<6Z#L zNY%Btw}_GL{{zK$`n%DWVqI5gfqG&kb&~N`2|kzPqMCW7v!}8pwEw)yZEUSBbpJGW z2fCJs{XqR?mZI*5K(tu)v;9!G;+cxRxS$0|u({nqnh|OIv7VKq;leS3fjs~;oT7K^ z<+KSy&V}#Ee_B)Q{qsb;YMC~vZ=j0ftzjOCxR6i6%J`j7W zGF7E5@B*;c3yAzg%&?B)d+FDc`FDgAQuvrdXUqK(@wl#R6U(IC(XwHwudXHMpYrbB z0G2}UB@(28n~JQ8W{uhNG~>mxl9}JM-|3nrQQ=G7ImW3hGClOiI_t*7?71!HZNpg5 zvI4pIum=NnNoe`Dsg)J{uj?Y2=)4K3edzpXq5Epstn5rK0q2W7v`l$D9}-cpu3qya z0QdIRl8)F}r=Hnv!n4_clGN8u9h{C%!k4Y3&d+K&51p^Q!PKCxi{pQIO0ac!UBJb$ zsu?erTy&EEk-IIK|1;3YeE9R=C~V*w&yQ~Ih1N%5sj8@iE2zjc>tu}?`_;?B$tq}*wsO-m*wuE4&bHf5&tz3FpNEq}6WO;5U- zeIUL5RHX^u?LX18H9)sY=*#zxl(A|yoitUKE^SQ_aSU$+AB3I`Sg5a-e}Cw?n4lcD zbH7YK`3_gSt|hreSev#SylB%i?qn-3`XTL7%}&i?BrmeYGgo~?yapVbwqWFKrC9nZ zkcZI%->czQFdF>thN~Xs@Gb85mBEKyU$ck;=WQEJb~&QCOKyZY|bxAGD3(H!0B# z$MQA{677FLEhi_@T(byln&E8^L$t|2!;*8c5LKUe@pbbVUQ5Jp39?5gjGmg{>^3L1YIka%b>00Akip!hu1|0Orj$;<# zSLhR0#y`5P?DQV{1xintf@J?I(B?yzxl24wa}b$JqGwysPv-DZ?_`O(F=$pt-5Q6S`I0t)cqJNA?^fewmwY0#a#fv)^cit+ z#)uWa0$M55#vqN1=C~qW^r!wy!!c7$MM{!>mP0$g^3vlb2y3cz$zAGH$s3=>sC?as z=aSSEr8azxM!=_e%*JVZ3(4_Y{(-^ImN#@-E^qK}to~#&`05d9ITA*H9gXtq>s%FQ z&CF%W6cPQ`$SAOKhzlY9ITgt1UiIo0mDkAE#_IU_C0oo@g~|#PN=aD3+m#T%JAFz< z=GLgP_n6VXN5|84utfc`Tc_6%(rcF!{Z+Ad@f5x*t@~>ng2KMeTcS{{9s%%n484Kv z(6w~ub=G(a0Xp(ZkP_S-_=HpqL-#mdmeE8oMX6K2V$|AJ9d{?Oi*L#(&P0Xvbc`>5pmxYh&Aai9ReLsDC0kV z%|Sm#Dq!DejQhEvMD8EP$ES&MEO}oGvdl%b(^;y%7~T`?t^?2tT4I!+HI8w}OA z6<>Dlh7L}#%1mVV2KZrl- zTk|^VChjJ5Q_jv>gy*oAV0Ma)pG{0{>~_eRE{Ro_vnO338;!KC%Tcm7Q@_phPzJcNfh=TCH1jy-U8_`}&L_i54nQ_DXxc18_+qUiyKbkFA^Gtn#* zl5_AZ=iP$tFW#CNYs)4(a^dT;0-60JPuS6%=^T7C9-^&5mKUt6WRg-sM0zhl)5?a2L9;AR^*(DLtQjr$CnX?TNy%TJmFjLCW69?UxF#kExvPi zU<|~{t@ALT)|b5i8HPlKxEZ>nF7DyRrZkNXsRHk2%Ygoa5XRgHISV?QJHf@j1a#sT z;C^7=CMac!O!~vlnB&|XfM%C?V%Zo$3OJaJzc~8;>R>|t_0v#6jwR~^N9l!+9ou!* zyg=UV)T;yx_@rm?Ln_J=>2R_#w@SmsyTOXWUeJjqbKeK+-)RFD586~hS2;cl%Tcat zkY-Y&dq)xk1mvb1X2F9Zznd1J58%*f8WjwHNG_7k8g1u->aN zk?M>@t;!$@oTh!f;yAeSJ*N6!6d=0)*|tK8Q;DXnfCoPzVU2>}ym zVPL7*UI88^zZ3PB6<)~V;^VhOVlx#mduszNvwwA|XjBe@`%#>szPP{(zZl-7LSy9O zs#e9yJ_<(C1K9toBO&bJ0s}~hxJL(k3Z5WzAjuyMcqt-D>-$f@aS`FCS{Ge1CM|B3 zPl|i8Xu{&ef&ews$u|90?yi#OvccR?<~M&9F*VKxqO*|>BTx?Cf-sKr<{ zB##}wT?A~4q$A`>bUv&(*_k8#McQMxuC5A1@z>)3Q8 zK+-LaII4h>x&1q>J;6)A@~V_Tn;~2jJap0S?idO?c2gHwU&CQw>`6xv|2*N$2Gls| zX-(X*OF~?^EGQmMed-`uXW||qr&b^FMR~wt)zB#%-y+99T#1#9nn^t$hZjeyO%*BGA8P(2~rbZlfNm2)CRIh>PtKr!kl>!3%m{wW;bF7Sc=28-hi>7rS*R_bB z(q10f`nFOX>FteD%jCr!aBble)(HyCt=omgsk=0x;gn_|nx4yY!ueY%3rOwvkLu9v zhj)|gr3^+Sq)UKd?*~VqkP34ik^_WTah?|ZU?Zb0oJm0EmQ5dZ514&PxJsx5l1}f2 z`oH-lQJT5*d%nT9+2K1!zCFBDp{zCnmx|4&#s_*c8QG!yn50Mk-vT^tcuRPgTZ=I!uoIy!$;Xo!`zS#Q p@VHyP|K$oLVd($IgB;n|I$OVW>)C(mCz1a=V{BlGFVb_k`5*MqI?Vt8 literal 0 HcmV?d00001 diff --git a/docs/getting-started/app-custom-theme.md b/docs/getting-started/app-custom-theme.md index 146f160d71..3015c18011 100644 --- a/docs/getting-started/app-custom-theme.md +++ b/docs/getting-started/app-custom-theme.md @@ -393,6 +393,98 @@ You can add more icons, if the [default icons](https://github.com/backstage/back Note: If the icon is not available as one of the default icons or one you've added then it will fall back to Material UI's `LanguageIcon` +## Custom Sidebar + +As you've seen there are many ways that you can customize your Backstage app. The following section will show you how you can customize the sidebar. + +### Sidebar Submenu + +For this example we'll show you how you can expand the sidebar with a submenu: + +1. Open the `Root.tsx` file located in `packages/app/src/components/Root` as this is where the sidebar code lives +2. Then we want to add the following imports for the icons: + + ```ts + import ApiIcon from '@material-ui/icons/Extension'; + import ComponentIcon from '@material-ui/icons/Memory'; + import DomainIcon from '@material-ui/icons/Apartment'; + import ResourceIcon from '@material-ui/icons/Work'; + import SystemIcon from '@material-ui/icons/Category'; + import UserIcon from '@material-ui/icons/Person'; + ``` + +3. Then update the `@backstage/core-components` import like this: + + ```diff + import { + Sidebar, + sidebarConfig, + SidebarDivider, + SidebarGroup, + SidebarItem, + SidebarPage, + SidebarScrollWrapper, + SidebarSpace, + useSidebarOpenState, + Link, + + GroupIcon, + + SidebarSubmenu, + + SidebarSubmenuItem, + } from '@backstage/core-components'; + ``` + +4. Finally replace `` with this: + + ```ts + + + + + + + + + + + + + + ``` + +When you startup your Backstage app and hover over the Home option on the sidebar you'll now see a nice submenu appear with links to the various Kinds in your Catalog. It would look like this: + +![Sidebar submenu example](./../assets/getting-started/sidebar-submenu-example.png) + +You can see more ways to use this in the [Storybook Sidebar examples](https://backstage.io/storybook/?path=/story/layout-sidebar--sample-scalable-sidebar) + ## Custom Homepage In addition to a custom theme, a custom logo, you can also customize the diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 0acaba7470..84882fa23f 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -74,47 +74,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( }> {/* Global nav, not org-specific */} - - - - - - - - - - - - - + From 6db37424c5512aa57d3333f87af98bce9dba7773 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Fri, 18 Nov 2022 12:43:34 -0600 Subject: [PATCH 39/68] Changes to make Vale happy, I hope Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- docs/getting-started/app-custom-theme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/getting-started/app-custom-theme.md b/docs/getting-started/app-custom-theme.md index 3015c18011..5ec0d6eeab 100644 --- a/docs/getting-started/app-custom-theme.md +++ b/docs/getting-started/app-custom-theme.md @@ -397,9 +397,9 @@ Note: If the icon is not available as one of the default icons or one you've add As you've seen there are many ways that you can customize your Backstage app. The following section will show you how you can customize the sidebar. -### Sidebar Submenu +### Sidebar Sub-menu -For this example we'll show you how you can expand the sidebar with a submenu: +For this example we'll show you how you can expand the sidebar with a sub-menu: 1. Open the `Root.tsx` file located in `packages/app/src/components/Root` as this is where the sidebar code lives 2. Then we want to add the following imports for the icons: @@ -479,9 +479,9 @@ For this example we'll show you how you can expand the sidebar with a submenu: ``` -When you startup your Backstage app and hover over the Home option on the sidebar you'll now see a nice submenu appear with links to the various Kinds in your Catalog. It would look like this: +When you startup your Backstage app and hover over the Home option on the sidebar you'll now see a nice sub-menu appear with links to the various Kinds in your Catalog. It would look like this: -![Sidebar submenu example](./../assets/getting-started/sidebar-submenu-example.png) +![Sidebar sub-menu example](./../assets/getting-started/sidebar-submenu-example.png) You can see more ways to use this in the [Storybook Sidebar examples](https://backstage.io/storybook/?path=/story/layout-sidebar--sample-scalable-sidebar) From 493fc5b80d21d0c95cd5359fe057f4552e3baeb1 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Fri, 18 Nov 2022 13:09:49 -0600 Subject: [PATCH 40/68] Removed imports Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- .../packages/app/src/components/Root/Root.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 84882fa23f..6768b48dad 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -23,18 +23,9 @@ import { SidebarSpace, useSidebarOpenState, Link, - GroupIcon, - SidebarSubmenu, - SidebarSubmenuItem, } from '@backstage/core-components'; import MenuIcon from '@material-ui/icons/Menu'; import SearchIcon from '@material-ui/icons/Search'; -import ApiIcon from '@material-ui/icons/Extension'; -import ComponentIcon from '@material-ui/icons/Memory'; -import DomainIcon from '@material-ui/icons/Apartment'; -import ResourceIcon from '@material-ui/icons/Work'; -import SystemIcon from '@material-ui/icons/Category'; -import UserIcon from '@material-ui/icons/Person'; const useSidebarLogoStyles = makeStyles({ root: { From 4c9a407e16b23bc7a8fe5924f37fc0879c34728d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 18 Nov 2022 23:12:22 +0100 Subject: [PATCH 41/68] workflows: add workflow for posting release information on PR merge Signed-off-by: Patrik Oldsberg --- .github/workflows/automate_merge_message.yml | 63 ++++++++++ scripts/generate-merge-message.js | 115 +++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 .github/workflows/automate_merge_message.yml create mode 100755 scripts/generate-merge-message.js diff --git a/.github/workflows/automate_merge_message.yml b/.github/workflows/automate_merge_message.yml new file mode 100644 index 0000000000..9d8deffd8a --- /dev/null +++ b/.github/workflows/automate_merge_message.yml @@ -0,0 +1,63 @@ +name: Automate merge message +on: + pull_request: + branches: ['master'] + types: ['closed'] + +permissions: + pull-requests: write + actions: none + checks: none + contents: none + deployments: none + issues: none + packages: none + pages: none + repository-projects: none + security-events: none + statuses: none + +jobs: + message: + # prevent running towards forks, and only run on merged PRs + if: github.repository == 'backstage/backstage' && github.event.pull_request.merged == true + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: '${{ github.event.pull_request.merge_commit_sha }}' + + - name: fetch base + run: git fetch --depth 1 origin ${{ github.event.pull_request.base.sha }} + + # We avoid using the in-source script since this workflow has elevated permissions that we don't want to expose + - name: Generate Message + id: generate-message + run: | + rm -f generate.js + wget -O generate.js https://raw.githubusercontent.com/backstage/backstage/master/scripts/generate-merge-message.js 1>&2 + node generate.js FETCH_HEAD > message.txt + + - name: Post Message + uses: actions/github-script@v6 + env: + ISSUE_NUMBER: ${{ github.event.pull_request.number }} + with: + script: | + const owner = "backstage"; + const repo = "backstage"; + const body = require('fs').readFileSync('message.txt', 'utf8').trim(); + const issue_number = Number(process.env.ISSUE_NUMBER); + + if (!body) { + console.log(`skipping comment for #${issue_number}`); + return; + } + + console.log(`creating comment for #${issue_number}`); + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body, + }); diff --git a/scripts/generate-merge-message.js b/scripts/generate-merge-message.js new file mode 100755 index 0000000000..9b6fb5cbc6 --- /dev/null +++ b/scripts/generate-merge-message.js @@ -0,0 +1,115 @@ +#!/usr/bin/env node +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { execFile: execFileCb } = require('child_process'); +const { promisify } = require('util'); +const { resolve: resolvePath } = require('path'); + +const execFile = promisify(execFileCb); + +async function hasNewChangesets(ref) { + if (!ref) { + throw new Error('ref is required'); + } + + const { stdout } = await execFile('git', [ + 'diff', + '--compact-summary', + ref, + '.changeset/*.md', + ]); + return stdout.includes('(new)'); +} + +function getReleaseOfMonth(year, month) { + const base = new Date(Date.UTC(year, month)); + const wednesdayOffset = + base.getUTCDay() > 3 ? 10 - base.getUTCDay() : 3 - base.getUTCDay(); + const thirdWednesdayOffset = wednesdayOffset + 7 * 2; + const releaseOffset = thirdWednesdayOffset - 1; + const releaseDay = new Date( + Date.UTC(base.getUTCFullYear(), base.getUTCMonth(), releaseOffset + 1), + ); + return releaseDay; +} + +function getReleaseSchedule() { + const firstReleaseYear = 2022; + const firstReleaseMonth = 2; + + return Array(100) + .fill(0) + .map((_, i) => { + const date = getReleaseOfMonth(firstReleaseYear, firstReleaseMonth + i); + return { version: `1.${i}.0`, date }; + }); +} + +function getCurrentRelease() { + const { version: releaseVersion } = require(resolvePath('package.json')); + + const match = releaseVersion.match(/^(\d+\.\d+\.\d+)/); + if (!match) { + throw new Error(`Failed to parse release version, '${releaseVersion}'`); + } + const [versionStr] = match; + if (versionStr === releaseVersion) { + return releaseVersion; + } + const [major, minor] = versionStr.split('.').map(Number); + return `${major}.${minor - 1}.0`; +} + +function findNextRelease(currentRelease, releaseSchedule) { + const currentIndex = releaseSchedule.findIndex( + r => r.version === currentRelease, + ); + if (currentIndex === -1) { + throw new Error( + `Failed to find current release '${currentRelease}' in release schedule`, + ); + } + + return releaseSchedule[currentIndex + 1]; +} + +async function main() { + const [diffRef = 'origin/master'] = process.argv.slice(2); + const needsMessage = await hasNewChangesets(diffRef); + if (!needsMessage) { + return; + } + + const currentRelease = getCurrentRelease(); + const releaseSchedule = getReleaseSchedule(); + const nextRelease = findNextRelease(currentRelease, releaseSchedule); + + const scheduledDate = nextRelease.date + .toUTCString() + .replace(/\s*\d+:\d+:\d+.*/, ''); + process.stdout.write( + [ + 'Thank you for contributing to Backstage! The changes in this pull request will be part', + `of the \`${nextRelease.version}\` release, scheduled for ${scheduledDate}.`, + ].join(' '), + ); +} + +main().catch(error => { + console.error(error.stack); + process.exit(1); +}); From cf41eedf435032146b46f9f95db5b584a225d0cd Mon Sep 17 00:00:00 2001 From: Patrick Jungermann Date: Fri, 18 Nov 2022 04:22:25 +0100 Subject: [PATCH 42/68] chore(events): apply post-merge comments Introduces a new interface `RequestDetails` to abstract `Request` providing access to request body and headers. **BREAKING:** Replace `request: Request` with `request: RequestDetails` at `RequestValidator`. **BREAKING:** Remove required field `router` at `HttpPostIngressEventPublisher.fromConfig` and replace it with `bind(router: Router)`. Additionally, the path prefix `/http` will be added inside `HttpPostIngressEventPublisher`. Relates-to: PR #13931 Signed-off-by: Patrick Jungermann --- .changeset/fluffy-walls-approve.md | 21 +++++++++++ .changeset/nine-ears-whisper.md | 9 +++++ packages/backend/src/plugins/events.ts | 4 +- plugins/events-backend/README.md | 2 +- plugins/events-backend/api-report.md | 3 +- .../src/service/EventsPlugin.ts | 26 +++++-------- .../HttpPostIngressEventPublisher.test.ts | 37 +++++++------------ .../http/HttpPostIngressEventPublisher.ts | 22 +++++++---- plugins/events-node/api-report.md | 9 ++++- plugins/events-node/package.json | 4 +- .../src/api/http/validation/RequestDetails.ts | 29 +++++++++++++++ .../api/http/validation/RequestValidator.ts | 4 +- .../src/api/http/validation/index.ts | 1 + yarn.lock | 2 - 14 files changed, 112 insertions(+), 61 deletions(-) create mode 100644 .changeset/fluffy-walls-approve.md create mode 100644 .changeset/nine-ears-whisper.md create mode 100644 plugins/events-node/src/api/http/validation/RequestDetails.ts diff --git a/.changeset/fluffy-walls-approve.md b/.changeset/fluffy-walls-approve.md new file mode 100644 index 0000000000..968f5cc402 --- /dev/null +++ b/.changeset/fluffy-walls-approve.md @@ -0,0 +1,21 @@ +--- +'@backstage/plugin-events-backend': minor +--- + +**BREAKING:** Remove required field `router` at `HttpPostIngressEventPublisher.fromConfig` +and replace it with `bind(router: Router)`. +Additionally, the path prefix `/http` will be added inside `HttpPostIngressEventPublisher`. + +```diff +// at packages/backend/src/plugins/events.ts + const eventsRouter = Router(); +- const httpRouter = Router(); +- eventsRouter.use('/http', httpRouter); + + const http = HttpPostIngressEventPublisher.fromConfig({ + config: env.config, + logger: env.logger, +- router: httpRouter, + }); ++ http.bind(eventsRouter); +``` diff --git a/.changeset/nine-ears-whisper.md b/.changeset/nine-ears-whisper.md new file mode 100644 index 0000000000..a1427158af --- /dev/null +++ b/.changeset/nine-ears-whisper.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-events-backend': patch +'@backstage/plugin-events-node': minor +--- + +Introduce a new interface `RequestDetails` to abstract `Request` +providing access to request body and headers. + +**BREAKING:** Replace `request: Request` with `request: RequestDetails` at `RequestValidator`. diff --git a/packages/backend/src/plugins/events.ts b/packages/backend/src/plugins/events.ts index 2ada92a5b6..7427d34217 100644 --- a/packages/backend/src/plugins/events.ts +++ b/packages/backend/src/plugins/events.ts @@ -27,14 +27,12 @@ export default async function createPlugin( subscribers: EventSubscriber[], ): Promise { const eventsRouter = Router(); - const httpRouter = Router(); - eventsRouter.use('/http', httpRouter); const http = HttpPostIngressEventPublisher.fromConfig({ config: env.config, logger: env.logger, - router: httpRouter, }); + http.bind(eventsRouter); await new EventsBackend(env.logger) .addPublishers(http) diff --git a/plugins/events-backend/README.md b/plugins/events-backend/README.md index f71df21517..cef8ac7330 100644 --- a/plugins/events-backend/README.md +++ b/plugins/events-backend/README.md @@ -170,8 +170,8 @@ const http = HttpPostIngressEventPublisher.fromConfig({ }, }, logger: env.logger, - router: httpRouter, }); +http.bind(router); await new EventsBackend(env.logger) .addPublishers(http) diff --git a/plugins/events-backend/api-report.md b/plugins/events-backend/api-report.md index 4154168fa1..69d7600ed3 100644 --- a/plugins/events-backend/api-report.md +++ b/plugins/events-backend/api-report.md @@ -33,6 +33,8 @@ export const eventsPlugin: (options?: undefined) => BackendFeature; // @public export class HttpPostIngressEventPublisher implements EventPublisher { + // (undocumented) + bind(router: express.Router): void; // (undocumented) static fromConfig(env: { config: Config; @@ -40,7 +42,6 @@ export class HttpPostIngressEventPublisher implements EventPublisher { [topic: string]: Omit; }; logger: Logger; - router: express.Router; }): HttpPostIngressEventPublisher; // (undocumented) setEventBroker(eventBroker: EventBroker): Promise; diff --git a/plugins/events-backend/src/service/EventsPlugin.ts b/plugins/events-backend/src/service/EventsPlugin.ts index d5a38a7fd7..65e7770d87 100644 --- a/plugins/events-backend/src/service/EventsPlugin.ts +++ b/plugins/events-backend/src/service/EventsPlugin.ts @@ -90,14 +90,11 @@ export const eventsPlugin = createBackendPlugin({ env.registerInit({ deps: { config: configServiceRef, - httpRouter: httpRouterServiceRef, logger: loggerServiceRef, + router: httpRouterServiceRef, }, - async init({ config, httpRouter, logger }) { + async init({ config, logger, router }) { const winstonLogger = loggerToWinstonLogger(logger); - const eventsRouter = Router(); - const router = Router(); - eventsRouter.use('/http', router); const ingresses = Object.fromEntries( extensionPoint.httpPostIngresses.map(ingress => [ @@ -108,23 +105,20 @@ export const eventsPlugin = createBackendPlugin({ const http = HttpPostIngressEventPublisher.fromConfig({ config, - logger: winstonLogger, - router, ingresses, + logger: winstonLogger, }); + const eventsRouter = Router(); + http.bind(eventsRouter); + router.use(eventsRouter); - if (!extensionPoint.eventBroker) { - extensionPoint.setEventBroker(new InMemoryEventBroker(winstonLogger)); - } + const eventBroker = + extensionPoint.eventBroker ?? new InMemoryEventBroker(winstonLogger); - extensionPoint.eventBroker!.subscribe(extensionPoint.subscribers); + eventBroker.subscribe(extensionPoint.subscribers); [extensionPoint.publishers, http] .flat() - .forEach(publisher => - publisher.setEventBroker(extensionPoint.eventBroker!), - ); - - httpRouter.use(eventsRouter); + .forEach(publisher => publisher.setEventBroker(eventBroker)); }, }); }, diff --git a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts index 3d93e347b3..28f3ddcac4 100644 --- a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts +++ b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { errorHandler, getVoidLogger } from '@backstage/backend-common'; +import { getVoidLogger } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { TestEventBroker } from '@backstage/plugin-events-backend-test-utils'; import express from 'express'; @@ -35,37 +35,35 @@ describe('HttpPostIngressEventPublisher', () => { }); const router = Router(); - router.use(express.json()); - router.use(errorHandler()); const app = express().use(router); const publisher = HttpPostIngressEventPublisher.fromConfig({ config, - logger, - router, ingresses: { testB: {}, }, + logger, }); + publisher.bind(router); const eventBroker = new TestEventBroker(); await publisher.setEventBroker(eventBroker); const notFoundResponse = await request(app) - .post('/unknown') + .post('/http/unknown') .timeout(100) .send({ test: 'data' }); expect(notFoundResponse.status).toBe(404); const response1 = await request(app) - .post('/testA') + .post('/http/testA') .set('X-Custom-Header', 'test-value') .timeout(100) .send({ testA: 'data' }); expect(response1.status).toBe(202); const response2 = await request(app) - .post('/testB') + .post('/http/testB') .set('X-Custom-Header', 'test-value') .timeout(100) .send({ testB: 'data' }); @@ -100,14 +98,10 @@ describe('HttpPostIngressEventPublisher', () => { }); const router = Router(); - router.use(express.json()); - router.use(errorHandler()); const app = express().use(router); const publisher = HttpPostIngressEventPublisher.fromConfig({ config, - logger, - router, ingresses: { testB: { validator: async (req, context) => { @@ -148,26 +142,28 @@ describe('HttpPostIngressEventPublisher', () => { }, }, }, + logger, }); + publisher.bind(router); const eventBroker = new TestEventBroker(); await publisher.setEventBroker(eventBroker); const response1 = await request(app) - .post('/testA') + .post('/http/testA') .timeout(100) .send({ test: 'data' }); expect(response1.status).toBe(202); const response2 = await request(app) - .post('/testB') + .post('/http/testB') .timeout(100) .send({ test: 'data' }); expect(response2.status).toBe(400); expect(response2.body).toEqual({ message: 'wrong signature' }); const response3 = await request(app) - .post('/testB') + .post('/http/testB') .set('X-Test-Signature', 'wrong') .timeout(100) .send({ test: 'data' }); @@ -175,21 +171,21 @@ describe('HttpPostIngressEventPublisher', () => { expect(response3.body).toEqual({ message: 'wrong signature' }); const response4 = await request(app) - .post('/testB') + .post('/http/testB') .set('X-Test-Signature', 'testB-signature') .timeout(100) .send({ test: 'data' }); expect(response4.status).toBe(202); const response5 = await request(app) - .post('/testC') + .post('/http/testC') .timeout(100) .send({ test: 'data' }); expect(response5.status).toBe(404); expect(response5.body).toEqual({}); const response6 = await request(app) - .post('/testD') + .post('/http/testD') .timeout(100) .send({ test: 'data' }); expect(response6.status).toBe(403); @@ -210,15 +206,10 @@ describe('HttpPostIngressEventPublisher', () => { it('without configuration', async () => { const config = new ConfigReader({}); - const router = Router(); - router.use(express.json()); - router.use(errorHandler()); - expect(() => HttpPostIngressEventPublisher.fromConfig({ config, logger, - router, }), ).not.toThrow(); }); diff --git a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts index 64b7140b54..b5a6ccbca1 100644 --- a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts +++ b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts @@ -41,7 +41,6 @@ export class HttpPostIngressEventPublisher implements EventPublisher { config: Config; ingresses?: { [topic: string]: Omit }; logger: Logger; - router: express.Router; }): HttpPostIngressEventPublisher { const topics = env.config.getOptionalStringArray('events.http.topics') ?? []; @@ -55,15 +54,18 @@ export class HttpPostIngressEventPublisher implements EventPublisher { } }); - return new HttpPostIngressEventPublisher(env.logger, env.router, ingresses); + return new HttpPostIngressEventPublisher(env.logger, ingresses); } private constructor( - private logger: Logger, - router: express.Router, - ingresses: { [topic: string]: Omit }, - ) { - router.use(this.createRouter(ingresses)); + private readonly logger: Logger, + private readonly ingresses: { + [topic: string]: Omit; + }, + ) {} + + bind(router: express.Router): void { + router.use('/http', this.createRouter(this.ingresses)); } async setEventBroker(eventBroker: EventBroker): Promise { @@ -92,8 +94,12 @@ export class HttpPostIngressEventPublisher implements EventPublisher { const path = `/${topic}`; router.post(path, async (request, response) => { + const requestDetails = { + body: request.body, + headers: request.headers, + }; const context = new RequestValidationContextImpl(); - await validator?.(request, context); + await validator?.(requestDetails, context); if (context.wasRejected()) { response .status(context.rejectionDetails!.status) diff --git a/plugins/events-node/api-report.md b/plugins/events-node/api-report.md index 1054211cbc..51136fd301 100644 --- a/plugins/events-node/api-report.md +++ b/plugins/events-node/api-report.md @@ -4,7 +4,6 @@ ```ts import { ExtensionPoint } from '@backstage/backend-plugin-api'; -import { Request as Request_2 } from 'express'; // @public export interface EventBroker { @@ -74,6 +73,12 @@ export interface HttpPostIngressOptions { validator?: RequestValidator; } +// @public (undocumented) +export interface RequestDetails { + body: unknown; + headers: Record; +} + // @public export interface RequestRejectionDetails { // (undocumented) @@ -89,7 +94,7 @@ export interface RequestValidationContext { // @public export type RequestValidator = ( - request: Request_2, + request: RequestDetails, context: RequestValidationContext, ) => Promise; diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index d193f33b00..978037f8ff 100644 --- a/plugins/events-node/package.json +++ b/plugins/events-node/package.json @@ -24,9 +24,7 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/backend-plugin-api": "workspace:^", - "@types/express": "^4.17.6", - "express": "^4.17.1" + "@backstage/backend-plugin-api": "workspace:^" }, "devDependencies": { "@backstage/cli": "workspace:^" diff --git a/plugins/events-node/src/api/http/validation/RequestDetails.ts b/plugins/events-node/src/api/http/validation/RequestDetails.ts new file mode 100644 index 0000000000..83c2669503 --- /dev/null +++ b/plugins/events-node/src/api/http/validation/RequestDetails.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @public + */ +export interface RequestDetails { + /** + * Request body. JSON payloads have been parsed already. + */ + body: unknown; + /** + * Key-value pairs of header names and values. Header names are lower-cased. + */ + headers: Record; +} diff --git a/plugins/events-node/src/api/http/validation/RequestValidator.ts b/plugins/events-node/src/api/http/validation/RequestValidator.ts index b419b855cf..3008db03ae 100644 --- a/plugins/events-node/src/api/http/validation/RequestValidator.ts +++ b/plugins/events-node/src/api/http/validation/RequestValidator.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Request } from 'express'; +import { RequestDetails } from './RequestDetails'; import { RequestValidationContext } from './RequestValidationContext'; /** @@ -29,6 +29,6 @@ import { RequestValidationContext } from './RequestValidationContext'; * @public */ export type RequestValidator = ( - request: Request, + request: RequestDetails, context: RequestValidationContext, ) => Promise; diff --git a/plugins/events-node/src/api/http/validation/index.ts b/plugins/events-node/src/api/http/validation/index.ts index 95f2d474eb..11f2e9f1f6 100644 --- a/plugins/events-node/src/api/http/validation/index.ts +++ b/plugins/events-node/src/api/http/validation/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +export type { RequestDetails } from './RequestDetails'; export type { RequestRejectionDetails } from './RequestRejectionDetails'; export type { RequestValidationContext } from './RequestValidationContext'; export type { RequestValidator } from './RequestValidator'; diff --git a/yarn.lock b/yarn.lock index b1531e5db9..b9fdd7f050 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5232,8 +5232,6 @@ __metadata: dependencies: "@backstage/backend-plugin-api": "workspace:^" "@backstage/cli": "workspace:^" - "@types/express": ^4.17.6 - express: ^4.17.1 languageName: unknown linkType: soft From 03f478fe0ae79694e554a90b1e915c8121cbccbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Fri, 18 Nov 2022 11:44:14 +0100 Subject: [PATCH 43/68] Use pluralize for catalog kind header component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- plugins/catalog/package.json | 1 + .../src/components/CatalogKindHeader/CatalogKindHeader.tsx | 3 ++- yarn.lock | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 1528737aac..244aab8626 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -49,6 +49,7 @@ "@material-ui/lab": "4.0.0-alpha.57", "history": "^5.0.0", "lodash": "^4.17.21", + "pluralize": "^8.0.0", "react-helmet": "6.1.0", "react-use": "^17.2.4", "zen-observable": "^0.9.0" diff --git a/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.tsx b/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.tsx index b0aaeae658..bb9e98d9c3 100644 --- a/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.tsx +++ b/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.tsx @@ -31,6 +31,7 @@ import { } from '@backstage/plugin-catalog-react'; import useAsync from 'react-use/lib/useAsync'; import { useApi } from '@backstage/core-plugin-api'; +import pluralize from 'pluralize'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -132,7 +133,7 @@ export function CatalogKindHeader(props: CatalogKindHeaderProps) { > {Object.keys(options).map(kind => ( - {`${options[kind]}s`} + {`${pluralize(options[kind])}`} ))} diff --git a/yarn.lock b/yarn.lock index 1a5617c798..0f497b5abe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4750,6 +4750,7 @@ __metadata: cross-fetch: ^3.1.5 history: ^5.0.0 lodash: ^4.17.21 + pluralize: ^8.0.0 react-helmet: 6.1.0 react-use: ^17.2.4 zen-observable: ^0.9.0 From 387d1d5218bfa01229a95e532f64c2d7c2cbfae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Fri, 18 Nov 2022 13:08:12 +0100 Subject: [PATCH 44/68] Add changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- .changeset/thin-donuts-join.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/thin-donuts-join.md diff --git a/.changeset/thin-donuts-join.md b/.changeset/thin-donuts-join.md new file mode 100644 index 0000000000..10e1030801 --- /dev/null +++ b/.changeset/thin-donuts-join.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Add `pluralize()` to the catalog kind header component. From 6273df985a4fcc6f82a631ce0aafab3a10e99c10 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 07:45:51 +0000 Subject: [PATCH 45/68] Update dependency css-loader to v6.7.2 Signed-off-by: Renovate Bot --- yarn.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1a5617c798..17b54cc038 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18382,20 +18382,20 @@ __metadata: linkType: hard "css-loader@npm:^6.5.1": - version: 6.7.1 - resolution: "css-loader@npm:6.7.1" + version: 6.7.2 + resolution: "css-loader@npm:6.7.2" dependencies: icss-utils: ^5.1.0 - postcss: ^8.4.7 + postcss: ^8.4.18 postcss-modules-extract-imports: ^3.0.0 postcss-modules-local-by-default: ^4.0.0 postcss-modules-scope: ^3.0.0 postcss-modules-values: ^4.0.0 postcss-value-parser: ^4.2.0 - semver: ^7.3.5 + semver: ^7.3.8 peerDependencies: webpack: ^5.0.0 - checksum: 170fdbc630a05a43679ef60fa97694766b568dbde37adccc0faafa964fc675f08b976bc68837bb73b61d60240e8d2cbcbf51540fe94ebc9dafc56e7c46ba5527 + checksum: f3c980cc9c033a02e60df7e5a2f33a1e8c2c3dd552f017485d2d81b383be623ae8c4189404e7a4a7403b52744683ae4b516def0f7ccf125c2b198cb647e46543 languageName: node linkType: hard @@ -30704,7 +30704,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.1.0, postcss@npm:^8.4.7": +"postcss@npm:^8.1.0, postcss@npm:^8.4.18": version: 8.4.19 resolution: "postcss@npm:8.4.19" dependencies: @@ -33287,14 +33287,14 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:~7.3.0": - version: 7.3.7 - resolution: "semver@npm:7.3.7" +"semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:~7.3.0": + version: 7.3.8 + resolution: "semver@npm:7.3.8" dependencies: lru-cache: ^6.0.0 bin: semver: bin/semver.js - checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 + checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 languageName: node linkType: hard From 429812a4233153482ab7c61076d4d99740465700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Fri, 18 Nov 2022 13:25:52 +0100 Subject: [PATCH 46/68] Test pluralized Technology kind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- .../components/CatalogKindHeader/CatalogKindHeader.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.test.tsx b/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.test.tsx index 9f68252ca7..f511f58c58 100644 --- a/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.test.tsx +++ b/plugins/catalog/src/components/CatalogKindHeader/CatalogKindHeader.test.tsx @@ -31,6 +31,7 @@ import { } from '@backstage/test-utils'; import { CatalogKindHeader } from './CatalogKindHeader'; import { errorApiRef } from '@backstage/core-plugin-api'; +import pluralize from 'pluralize'; const entities: Entity[] = [ { @@ -96,7 +97,7 @@ describe('', () => { entities.map(entity => { expect( - screen.getByRole('option', { name: `${entity.kind}s` }), + screen.getByRole('option', { name: `${pluralize(entity.kind)}` }), ).toBeInTheDocument(); }); }); From 5ee8a146fc9700c6e06ea775c5fae127330f4ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20=C5=A0neberger?= Date: Mon, 21 Nov 2022 08:43:20 +0100 Subject: [PATCH 47/68] Better changeset message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Šneberger --- .changeset/thin-donuts-join.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/thin-donuts-join.md b/.changeset/thin-donuts-join.md index 10e1030801..84705711d2 100644 --- a/.changeset/thin-donuts-join.md +++ b/.changeset/thin-donuts-join.md @@ -2,4 +2,4 @@ '@backstage/plugin-catalog': patch --- -Add `pluralize()` to the catalog kind header component. +Fixed Entity kind pluralisation in the `CatalogKindHeader` component. From fefe853fbd9779466d35f95dfa0eeb1a117cbbd3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 08:38:16 +0000 Subject: [PATCH 48/68] Update dependency @rjsf/validator-ajv8 to v5.0.0-beta.13 Signed-off-by: Renovate Bot --- yarn.lock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/yarn.lock b/yarn.lock index 17b54cc038..87e6ac97dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11584,16 +11584,16 @@ __metadata: linkType: hard "@rjsf/validator-ajv8@npm:^5.0.0-beta.12": - version: 5.0.0-beta.12 - resolution: "@rjsf/validator-ajv8@npm:5.0.0-beta.12" + version: 5.0.0-beta.13 + resolution: "@rjsf/validator-ajv8@npm:5.0.0-beta.13" dependencies: - ajv: ^8.11.0 ajv-formats: ^2.1.1 + ajv8: "npm:ajv@^8.11.0" lodash: ^4.17.15 lodash-es: ^4.17.15 peerDependencies: - "@rjsf/utils": ^5.0.0-beta.1 - checksum: 2acd420fdd099b35e534c4086ce5d3759574a8b4dc4b4a21ca35e85b023e6d9688aab1dbfca9381bdbf4aecbe3eb195e27453e8dd9cc70712fe2652e1496840c + "@rjsf/utils": ^5.0.0-beta.12 + checksum: e0a51e25845745da628794dadef2c7ffc910d26cb99a90fa763d4a5593b554b084c578a73ed688d02defbeb5ddec38066caae527f5dc8dabba7f012cec31e8a3 languageName: node linkType: hard @@ -15101,6 +15101,18 @@ __metadata: languageName: node linkType: hard +"ajv8@npm:ajv@^8.11.0, ajv@npm:^8.0.0, ajv@npm:^8.10.0, ajv@npm:^8.6.3, ajv@npm:^8.8.0": + version: 8.11.2 + resolution: "ajv@npm:8.11.2" + dependencies: + fast-deep-equal: ^3.1.1 + json-schema-traverse: ^1.0.0 + require-from-string: ^2.0.2 + uri-js: ^4.2.2 + checksum: 53435bf79ee7d1eabba8085962dba4c08d08593334b304db7772887f0b7beebc1b3d957432f7437ed4b60e53b5d966a57b439869890209c50fed610459999e3e + languageName: node + linkType: hard + "ajv@npm:^6.10.0, ajv@npm:^6.10.1, ajv@npm:^6.12.2, ajv@npm:^6.12.4, ajv@npm:^6.12.5, ajv@npm:^6.5.5, ajv@npm:^6.7.0, ajv@npm:~6.12.6": version: 6.12.6 resolution: "ajv@npm:6.12.6" @@ -15113,18 +15125,6 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.0, ajv@npm:^8.10.0, ajv@npm:^8.11.0, ajv@npm:^8.6.3, ajv@npm:^8.8.0": - version: 8.11.2 - resolution: "ajv@npm:8.11.2" - dependencies: - fast-deep-equal: ^3.1.1 - json-schema-traverse: ^1.0.0 - require-from-string: ^2.0.2 - uri-js: ^4.2.2 - checksum: 53435bf79ee7d1eabba8085962dba4c08d08593334b304db7772887f0b7beebc1b3d957432f7437ed4b60e53b5d966a57b439869890209c50fed610459999e3e - languageName: node - linkType: hard - "alphanum-sort@npm:^1.0.2": version: 1.0.2 resolution: "alphanum-sort@npm:1.0.2" From f46c2209ae86cce74178a91845356dc2aeddb5c9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 09:25:40 +0000 Subject: [PATCH 49/68] Update dependency @swc/core to v1.3.19 Signed-off-by: Renovate Bot --- storybook/yarn.lock | 86 ++++++++++++++++++++++----------------------- yarn.lock | 86 ++++++++++++++++++++++----------------------- 2 files changed, 86 insertions(+), 86 deletions(-) diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 77fbf8ca66..f7c56bc493 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -2966,90 +2966,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-darwin-arm64@npm:1.3.16" +"@swc/core-darwin-arm64@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-darwin-arm64@npm:1.3.19" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-darwin-x64@npm:1.3.16" +"@swc/core-darwin-x64@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-darwin-x64@npm:1.3.19" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.16" +"@swc/core-linux-arm-gnueabihf@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.19" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.16" +"@swc/core-linux-arm64-gnu@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.19" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.16" +"@swc/core-linux-arm64-musl@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.19" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.16" +"@swc/core-linux-x64-gnu@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.19" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-x64-musl@npm:1.3.16" +"@swc/core-linux-x64-musl@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-x64-musl@npm:1.3.19" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.16" +"@swc/core-win32-arm64-msvc@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.19" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.16" +"@swc/core-win32-ia32-msvc@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.19" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.16" +"@swc/core-win32-x64-msvc@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.19" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.9": - version: 1.3.16 - resolution: "@swc/core@npm:1.3.16" + version: 1.3.19 + resolution: "@swc/core@npm:1.3.19" dependencies: - "@swc/core-darwin-arm64": 1.3.16 - "@swc/core-darwin-x64": 1.3.16 - "@swc/core-linux-arm-gnueabihf": 1.3.16 - "@swc/core-linux-arm64-gnu": 1.3.16 - "@swc/core-linux-arm64-musl": 1.3.16 - "@swc/core-linux-x64-gnu": 1.3.16 - "@swc/core-linux-x64-musl": 1.3.16 - "@swc/core-win32-arm64-msvc": 1.3.16 - "@swc/core-win32-ia32-msvc": 1.3.16 - "@swc/core-win32-x64-msvc": 1.3.16 + "@swc/core-darwin-arm64": 1.3.19 + "@swc/core-darwin-x64": 1.3.19 + "@swc/core-linux-arm-gnueabihf": 1.3.19 + "@swc/core-linux-arm64-gnu": 1.3.19 + "@swc/core-linux-arm64-musl": 1.3.19 + "@swc/core-linux-x64-gnu": 1.3.19 + "@swc/core-linux-x64-musl": 1.3.19 + "@swc/core-win32-arm64-msvc": 1.3.19 + "@swc/core-win32-ia32-msvc": 1.3.19 + "@swc/core-win32-x64-msvc": 1.3.19 dependenciesMeta: "@swc/core-darwin-arm64": optional: true @@ -3073,7 +3073,7 @@ __metadata: optional: true bin: swcx: run_swcx.js - checksum: 4361252c928c487a02f526aecd8f3072b923244234c2701916944cf13c252b6d5ce2466caf3e0797d3e92e71a89d4a044f8577e51e8f7fe3af0fe30d94e94b13 + checksum: 752499e18f81df789a9737936b2a83ea1db34bb2f983c80b394766796d853fa7d30701b52b9cb30ba943ad15286d8316603149683f4c553a5493aa78d89a76e0 languageName: node linkType: hard diff --git a/yarn.lock b/yarn.lock index 87e6ac97dd..6edf89a5fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12150,90 +12150,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-darwin-arm64@npm:1.3.16" +"@swc/core-darwin-arm64@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-darwin-arm64@npm:1.3.19" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-darwin-x64@npm:1.3.16" +"@swc/core-darwin-x64@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-darwin-x64@npm:1.3.19" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.16" +"@swc/core-linux-arm-gnueabihf@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.19" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.16" +"@swc/core-linux-arm64-gnu@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.19" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.16" +"@swc/core-linux-arm64-musl@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.19" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.16" +"@swc/core-linux-x64-gnu@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.19" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-linux-x64-musl@npm:1.3.16" +"@swc/core-linux-x64-musl@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-linux-x64-musl@npm:1.3.19" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.16" +"@swc/core-win32-arm64-msvc@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.19" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.16" +"@swc/core-win32-ia32-msvc@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.19" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.16": - version: 1.3.16 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.16" +"@swc/core-win32-x64-msvc@npm:1.3.19": + version: 1.3.19 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.19" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.9": - version: 1.3.16 - resolution: "@swc/core@npm:1.3.16" + version: 1.3.19 + resolution: "@swc/core@npm:1.3.19" dependencies: - "@swc/core-darwin-arm64": 1.3.16 - "@swc/core-darwin-x64": 1.3.16 - "@swc/core-linux-arm-gnueabihf": 1.3.16 - "@swc/core-linux-arm64-gnu": 1.3.16 - "@swc/core-linux-arm64-musl": 1.3.16 - "@swc/core-linux-x64-gnu": 1.3.16 - "@swc/core-linux-x64-musl": 1.3.16 - "@swc/core-win32-arm64-msvc": 1.3.16 - "@swc/core-win32-ia32-msvc": 1.3.16 - "@swc/core-win32-x64-msvc": 1.3.16 + "@swc/core-darwin-arm64": 1.3.19 + "@swc/core-darwin-x64": 1.3.19 + "@swc/core-linux-arm-gnueabihf": 1.3.19 + "@swc/core-linux-arm64-gnu": 1.3.19 + "@swc/core-linux-arm64-musl": 1.3.19 + "@swc/core-linux-x64-gnu": 1.3.19 + "@swc/core-linux-x64-musl": 1.3.19 + "@swc/core-win32-arm64-msvc": 1.3.19 + "@swc/core-win32-ia32-msvc": 1.3.19 + "@swc/core-win32-x64-msvc": 1.3.19 dependenciesMeta: "@swc/core-darwin-arm64": optional: true @@ -12257,7 +12257,7 @@ __metadata: optional: true bin: swcx: run_swcx.js - checksum: 4361252c928c487a02f526aecd8f3072b923244234c2701916944cf13c252b6d5ce2466caf3e0797d3e92e71a89d4a044f8577e51e8f7fe3af0fe30d94e94b13 + checksum: 752499e18f81df789a9737936b2a83ea1db34bb2f983c80b394766796d853fa7d30701b52b9cb30ba943ad15286d8316603149683f4c553a5493aa78d89a76e0 languageName: node linkType: hard From 99014b723e28d9dc3109db22fcb4e127f2484585 Mon Sep 17 00:00:00 2001 From: Maximilian Vorbrodt Date: Mon, 21 Nov 2022 11:23:52 +0100 Subject: [PATCH 50/68] surface prop for growing or containing height of graph Signed-off-by: Maximilian Vorbrodt --- .../components/DependencyGraph/DependencyGraph.tsx | 13 ++++++++++++- .../GroupsExplorerContent/GroupsDiagram.tsx | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 5b26629082..94d152ce1e 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -170,6 +170,14 @@ export interface DependencyGraphProps * Default: 'curveMonotoneX' */ curve?: 'curveStepBefore' | 'curveMonotoneX'; + /** + * Controls if the graph should be contained or grow + * + * @remarks + * + * Default: 'grow' + */ + fit?: 'grow' | 'contain'; } const WORKSPACE_ID = 'workspace'; @@ -203,6 +211,7 @@ export function DependencyGraph( defs, zoom = 'enabled', curve = 'curveMonotoneX', + fit = 'grow', ...svgProps } = props; const theme: BackstageTheme = useTheme(); @@ -225,6 +234,8 @@ export function DependencyGraph( const maxHeight = Math.max(graphHeight, containerHeight); const minHeight = Math.min(graphHeight, containerHeight); + const scalableHeight = fit === 'grow' ? maxHeight : minHeight; + const containerRef = React.useMemo( () => debounce((node: SVGSVGElement) => { @@ -395,7 +406,7 @@ export function DependencyGraph( ref={containerRef} {...svgProps} width="100%" - height={minHeight} + height={scalableHeight} viewBox={`0 0 ${maxWidth} ${maxHeight}`} > diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx index d00eddb35e..674ef1e43f 100644 --- a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx @@ -240,6 +240,7 @@ export function GroupsDiagram() { direction={DependencyGraphTypes.Direction.RIGHT_LEFT} renderNode={RenderNode} className={classes.graph} + fit="contain" /> Date: Mon, 21 Nov 2022 11:29:45 +0100 Subject: [PATCH 51/68] update changeset Signed-off-by: Maximilian Vorbrodt --- .changeset/hip-stingrays-kneel.md | 5 +++++ .changeset/lucky-carrots-stare.md | 6 ------ .changeset/new-bugs-march.md | 5 +++++ 3 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 .changeset/hip-stingrays-kneel.md delete mode 100644 .changeset/lucky-carrots-stare.md create mode 100644 .changeset/new-bugs-march.md diff --git a/.changeset/hip-stingrays-kneel.md b/.changeset/hip-stingrays-kneel.md new file mode 100644 index 0000000000..1a8142b7c9 --- /dev/null +++ b/.changeset/hip-stingrays-kneel.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Create a minHeight variable and add a prop named 'fit' for determining if the graph height should grow or be contained. diff --git a/.changeset/lucky-carrots-stare.md b/.changeset/lucky-carrots-stare.md deleted file mode 100644 index 81ad1b7268..0000000000 --- a/.changeset/lucky-carrots-stare.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/plugin-explore': patch ---- - -Adds a background to GroupsDiagram and makes the graph fill up all the visible space on the page for, making it so the user does not have to scroll up or down on the page. diff --git a/.changeset/new-bugs-march.md b/.changeset/new-bugs-march.md new file mode 100644 index 0000000000..346f5a151e --- /dev/null +++ b/.changeset/new-bugs-march.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-explore': patch +--- + +Adds styling to graph forcing it to always fill out the available space. From f9c557ddf797b96a5f53c8ddd2fc14a206fd6031 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 10:33:07 +0000 Subject: [PATCH 52/68] Update dependency @swc/helpers to v0.4.14 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 87e6ac97dd..3beb795108 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12262,11 +12262,11 @@ __metadata: linkType: hard "@swc/helpers@npm:^0.4.7": - version: 0.4.12 - resolution: "@swc/helpers@npm:0.4.12" + version: 0.4.14 + resolution: "@swc/helpers@npm:0.4.14" dependencies: tslib: ^2.4.0 - checksum: 3f9112f37d87815b6d4270137fc78d22bb98c75138e9b0eac7cac203ec2cf2bffbf13b20a713067c292affd5e9e70a724eb245b8daf0963e7fe528b901771c28 + checksum: 273fd3f3fc461a92f3790cc551ea054745c6d6959afbe1232e6d7aa1c722bbc114d308aab96bef5c78fc0303c85c7b472ef00e2253251cc89737f3b1af56e5a5 languageName: node linkType: hard From 7913136f4aea7535cb7aa432b6f7063a4fa8c081 Mon Sep 17 00:00:00 2001 From: Maximilian Vorbrodt Date: Mon, 21 Nov 2022 11:34:43 +0100 Subject: [PATCH 53/68] fix spelling changeset Signed-off-by: Maximilian Vorbrodt --- .changeset/hip-stingrays-kneel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/hip-stingrays-kneel.md b/.changeset/hip-stingrays-kneel.md index 1a8142b7c9..171e9257f2 100644 --- a/.changeset/hip-stingrays-kneel.md +++ b/.changeset/hip-stingrays-kneel.md @@ -2,4 +2,4 @@ '@backstage/core-components': patch --- -Create a minHeight variable and add a prop named 'fit' for determining if the graph height should grow or be contained. +Create a variable for minimum height and add a prop named 'fit' for determining if the graph height should grow or be contained. From e006badb53d32d7abaad42961a0b28e2af4a7811 Mon Sep 17 00:00:00 2001 From: Maximilian Vorbrodt Date: Mon, 21 Nov 2022 12:04:59 +0100 Subject: [PATCH 54/68] update public API report of the package Signed-off-by: Maximilian Vorbrodt --- packages/core-components/api-report.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index fc3ab4e5fc..248b9e227c 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -250,6 +250,7 @@ export interface DependencyGraphProps edgeRanks?: number; edges: DependencyEdge[]; edgeWeight?: number; + fit?: 'grow' | 'contain'; labelOffset?: number; // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver labelPosition?: LabelPosition; From 9f5b72cea1538460676a5a911a2d59cd2a9d23d4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 11:09:55 +0000 Subject: [PATCH 55/68] Update dependency @testing-library/cypress to v8.0.7 Signed-off-by: Renovate Bot --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3beb795108..be413cbe94 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12353,14 +12353,14 @@ __metadata: linkType: soft "@testing-library/cypress@npm:^8.0.2": - version: 8.0.3 - resolution: "@testing-library/cypress@npm:8.0.3" + version: 8.0.7 + resolution: "@testing-library/cypress@npm:8.0.7" dependencies: "@babel/runtime": ^7.14.6 "@testing-library/dom": ^8.1.0 peerDependencies: - cypress: ^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 - checksum: 319f6c7297f85e5673882a71009b5d5b576006f986d87a6ca6f2f8c679d96d9779f3ddbc01654b5a943407fa91192814f250835ce01f153e33ef24818cf1bf20 + cypress: ^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + checksum: e005bc1a7ec808706c57e95ed312069fb5be39ea7362900dc2a32c09f124d478ade69ebcd7df88c076e3867ab328ae6e6ce13791bdf042621ff66b56552bf74b languageName: node linkType: hard From 87d9610984a980eaf35e6a040de5040ee5b5ec7e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 11:52:31 +0000 Subject: [PATCH 56/68] Update dependency @types/dockerode to v3.3.14 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index be413cbe94..d09015bd75 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12971,12 +12971,12 @@ __metadata: linkType: hard "@types/dockerode@npm:^3.3.0, @types/dockerode@npm:^3.3.8": - version: 3.3.13 - resolution: "@types/dockerode@npm:3.3.13" + version: 3.3.14 + resolution: "@types/dockerode@npm:3.3.14" dependencies: "@types/docker-modem": "*" "@types/node": "*" - checksum: dbcf3e33f5d8b731a1540d8b72c91d12583eb87d47adf8defb71f8bc1b328a26a035c66068255929ef99a7906da53ac6e4be76099e8fa7e9720c134b1ab3e153 + checksum: 6a8472622861fb0c97908963a8236d6e439c9feefc845b866326272ad0e5c33f537206fb4633424c5e3c8ec63559cb14bc737e2f88f0fcdf11496a9162d5b139 languageName: node linkType: hard From 7a3d2688edd86c86f48c9f6583aa47d94ac190a7 Mon Sep 17 00:00:00 2001 From: ivgo Date: Mon, 21 Nov 2022 15:00:45 +0100 Subject: [PATCH 57/68] Use promise Router & add known error in Vault backend Signed-off-by: ivgo --- .changeset/twelve-meals-smell.md | 6 ++++++ plugins/vault-backend/src/service/VaultBuilder.ts | 3 ++- plugins/vault-backend/src/service/vaultApi.ts | 4 +++- 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .changeset/twelve-meals-smell.md diff --git a/.changeset/twelve-meals-smell.md b/.changeset/twelve-meals-smell.md new file mode 100644 index 0000000000..a9d64823a3 --- /dev/null +++ b/.changeset/twelve-meals-smell.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-vault-backend': patch +--- + +Use `express-promise-router` to catch errors properly. +Add `403` error as a known one. It will now return a `NotAllowed` error. diff --git a/plugins/vault-backend/src/service/VaultBuilder.ts b/plugins/vault-backend/src/service/VaultBuilder.ts index b82a5802bb..788b90ff01 100644 --- a/plugins/vault-backend/src/service/VaultBuilder.ts +++ b/plugins/vault-backend/src/service/VaultBuilder.ts @@ -17,7 +17,8 @@ import { Config } from '@backstage/config'; import { InputError } from '@backstage/errors'; import { Logger } from 'winston'; -import express, { Router } from 'express'; +import express from 'express'; +import Router from 'express-promise-router'; import { VaultClient } from './vaultApi'; import { TaskRunner, PluginTaskScheduler } from '@backstage/backend-tasks'; import { errorHandler } from '@backstage/backend-common'; diff --git a/plugins/vault-backend/src/service/vaultApi.ts b/plugins/vault-backend/src/service/vaultApi.ts index 785352fea8..ea588964dc 100644 --- a/plugins/vault-backend/src/service/vaultApi.ts +++ b/plugins/vault-backend/src/service/vaultApi.ts @@ -15,7 +15,7 @@ */ import { Config } from '@backstage/config'; -import { NotFoundError } from '@backstage/errors'; +import { NotAllowedError, NotFoundError } from '@backstage/errors'; import fetch from 'node-fetch'; import plimit from 'p-limit'; import { getVaultConfig, VaultConfig } from '../config'; @@ -103,6 +103,8 @@ export class VaultClient implements VaultApi { return (await response.json()) as T; } else if (response.status === 404) { throw new NotFoundError(`No secrets found in path '${path}'`); + } else if (response.status === 403) { + throw new NotAllowedError(response.statusText); } throw new Error( `Unexpected error while fetching secrets from path '${path}'`, From f8cfa44bb584dc0e9f66a23a5e4ae451e604eb7b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 14:11:43 +0000 Subject: [PATCH 58/68] Update dependency esbuild to v0.15.15 Signed-off-by: Renovate Bot --- yarn.lock | 182 +++++++++++++++++++++++++++--------------------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7b79b050f3..49e22b1776 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8178,16 +8178,16 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.15.13": - version: 0.15.13 - resolution: "@esbuild/android-arm@npm:0.15.13" +"@esbuild/android-arm@npm:0.15.15": + version: 0.15.15 + resolution: "@esbuild/android-arm@npm:0.15.15" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.15.13": - version: 0.15.13 - resolution: "@esbuild/linux-loong64@npm:0.15.13" +"@esbuild/linux-loong64@npm:0.15.15": + version: 0.15.15 + resolution: "@esbuild/linux-loong64@npm:0.15.15" conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -20060,100 +20060,100 @@ __metadata: languageName: node linkType: hard -"esbuild-android-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-android-64@npm:0.15.13" +"esbuild-android-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-android-64@npm:0.15.15" conditions: os=android & cpu=x64 languageName: node linkType: hard -"esbuild-android-arm64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-android-arm64@npm:0.15.13" +"esbuild-android-arm64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-android-arm64@npm:0.15.15" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"esbuild-darwin-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-darwin-64@npm:0.15.13" +"esbuild-darwin-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-darwin-64@npm:0.15.15" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"esbuild-darwin-arm64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-darwin-arm64@npm:0.15.13" +"esbuild-darwin-arm64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-darwin-arm64@npm:0.15.15" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"esbuild-freebsd-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-freebsd-64@npm:0.15.13" +"esbuild-freebsd-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-freebsd-64@npm:0.15.15" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"esbuild-freebsd-arm64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-freebsd-arm64@npm:0.15.13" +"esbuild-freebsd-arm64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-freebsd-arm64@npm:0.15.15" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-32@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-32@npm:0.15.13" +"esbuild-linux-32@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-32@npm:0.15.15" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"esbuild-linux-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-64@npm:0.15.13" +"esbuild-linux-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-64@npm:0.15.15" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"esbuild-linux-arm64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-arm64@npm:0.15.13" +"esbuild-linux-arm64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-arm64@npm:0.15.15" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"esbuild-linux-arm@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-arm@npm:0.15.13" +"esbuild-linux-arm@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-arm@npm:0.15.15" conditions: os=linux & cpu=arm languageName: node linkType: hard -"esbuild-linux-mips64le@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-mips64le@npm:0.15.13" +"esbuild-linux-mips64le@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-mips64le@npm:0.15.15" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"esbuild-linux-ppc64le@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-ppc64le@npm:0.15.13" +"esbuild-linux-ppc64le@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-ppc64le@npm:0.15.15" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"esbuild-linux-riscv64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-riscv64@npm:0.15.13" +"esbuild-linux-riscv64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-riscv64@npm:0.15.15" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"esbuild-linux-s390x@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-linux-s390x@npm:0.15.13" +"esbuild-linux-s390x@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-linux-s390x@npm:0.15.15" conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -20174,74 +20174,74 @@ __metadata: languageName: node linkType: hard -"esbuild-netbsd-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-netbsd-64@npm:0.15.13" +"esbuild-netbsd-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-netbsd-64@npm:0.15.15" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"esbuild-openbsd-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-openbsd-64@npm:0.15.13" +"esbuild-openbsd-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-openbsd-64@npm:0.15.15" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"esbuild-sunos-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-sunos-64@npm:0.15.13" +"esbuild-sunos-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-sunos-64@npm:0.15.15" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"esbuild-windows-32@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-windows-32@npm:0.15.13" +"esbuild-windows-32@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-windows-32@npm:0.15.15" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"esbuild-windows-64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-windows-64@npm:0.15.13" +"esbuild-windows-64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-windows-64@npm:0.15.15" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"esbuild-windows-arm64@npm:0.15.13": - version: 0.15.13 - resolution: "esbuild-windows-arm64@npm:0.15.13" +"esbuild-windows-arm64@npm:0.15.15": + version: 0.15.15 + resolution: "esbuild-windows-arm64@npm:0.15.15" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard "esbuild@npm:^0.15.0, esbuild@npm:^0.15.6": - version: 0.15.13 - resolution: "esbuild@npm:0.15.13" + version: 0.15.15 + resolution: "esbuild@npm:0.15.15" dependencies: - "@esbuild/android-arm": 0.15.13 - "@esbuild/linux-loong64": 0.15.13 - esbuild-android-64: 0.15.13 - esbuild-android-arm64: 0.15.13 - esbuild-darwin-64: 0.15.13 - esbuild-darwin-arm64: 0.15.13 - esbuild-freebsd-64: 0.15.13 - esbuild-freebsd-arm64: 0.15.13 - esbuild-linux-32: 0.15.13 - esbuild-linux-64: 0.15.13 - esbuild-linux-arm: 0.15.13 - esbuild-linux-arm64: 0.15.13 - esbuild-linux-mips64le: 0.15.13 - esbuild-linux-ppc64le: 0.15.13 - esbuild-linux-riscv64: 0.15.13 - esbuild-linux-s390x: 0.15.13 - esbuild-netbsd-64: 0.15.13 - esbuild-openbsd-64: 0.15.13 - esbuild-sunos-64: 0.15.13 - esbuild-windows-32: 0.15.13 - esbuild-windows-64: 0.15.13 - esbuild-windows-arm64: 0.15.13 + "@esbuild/android-arm": 0.15.15 + "@esbuild/linux-loong64": 0.15.15 + esbuild-android-64: 0.15.15 + esbuild-android-arm64: 0.15.15 + esbuild-darwin-64: 0.15.15 + esbuild-darwin-arm64: 0.15.15 + esbuild-freebsd-64: 0.15.15 + esbuild-freebsd-arm64: 0.15.15 + esbuild-linux-32: 0.15.15 + esbuild-linux-64: 0.15.15 + esbuild-linux-arm: 0.15.15 + esbuild-linux-arm64: 0.15.15 + esbuild-linux-mips64le: 0.15.15 + esbuild-linux-ppc64le: 0.15.15 + esbuild-linux-riscv64: 0.15.15 + esbuild-linux-s390x: 0.15.15 + esbuild-netbsd-64: 0.15.15 + esbuild-openbsd-64: 0.15.15 + esbuild-sunos-64: 0.15.15 + esbuild-windows-32: 0.15.15 + esbuild-windows-64: 0.15.15 + esbuild-windows-arm64: 0.15.15 dependenciesMeta: "@esbuild/android-arm": optional: true @@ -20289,7 +20289,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: ef5f339fae7e2abc4ec5484d4b301efdf40f580e043cbf8a66e19d6c91df82368a810abec61fd5e5db226f0c354f49c36616c9ea04c5412a142a050c10239bf7 + checksum: 9a194a310ac7d6989d1fe7354901d531d3b0d4cc1897a85a7cb9f1ae0fe8d82eb59747e63c91f676bab7bed8d6a1f28f620d49f043e7bd6203f9afe34031f739 languageName: node linkType: hard From b9360478de845f7d56bfc73ca020170577aa67c2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 15:10:10 +0000 Subject: [PATCH 59/68] Update dependency @graphql-tools/schema to v9.0.10 Signed-off-by: Renovate Bot --- yarn.lock | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0bc2702bd3..caeb409e14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8724,15 +8724,15 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/merge@npm:8.3.11, @graphql-tools/merge@npm:^8.2.6": - version: 8.3.11 - resolution: "@graphql-tools/merge@npm:8.3.11" +"@graphql-tools/merge@npm:8.3.12, @graphql-tools/merge@npm:^8.2.6": + version: 8.3.12 + resolution: "@graphql-tools/merge@npm:8.3.12" dependencies: - "@graphql-tools/utils": 9.1.0 + "@graphql-tools/utils": 9.1.1 tslib: ^2.4.0 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 3cd16577f1ea1bcc389d232fa620adc0fefc91a6b42b48e7bb5e7bb67b67d206d409a9a8d60cac4d8cd5f0f1c5353369f7c5eb4054b44ce2277545e1fd1de17f + checksum: aab6f6edbaa2f63c7f44e4a1832d8028570d09ea85bb4a9525016879fad2ea2f8ea260c0b78ab1f6b7a418d92a3debfbbedecb3f5a36a5590edd0b2ac1cd2012 languageName: node linkType: hard @@ -8858,16 +8858,16 @@ __metadata: linkType: hard "@graphql-tools/schema@npm:^9.0.0": - version: 9.0.9 - resolution: "@graphql-tools/schema@npm:9.0.9" + version: 9.0.10 + resolution: "@graphql-tools/schema@npm:9.0.10" dependencies: - "@graphql-tools/merge": 8.3.11 - "@graphql-tools/utils": 9.1.0 + "@graphql-tools/merge": 8.3.12 + "@graphql-tools/utils": 9.1.1 tslib: ^2.4.0 value-or-promise: 1.0.11 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 23620a993eff57723302315100d947b81664d6a1e00010ff293b3649f51ab423946d73920cce0ce822b7f7f42b99ea23240902ff47df3d252639613c0df7b971 + checksum: 4c1c7fc1c84a2faf2310a110ed72125b511371f1ea2869442fd55b5bcdb2c431dd60235ff77f32860235ee99dd12746e23dace35a18a8ffd339fad4f51004321 languageName: node linkType: hard @@ -8940,18 +8940,7 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/utils@npm:9.1.0": - version: 9.1.0 - resolution: "@graphql-tools/utils@npm:9.1.0" - dependencies: - tslib: ^2.4.0 - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: dcf08df3bd1715e25e3b80bf2dd332376239ebcd2a9b93f4d6b72a7bd3feffe2207b9683bb8fcaed9d0d1286f701d0686fe99ac803b91c0df54509b869c0554d - languageName: node - linkType: hard - -"@graphql-tools/utils@npm:^9.1.1": +"@graphql-tools/utils@npm:9.1.1, @graphql-tools/utils@npm:^9.1.1": version: 9.1.1 resolution: "@graphql-tools/utils@npm:9.1.1" dependencies: From 7b78c0bac0da03b244cf60919a6ab9b15970414e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 15:12:31 +0000 Subject: [PATCH 60/68] Update dependency eslint-plugin-react to v7.31.11 Signed-off-by: Renovate Bot --- yarn.lock | 170 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 98 insertions(+), 72 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0bc2702bd3..abf7bb2434 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15553,16 +15553,16 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.1.4, array-includes@npm:^3.1.5": - version: 3.1.5 - resolution: "array-includes@npm:3.1.5" +"array-includes@npm:^3.1.4, array-includes@npm:^3.1.5, array-includes@npm:^3.1.6": + version: 3.1.6 + resolution: "array-includes@npm:3.1.6" dependencies: call-bind: ^1.0.2 define-properties: ^1.1.4 - es-abstract: ^1.19.5 - get-intrinsic: ^1.1.1 + es-abstract: ^1.20.4 + get-intrinsic: ^1.1.3 is-string: ^1.0.7 - checksum: f6f24d834179604656b7bec3e047251d5cc87e9e87fab7c175c61af48e80e75acd296017abcde21fb52292ab6a2a449ab2ee37213ee48c8709f004d75983f9c5 + checksum: f22f8cd8ba8a6448d91eebdc69f04e4e55085d09232b5216ee2d476dab3ef59984e8d1889e662c6a0ed939dcb1b57fd05b2c0209c3370942fc41b752c82a2ca5 languageName: node linkType: hard @@ -15601,15 +15601,28 @@ __metadata: languageName: node linkType: hard -"array.prototype.flatmap@npm:^1.3.0": - version: 1.3.0 - resolution: "array.prototype.flatmap@npm:1.3.0" +"array.prototype.flatmap@npm:^1.3.1": + version: 1.3.1 + resolution: "array.prototype.flatmap@npm:1.3.1" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.2 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 es-shim-unscopables: ^1.0.0 - checksum: 818538f39409c4045d874be85df0dbd195e1446b14d22f95bdcfefea44ae77db44e42dcd89a559254ec5a7c8b338cfc986cc6d641e3472f9a5326b21eb2976a2 + checksum: 8c1c43a4995f12cf12523436da28515184c753807b3f0bc2ca6c075f71c470b099e2090cc67dba8e5280958fea401c1d0c59e1db0143272aef6cd1103921a987 + languageName: node + linkType: hard + +"array.prototype.tosorted@npm:^1.1.1": + version: 1.1.1 + resolution: "array.prototype.tosorted@npm:1.1.1" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + es-shim-unscopables: ^1.0.0 + get-intrinsic: ^1.1.3 + checksum: 7923324a67e70a2fc0a6e40237405d92395e45ebd76f5cb89c2a5cf1e66b47aca6baacd0cd628ffd88830b90d47fff268071493d09c9ae123645613dac2c2ca3 languageName: node linkType: hard @@ -19995,34 +20008,35 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.18.0-next.1, es-abstract@npm:^1.18.0-next.2, es-abstract@npm:^1.19.0, es-abstract@npm:^1.19.1, es-abstract@npm:^1.19.2, es-abstract@npm:^1.19.5": - version: 1.20.1 - resolution: "es-abstract@npm:1.20.1" +"es-abstract@npm:^1.18.0-next.1, es-abstract@npm:^1.18.0-next.2, es-abstract@npm:^1.19.0, es-abstract@npm:^1.19.2, es-abstract@npm:^1.19.5, es-abstract@npm:^1.20.4": + version: 1.20.4 + resolution: "es-abstract@npm:1.20.4" dependencies: call-bind: ^1.0.2 es-to-primitive: ^1.2.1 function-bind: ^1.1.1 function.prototype.name: ^1.1.5 - get-intrinsic: ^1.1.1 + get-intrinsic: ^1.1.3 get-symbol-description: ^1.0.0 has: ^1.0.3 has-property-descriptors: ^1.0.0 has-symbols: ^1.0.3 internal-slot: ^1.0.3 - is-callable: ^1.2.4 + is-callable: ^1.2.7 is-negative-zero: ^2.0.2 is-regex: ^1.1.4 is-shared-array-buffer: ^1.0.2 is-string: ^1.0.7 is-weakref: ^1.0.2 - object-inspect: ^1.12.0 + object-inspect: ^1.12.2 object-keys: ^1.1.1 - object.assign: ^4.1.2 + object.assign: ^4.1.4 regexp.prototype.flags: ^1.4.3 + safe-regex-test: ^1.0.0 string.prototype.trimend: ^1.0.5 string.prototype.trimstart: ^1.0.5 unbox-primitive: ^1.0.2 - checksum: 28da27ae0ed9c76df7ee8ef5c278df79dcfdb554415faf7068bb7c58f8ba8e2a16bfb59e586844be6429ab4c302ca7748979d48442224cb1140b051866d74b7f + checksum: 89297cc785c31aedf961a603d5a07ed16471e435d3a1b6d070b54f157cf48454b95cda2ac55e4b86ff4fe3276e835fcffd2771578e6fa634337da49b26826141 languageName: node linkType: hard @@ -20543,26 +20557,27 @@ __metadata: linkType: hard "eslint-plugin-react@npm:^7.28.0": - version: 7.31.10 - resolution: "eslint-plugin-react@npm:7.31.10" + version: 7.31.11 + resolution: "eslint-plugin-react@npm:7.31.11" dependencies: - array-includes: ^3.1.5 - array.prototype.flatmap: ^1.3.0 + array-includes: ^3.1.6 + array.prototype.flatmap: ^1.3.1 + array.prototype.tosorted: ^1.1.1 doctrine: ^2.1.0 estraverse: ^5.3.0 jsx-ast-utils: ^2.4.1 || ^3.0.0 minimatch: ^3.1.2 - object.entries: ^1.1.5 - object.fromentries: ^2.0.5 - object.hasown: ^1.1.1 - object.values: ^1.1.5 + object.entries: ^1.1.6 + object.fromentries: ^2.0.6 + object.hasown: ^1.1.2 + object.values: ^1.1.6 prop-types: ^15.8.1 resolve: ^2.0.0-next.3 semver: ^6.3.0 - string.prototype.matchall: ^4.0.7 + string.prototype.matchall: ^4.0.8 peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: f013669c296483559a760648fa06425f161b1aff93c668f14c4561c933d22a7836b745b88a795c53cab929c71513d5fd1f2ffdddff915709f01b77ac25f5b71b + checksum: a3d612f6647bef33cf2a67c81a6b37b42c075300ed079cffecf5fb475c0d6ab855c1de340d1cbf361a0126429fb906dda597527235d2d12c4404453dbc712fc6 languageName: node linkType: hard @@ -22218,14 +22233,14 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.0, get-intrinsic@npm:^1.1.1": - version: 1.1.2 - resolution: "get-intrinsic@npm:1.1.2" +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.0, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3": + version: 1.1.3 + resolution: "get-intrinsic@npm:1.1.3" dependencies: function-bind: ^1.1.1 has: ^1.0.3 has-symbols: ^1.0.3 - checksum: 252f45491f2ba88ebf5b38018020c7cc3279de54b1d67ffb70c0cdf1dfa8ab31cd56467b5d117a8b4275b7a4dde91f86766b163a17a850f036528a7b2faafb2b + checksum: 152d79e87251d536cf880ba75cfc3d6c6c50e12b3a64e1ea960e73a3752b47c69f46034456eae1b0894359ce3bc64c55c186f2811f8a788b75b638b06fab228a languageName: node linkType: hard @@ -23960,10 +23975,10 @@ __metadata: languageName: node linkType: hard -"is-callable@npm:^1.1.4, is-callable@npm:^1.2.4": - version: 1.2.4 - resolution: "is-callable@npm:1.2.4" - checksum: 1a28d57dc435797dae04b173b65d6d1e77d4f16276e9eff973f994eadcfdc30a017e6a597f092752a083c1103cceb56c91e3dadc6692fedb9898dfaba701575f +"is-callable@npm:^1.1.4, is-callable@npm:^1.2.7": + version: 1.2.7 + resolution: "is-callable@npm:1.2.7" + checksum: 61fd57d03b0d984e2ed3720fb1c7a897827ea174bd44402878e059542ea8c4aeedee0ea0985998aa5cc2736b2fa6e271c08587addb5b3959ac52cf665173d1ac languageName: node linkType: hard @@ -28875,7 +28890,7 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.0, object-inspect@npm:^1.12.2, object-inspect@npm:^1.9.0": +"object-inspect@npm:^1.12.2, object-inspect@npm:^1.9.0": version: 1.12.2 resolution: "object-inspect@npm:1.12.2" checksum: a534fc1b8534284ed71f25ce3a496013b7ea030f3d1b77118f6b7b1713829262be9e6243acbcb3ef8c626e2b64186112cb7f6db74e37b2789b9c789ca23048b2 @@ -28889,7 +28904,7 @@ __metadata: languageName: node linkType: hard -"object.assign@npm:^4.1.0, object.assign@npm:^4.1.2": +"object.assign@npm:^4.1.0, object.assign@npm:^4.1.2, object.assign@npm:^4.1.4": version: 4.1.4 resolution: "object.assign@npm:4.1.4" dependencies: @@ -28901,46 +28916,46 @@ __metadata: languageName: node linkType: hard -"object.entries@npm:^1.1.5": - version: 1.1.5 - resolution: "object.entries@npm:1.1.5" +"object.entries@npm:^1.1.6": + version: 1.1.6 + resolution: "object.entries@npm:1.1.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.1 - checksum: d658696f74fd222060d8428d2a9fda2ce736b700cb06f6bdf4a16a1892d145afb746f453502b2fa55d1dca8ead6f14ddbcf66c545df45adadea757a6c4cd86c7 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 0f8c47517e6a9a980241eafe3b73de11e59511883173c2b93d67424a008e47e11b77c80e431ad1d8a806f6108b225a1cab9223e53e555776c612a24297117d28 languageName: node linkType: hard -"object.fromentries@npm:^2.0.5": - version: 2.0.5 - resolution: "object.fromentries@npm:2.0.5" +"object.fromentries@npm:^2.0.6": + version: 2.0.6 + resolution: "object.fromentries@npm:2.0.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.1 - checksum: 61a0b565ded97b76df9e30b569729866e1824cce902f98e90bb106e84f378aea20163366f66dc75c9000e2aad2ed0caf65c6f530cb2abc4c0c0f6c982102db4b + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 453c6d694180c0c30df451b60eaf27a5b9bca3fb43c37908fd2b78af895803dc631242bcf05582173afa40d8d0e9c96e16e8874b39471aa53f3ac1f98a085d85 languageName: node linkType: hard -"object.hasown@npm:^1.1.1": - version: 1.1.1 - resolution: "object.hasown@npm:1.1.1" +"object.hasown@npm:^1.1.2": + version: 1.1.2 + resolution: "object.hasown@npm:1.1.2" dependencies: define-properties: ^1.1.4 - es-abstract: ^1.19.5 - checksum: d8ed4907ce57f48b93e3b53c418fd6787bf226a51e8d698c91e39b78e80fe5b124cb6282f6a9d5be21cf9e2c7829ab10206dcc6112b7748860eefe641880c793 + es-abstract: ^1.20.4 + checksum: b936572536db0cdf38eb30afd2f1026a8b6f2cc5d2c4497c9d9bbb01eaf3e980dead4fd07580cfdd098e6383e5a9db8212d3ea0c6bdd2b5e68c60aa7e3b45566 languageName: node linkType: hard -"object.values@npm:^1.1.5": - version: 1.1.5 - resolution: "object.values@npm:1.1.5" +"object.values@npm:^1.1.5, object.values@npm:^1.1.6": + version: 1.1.6 + resolution: "object.values@npm:1.1.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.1 - checksum: 0f17e99741ebfbd0fa55ce942f6184743d3070c61bd39221afc929c8422c4907618c8da694c6915bc04a83ab3224260c779ba37fc07bb668bdc5f33b66a902a4 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: f6fff9fd817c24cfd8107f50fb33061d81cd11bacc4e3dbb3852e9ff7692fde4dbce823d4333ea27cd9637ef1b6690df5fbb61f1ed314fa2959598dc3ae23d8e languageName: node linkType: hard @@ -32302,7 +32317,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.4.1, regexp.prototype.flags@npm:^1.4.3": +"regexp.prototype.flags@npm:^1.4.3": version: 1.4.3 resolution: "regexp.prototype.flags@npm:1.4.3" dependencies: @@ -33085,6 +33100,17 @@ __metadata: languageName: node linkType: hard +"safe-regex-test@npm:^1.0.0": + version: 1.0.0 + resolution: "safe-regex-test@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.1.3 + is-regex: ^1.1.4 + checksum: bc566d8beb8b43c01b94e67de3f070fd2781685e835959bbbaaec91cc53381145ca91f69bd837ce6ec244817afa0a5e974fc4e40a2957f0aca68ac3add1ddd34 + languageName: node + linkType: hard + "safe-regex2@npm:^2.0.0": version: 2.0.0 resolution: "safe-regex2@npm:2.0.0" @@ -34409,19 +34435,19 @@ __metadata: languageName: node linkType: hard -"string.prototype.matchall@npm:^4.0.7": - version: 4.0.7 - resolution: "string.prototype.matchall@npm:4.0.7" +"string.prototype.matchall@npm:^4.0.8": + version: 4.0.8 + resolution: "string.prototype.matchall@npm:4.0.8" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.1 - get-intrinsic: ^1.1.1 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + get-intrinsic: ^1.1.3 has-symbols: ^1.0.3 internal-slot: ^1.0.3 - regexp.prototype.flags: ^1.4.1 + regexp.prototype.flags: ^1.4.3 side-channel: ^1.0.4 - checksum: fc09f3ccbfb325de0472bcc87a6be0598a7499e0b4a31db5789676155b15754a4cc4bb83924f15fc9ed48934dac7366ee52c8b9bd160bed6fd072c93b489e75c + checksum: 952da3a818de42ad1c10b576140a5e05b4de7b34b8d9dbf00c3ac8c1293e9c0f533613a39c5cda53e0a8221f2e710bc2150e730b1c2278d60004a8a35726efb6 languageName: node linkType: hard From 4019bef47ba5f6aa6901051f3c08e4a899a05f0c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 15:13:40 +0000 Subject: [PATCH 61/68] Update dependency js-base64 to v3.7.3 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0bc2702bd3..56ec73e8f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25248,9 +25248,9 @@ __metadata: linkType: hard "js-base64@npm:^3.6.0": - version: 3.7.2 - resolution: "js-base64@npm:3.7.2" - checksum: 573f28e9a27c3df60096d4d3f551bcb4fcb6d49161cf83396e9bad9b76f94736a70bb70b8808fe834dff2a388f76604ba09d6e153bbf181646e407720139fa5b + version: 3.7.3 + resolution: "js-base64@npm:3.7.3" + checksum: ee19bed9ba21693e4583a47773dd701938de63db82e7c324d4c19093046157f0d87905cc20540f194f1d0ec88d6e1bfc4056aea916bcb873ebcf95942d26aa5a languageName: node linkType: hard From 806111f8ba4d3c5a4fc09110184038fef24e905a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 15:15:03 +0000 Subject: [PATCH 62/68] Update dependency @aws-sdk/client-sqs to v3.213.0 Signed-off-by: Renovate Bot --- yarn.lock | 973 +++++++++++++++++++++++++++++------------------------- 1 file changed, 527 insertions(+), 446 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0bc2702bd3..c668093de0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -373,647 +373,719 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/abort-controller@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/abort-controller@npm:3.183.0" +"@aws-sdk/abort-controller@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/abort-controller@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 28f5bc16b5aba414e354df603bc8becd010aa8c9b3c79e527130723a39f21d5a3df98c3bafb3cea733d415a5b042af90098ffa9dfa29d400114c2f6f5292524e + checksum: ba69d926f6939fe91d4d169c64106683fc467a0b1778d48ddae194f8abaf484a2c2e9f997b7de9d4bab9dcb30532551aec88c4e52aeed71f43eb7335bd159adc languageName: node linkType: hard "@aws-sdk/client-sqs@npm:^3.0.0": - version: 3.183.0 - resolution: "@aws-sdk/client-sqs@npm:3.183.0" + version: 3.213.0 + resolution: "@aws-sdk/client-sqs@npm:3.213.0" dependencies: "@aws-crypto/sha256-browser": 2.0.0 "@aws-crypto/sha256-js": 2.0.0 - "@aws-sdk/client-sts": 3.183.0 - "@aws-sdk/config-resolver": 3.183.0 - "@aws-sdk/credential-provider-node": 3.183.0 - "@aws-sdk/fetch-http-handler": 3.183.0 - "@aws-sdk/hash-node": 3.183.0 - "@aws-sdk/invalid-dependency": 3.183.0 - "@aws-sdk/md5-js": 3.183.0 - "@aws-sdk/middleware-content-length": 3.183.0 - "@aws-sdk/middleware-host-header": 3.183.0 - "@aws-sdk/middleware-logger": 3.183.0 - "@aws-sdk/middleware-recursion-detection": 3.183.0 - "@aws-sdk/middleware-retry": 3.183.0 - "@aws-sdk/middleware-sdk-sqs": 3.183.0 - "@aws-sdk/middleware-serde": 3.183.0 - "@aws-sdk/middleware-signing": 3.183.0 - "@aws-sdk/middleware-stack": 3.183.0 - "@aws-sdk/middleware-user-agent": 3.183.0 - "@aws-sdk/node-config-provider": 3.183.0 - "@aws-sdk/node-http-handler": 3.183.0 - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/smithy-client": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/url-parser": 3.183.0 - "@aws-sdk/util-base64-browser": 3.183.0 - "@aws-sdk/util-base64-node": 3.183.0 - "@aws-sdk/util-body-length-browser": 3.183.0 - "@aws-sdk/util-body-length-node": 3.183.0 - "@aws-sdk/util-defaults-mode-browser": 3.183.0 - "@aws-sdk/util-defaults-mode-node": 3.183.0 - "@aws-sdk/util-user-agent-browser": 3.183.0 - "@aws-sdk/util-user-agent-node": 3.183.0 - "@aws-sdk/util-utf8-browser": 3.183.0 - "@aws-sdk/util-utf8-node": 3.183.0 - entities: 2.2.0 - fast-xml-parser: 3.19.0 + "@aws-sdk/client-sts": 3.213.0 + "@aws-sdk/config-resolver": 3.212.0 + "@aws-sdk/credential-provider-node": 3.212.0 + "@aws-sdk/fetch-http-handler": 3.212.0 + "@aws-sdk/hash-node": 3.212.0 + "@aws-sdk/invalid-dependency": 3.212.0 + "@aws-sdk/md5-js": 3.212.0 + "@aws-sdk/middleware-content-length": 3.212.0 + "@aws-sdk/middleware-endpoint": 3.212.0 + "@aws-sdk/middleware-host-header": 3.212.0 + "@aws-sdk/middleware-logger": 3.212.0 + "@aws-sdk/middleware-recursion-detection": 3.212.0 + "@aws-sdk/middleware-retry": 3.212.0 + "@aws-sdk/middleware-sdk-sqs": 3.212.0 + "@aws-sdk/middleware-serde": 3.212.0 + "@aws-sdk/middleware-signing": 3.212.0 + "@aws-sdk/middleware-stack": 3.212.0 + "@aws-sdk/middleware-user-agent": 3.212.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/node-http-handler": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/smithy-client": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/url-parser": 3.212.0 + "@aws-sdk/util-base64": 3.208.0 + "@aws-sdk/util-body-length-browser": 3.188.0 + "@aws-sdk/util-body-length-node": 3.208.0 + "@aws-sdk/util-defaults-mode-browser": 3.212.0 + "@aws-sdk/util-defaults-mode-node": 3.212.0 + "@aws-sdk/util-endpoints": 3.212.0 + "@aws-sdk/util-user-agent-browser": 3.212.0 + "@aws-sdk/util-user-agent-node": 3.212.0 + "@aws-sdk/util-utf8-browser": 3.188.0 + "@aws-sdk/util-utf8-node": 3.208.0 + fast-xml-parser: 4.0.11 tslib: ^2.3.1 - checksum: fe0a7ba53cac4d52c25a4a4f1a1bb16438d5011347fcfec037df1d73379d750cd47b437f7a45aea2d4e29bc92587b51dd7631a06f5d6881e3b67b2d4c0dd2633 + checksum: baa9d4c816cae23874f14fe4c6c6da26b8d52b65d459167ebacbc3ec207ef2a83860f452df8b47e11231587e9b1d7a1aedc74051bb79473037756e4d6e492b36 languageName: node linkType: hard -"@aws-sdk/client-sso@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/client-sso@npm:3.183.0" +"@aws-sdk/client-sso-oidc@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/client-sso-oidc@npm:3.212.0" dependencies: "@aws-crypto/sha256-browser": 2.0.0 "@aws-crypto/sha256-js": 2.0.0 - "@aws-sdk/config-resolver": 3.183.0 - "@aws-sdk/fetch-http-handler": 3.183.0 - "@aws-sdk/hash-node": 3.183.0 - "@aws-sdk/invalid-dependency": 3.183.0 - "@aws-sdk/middleware-content-length": 3.183.0 - "@aws-sdk/middleware-host-header": 3.183.0 - "@aws-sdk/middleware-logger": 3.183.0 - "@aws-sdk/middleware-recursion-detection": 3.183.0 - "@aws-sdk/middleware-retry": 3.183.0 - "@aws-sdk/middleware-serde": 3.183.0 - "@aws-sdk/middleware-stack": 3.183.0 - "@aws-sdk/middleware-user-agent": 3.183.0 - "@aws-sdk/node-config-provider": 3.183.0 - "@aws-sdk/node-http-handler": 3.183.0 - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/smithy-client": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/url-parser": 3.183.0 - "@aws-sdk/util-base64-browser": 3.183.0 - "@aws-sdk/util-base64-node": 3.183.0 - "@aws-sdk/util-body-length-browser": 3.183.0 - "@aws-sdk/util-body-length-node": 3.183.0 - "@aws-sdk/util-defaults-mode-browser": 3.183.0 - "@aws-sdk/util-defaults-mode-node": 3.183.0 - "@aws-sdk/util-user-agent-browser": 3.183.0 - "@aws-sdk/util-user-agent-node": 3.183.0 - "@aws-sdk/util-utf8-browser": 3.183.0 - "@aws-sdk/util-utf8-node": 3.183.0 + "@aws-sdk/config-resolver": 3.212.0 + "@aws-sdk/fetch-http-handler": 3.212.0 + "@aws-sdk/hash-node": 3.212.0 + "@aws-sdk/invalid-dependency": 3.212.0 + "@aws-sdk/middleware-content-length": 3.212.0 + "@aws-sdk/middleware-endpoint": 3.212.0 + "@aws-sdk/middleware-host-header": 3.212.0 + "@aws-sdk/middleware-logger": 3.212.0 + "@aws-sdk/middleware-recursion-detection": 3.212.0 + "@aws-sdk/middleware-retry": 3.212.0 + "@aws-sdk/middleware-serde": 3.212.0 + "@aws-sdk/middleware-stack": 3.212.0 + "@aws-sdk/middleware-user-agent": 3.212.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/node-http-handler": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/smithy-client": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/url-parser": 3.212.0 + "@aws-sdk/util-base64": 3.208.0 + "@aws-sdk/util-body-length-browser": 3.188.0 + "@aws-sdk/util-body-length-node": 3.208.0 + "@aws-sdk/util-defaults-mode-browser": 3.212.0 + "@aws-sdk/util-defaults-mode-node": 3.212.0 + "@aws-sdk/util-endpoints": 3.212.0 + "@aws-sdk/util-user-agent-browser": 3.212.0 + "@aws-sdk/util-user-agent-node": 3.212.0 + "@aws-sdk/util-utf8-browser": 3.188.0 + "@aws-sdk/util-utf8-node": 3.208.0 tslib: ^2.3.1 - checksum: 6909329cf87c1a0c830fa9657e04c7e1ae496d7d79cc0a6813999503ae809f245f0b879af1d5564a5d97c2c69583c8a4df702d29ec7dc75934d98a0cdf017ad3 + checksum: 45f32b8c0a39d3d6788d5a359aff1bcd42c4ada3edb4d6ef072741a9128fc726090fb7e4b5770307f106eb6470f1a29c70843b628ef1652a944cd8dcbe4e8112 languageName: node linkType: hard -"@aws-sdk/client-sts@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/client-sts@npm:3.183.0" +"@aws-sdk/client-sso@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/client-sso@npm:3.212.0" dependencies: "@aws-crypto/sha256-browser": 2.0.0 "@aws-crypto/sha256-js": 2.0.0 - "@aws-sdk/config-resolver": 3.183.0 - "@aws-sdk/credential-provider-node": 3.183.0 - "@aws-sdk/fetch-http-handler": 3.183.0 - "@aws-sdk/hash-node": 3.183.0 - "@aws-sdk/invalid-dependency": 3.183.0 - "@aws-sdk/middleware-content-length": 3.183.0 - "@aws-sdk/middleware-host-header": 3.183.0 - "@aws-sdk/middleware-logger": 3.183.0 - "@aws-sdk/middleware-recursion-detection": 3.183.0 - "@aws-sdk/middleware-retry": 3.183.0 - "@aws-sdk/middleware-sdk-sts": 3.183.0 - "@aws-sdk/middleware-serde": 3.183.0 - "@aws-sdk/middleware-signing": 3.183.0 - "@aws-sdk/middleware-stack": 3.183.0 - "@aws-sdk/middleware-user-agent": 3.183.0 - "@aws-sdk/node-config-provider": 3.183.0 - "@aws-sdk/node-http-handler": 3.183.0 - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/smithy-client": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/url-parser": 3.183.0 - "@aws-sdk/util-base64-browser": 3.183.0 - "@aws-sdk/util-base64-node": 3.183.0 - "@aws-sdk/util-body-length-browser": 3.183.0 - "@aws-sdk/util-body-length-node": 3.183.0 - "@aws-sdk/util-defaults-mode-browser": 3.183.0 - "@aws-sdk/util-defaults-mode-node": 3.183.0 - "@aws-sdk/util-user-agent-browser": 3.183.0 - "@aws-sdk/util-user-agent-node": 3.183.0 - "@aws-sdk/util-utf8-browser": 3.183.0 - "@aws-sdk/util-utf8-node": 3.183.0 - entities: 2.2.0 - fast-xml-parser: 3.19.0 + "@aws-sdk/config-resolver": 3.212.0 + "@aws-sdk/fetch-http-handler": 3.212.0 + "@aws-sdk/hash-node": 3.212.0 + "@aws-sdk/invalid-dependency": 3.212.0 + "@aws-sdk/middleware-content-length": 3.212.0 + "@aws-sdk/middleware-endpoint": 3.212.0 + "@aws-sdk/middleware-host-header": 3.212.0 + "@aws-sdk/middleware-logger": 3.212.0 + "@aws-sdk/middleware-recursion-detection": 3.212.0 + "@aws-sdk/middleware-retry": 3.212.0 + "@aws-sdk/middleware-serde": 3.212.0 + "@aws-sdk/middleware-stack": 3.212.0 + "@aws-sdk/middleware-user-agent": 3.212.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/node-http-handler": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/smithy-client": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/url-parser": 3.212.0 + "@aws-sdk/util-base64": 3.208.0 + "@aws-sdk/util-body-length-browser": 3.188.0 + "@aws-sdk/util-body-length-node": 3.208.0 + "@aws-sdk/util-defaults-mode-browser": 3.212.0 + "@aws-sdk/util-defaults-mode-node": 3.212.0 + "@aws-sdk/util-endpoints": 3.212.0 + "@aws-sdk/util-user-agent-browser": 3.212.0 + "@aws-sdk/util-user-agent-node": 3.212.0 + "@aws-sdk/util-utf8-browser": 3.188.0 + "@aws-sdk/util-utf8-node": 3.208.0 tslib: ^2.3.1 - checksum: d4492e537803d64e5fc0c5db8ac3a3788204aadc14b21e4fb407188feb58b35689c4a6878b4f13123a5f35f7e9fa01d95d427be168aeec9072aea881f2a92fb7 + checksum: 14426e200171d3ee596f946744d8ee325b3f8fbfcfb3f266482142df20ee418752b64be1131b473cf0b60ed8615dab1cd9350f93a547dca34ca84c07c47fc697 languageName: node linkType: hard -"@aws-sdk/config-resolver@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/config-resolver@npm:3.183.0" +"@aws-sdk/client-sts@npm:3.213.0": + version: 3.213.0 + resolution: "@aws-sdk/client-sts@npm:3.213.0" dependencies: - "@aws-sdk/signature-v4": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-config-provider": 3.183.0 - "@aws-sdk/util-middleware": 3.183.0 + "@aws-crypto/sha256-browser": 2.0.0 + "@aws-crypto/sha256-js": 2.0.0 + "@aws-sdk/config-resolver": 3.212.0 + "@aws-sdk/credential-provider-node": 3.212.0 + "@aws-sdk/fetch-http-handler": 3.212.0 + "@aws-sdk/hash-node": 3.212.0 + "@aws-sdk/invalid-dependency": 3.212.0 + "@aws-sdk/middleware-content-length": 3.212.0 + "@aws-sdk/middleware-endpoint": 3.212.0 + "@aws-sdk/middleware-host-header": 3.212.0 + "@aws-sdk/middleware-logger": 3.212.0 + "@aws-sdk/middleware-recursion-detection": 3.212.0 + "@aws-sdk/middleware-retry": 3.212.0 + "@aws-sdk/middleware-sdk-sts": 3.212.0 + "@aws-sdk/middleware-serde": 3.212.0 + "@aws-sdk/middleware-signing": 3.212.0 + "@aws-sdk/middleware-stack": 3.212.0 + "@aws-sdk/middleware-user-agent": 3.212.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/node-http-handler": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/smithy-client": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/url-parser": 3.212.0 + "@aws-sdk/util-base64": 3.208.0 + "@aws-sdk/util-body-length-browser": 3.188.0 + "@aws-sdk/util-body-length-node": 3.208.0 + "@aws-sdk/util-defaults-mode-browser": 3.212.0 + "@aws-sdk/util-defaults-mode-node": 3.212.0 + "@aws-sdk/util-endpoints": 3.212.0 + "@aws-sdk/util-user-agent-browser": 3.212.0 + "@aws-sdk/util-user-agent-node": 3.212.0 + "@aws-sdk/util-utf8-browser": 3.188.0 + "@aws-sdk/util-utf8-node": 3.208.0 + fast-xml-parser: 4.0.11 tslib: ^2.3.1 - checksum: 1be0ab79d5019f7502a7aee8c740f4e381ab7501ed639569526a4a7e1c70ad6bb36d642a8a2b18afe58c34497c6f2f8244dbf05211a0f6bc9a57e5e379c70b2b + checksum: 6d375d8d22bdd08130ea624d6b980e366ce556b3178a4dbfcfd7ec7b9ab629dd57d34b350129231bd96be078778df0de6c61783c49a739142b055882f943f7d2 languageName: node linkType: hard -"@aws-sdk/credential-provider-env@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-env@npm:3.183.0" +"@aws-sdk/config-resolver@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/config-resolver@npm:3.212.0" dependencies: - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/signature-v4": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-config-provider": 3.208.0 + "@aws-sdk/util-middleware": 3.212.0 tslib: ^2.3.1 - checksum: 4281e14c286cc5c57c69ba88c49fe1c1ff61f27923db5e8f852a84cce02b40fdc938f876ad08e72005deae9a28fab7400f747f63ca50f9c4d60e04e6c2705205 + checksum: 2d2844b93ca27a60d311d6998582bba99ac922299db76309f6136625ffaeecba172b906dee2598c381aebd5c88ca5b5442e5dfdcaa932ddaaf88d7257953022a languageName: node linkType: hard -"@aws-sdk/credential-provider-imds@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-imds@npm:3.183.0" +"@aws-sdk/credential-provider-env@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.212.0" dependencies: - "@aws-sdk/node-config-provider": 3.183.0 - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/url-parser": 3.183.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: e9fe1007b22825dc0c033beb2057b9f759d8cafec6e65d08ba4915bccb9b7d737eeccb3d06e9768a084b6726d9ea10778172b1bec695353c60276c1a5c21370e + checksum: da7af6765a551b9c55c56d5e1f287877839aec8cfbe301d4b483180a72f62113e15b87c8b4dbbe608e06db96bcad52b3becc9f02f3664607f0c477d03fef1bac languageName: node linkType: hard -"@aws-sdk/credential-provider-ini@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-ini@npm:3.183.0" +"@aws-sdk/credential-provider-imds@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-imds@npm:3.212.0" dependencies: - "@aws-sdk/credential-provider-env": 3.183.0 - "@aws-sdk/credential-provider-imds": 3.183.0 - "@aws-sdk/credential-provider-sso": 3.183.0 - "@aws-sdk/credential-provider-web-identity": 3.183.0 - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/shared-ini-file-loader": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/url-parser": 3.212.0 tslib: ^2.3.1 - checksum: c7784a3674c863dacc1305a08dd63cb16d1e7374250b0ceafa9feee8344de4228d7c32e84fbaa9d963adab69eb3938cd75b99554756da120cd9beae4693eded9 + checksum: 4b3c31bc88a1bf15d0721c7e124795361c6f532ee1b33400f2f71393d60ee5c14b0bf2b4e2abc6c3ef22aa8304aa19e5d9e3e733e17da454e3371615567440dc languageName: node linkType: hard -"@aws-sdk/credential-provider-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-node@npm:3.183.0" +"@aws-sdk/credential-provider-ini@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.212.0" dependencies: - "@aws-sdk/credential-provider-env": 3.183.0 - "@aws-sdk/credential-provider-imds": 3.183.0 - "@aws-sdk/credential-provider-ini": 3.183.0 - "@aws-sdk/credential-provider-process": 3.183.0 - "@aws-sdk/credential-provider-sso": 3.183.0 - "@aws-sdk/credential-provider-web-identity": 3.183.0 - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/shared-ini-file-loader": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/credential-provider-env": 3.212.0 + "@aws-sdk/credential-provider-imds": 3.212.0 + "@aws-sdk/credential-provider-sso": 3.212.0 + "@aws-sdk/credential-provider-web-identity": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/shared-ini-file-loader": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: f23e1148054a96d68632284501ba01c4285cce1a2f4562817ec4b7fa315ff56e2f6b97fae0d2ae26fd52fca8c9f000be5d72a3582cff6c32ba10010f02a30977 + checksum: de60d4729ed6e162628563990b08be88486131c09ec372190a9b38faea72d8969d1e277b2e01a28fa3432c34e92bd39057fc6b923de9fcd0fcabd5b9ec69425e languageName: node linkType: hard -"@aws-sdk/credential-provider-process@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-process@npm:3.183.0" +"@aws-sdk/credential-provider-node@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.212.0" dependencies: - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/shared-ini-file-loader": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/credential-provider-env": 3.212.0 + "@aws-sdk/credential-provider-imds": 3.212.0 + "@aws-sdk/credential-provider-ini": 3.212.0 + "@aws-sdk/credential-provider-process": 3.212.0 + "@aws-sdk/credential-provider-sso": 3.212.0 + "@aws-sdk/credential-provider-web-identity": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/shared-ini-file-loader": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 3109e79ec2b4647f5e7f78c6dc2e10cd8a34a70147e7e90549153b7ff3ff12eda3499b3189d6fc1f3b99f461d5445e8017b44cd50029d30b0fc7437ad88a5f38 + checksum: 421a65b57cfd280a14707c8563da285263a55471d1c3fbfe66305f0d42dcc59fb2884e84d380b8bcf2fea0ccdd419e92b5361b5b2a5832cf45ca0c86127a0137 languageName: node linkType: hard -"@aws-sdk/credential-provider-sso@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-sso@npm:3.183.0" +"@aws-sdk/credential-provider-process@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.212.0" dependencies: - "@aws-sdk/client-sso": 3.183.0 - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/shared-ini-file-loader": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/shared-ini-file-loader": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 8775894ef50499361a4b49d846417c2c1e1ea90383730a32428154628358e7847f6f29dddd3cd04d3a4e15c1343fe10b051a92863055989ac3c4cb241e68f63a + checksum: 4842577d8c7b2daa80db20f5a21e56a42a57de9c5d27c50806a2c76e5b0eb0fa9d6b5aed9dcae1d0820ce410999e139f81b0440d33dc104cbc357a3f59680d8f languageName: node linkType: hard -"@aws-sdk/credential-provider-web-identity@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/credential-provider-web-identity@npm:3.183.0" +"@aws-sdk/credential-provider-sso@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.212.0" dependencies: - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/client-sso": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/shared-ini-file-loader": 3.212.0 + "@aws-sdk/token-providers": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: c39576c8f35137284fc69db8d94adcf07fa89d51153ee9710481262e205d485ff089a796e8a253aa14a0434dde91eadb65602fc614167a4f14301201ee840c08 + checksum: b575af71e26343536997261ac97820afa697b789b152106f64fea1e24fe6a21437a195f46a23828b92dbd82a32d7c4339d834a0aa52dd2e075974363c1f8837f languageName: node linkType: hard -"@aws-sdk/fetch-http-handler@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/fetch-http-handler@npm:3.183.0" +"@aws-sdk/credential-provider-web-identity@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.212.0" dependencies: - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/querystring-builder": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-base64-browser": 3.183.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 87c51d1dad8c469e32b99f6ed3729e753097cd4a9f8f417ec8681cf01e18b9a5dc6571f3cd1220293216bd4527b8499bb2ea1f821a240fde6d9560817c120066 + checksum: 1bef8b133fed07802e82406d3e46d7bdd110197b1416debd8953f7192814ebf2a6300520adfe62a9b68bc84fab70fd97fd3f4126855c297b8bbb947ce97d4184 languageName: node linkType: hard -"@aws-sdk/hash-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/hash-node@npm:3.183.0" +"@aws-sdk/fetch-http-handler@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/fetch-http-handler@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-buffer-from": 3.183.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/querystring-builder": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-base64": 3.208.0 tslib: ^2.3.1 - checksum: fbed8757df633620807594535e3bb7d387576e0c2f89ad16dc7c98405a9d1d9743bea50148186de2f19cac67144b30f7f89e6c15317ddd7b8c0c8f61ca9556ef + checksum: 477601560df9a58c8e6bb50cdc313205fc75681699275f153e96ce6c62cf1c55ec8c394bb4774ba69a483987094b7416aa8e3fb5c89cdd530adb25e1cd909cb9 languageName: node linkType: hard -"@aws-sdk/invalid-dependency@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/invalid-dependency@npm:3.183.0" +"@aws-sdk/hash-node@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/hash-node@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-buffer-from": 3.208.0 tslib: ^2.3.1 - checksum: d928ca26d4390ad4f5e2c8198d16e14b675face6ad8c4a4eef699b54ce3f6cba584456659287b0e57e9f9019bc456b51dfa474671e11bf555fe04ed16174c10c + checksum: f5855ff20e37a0779da515fa78f26413762a5246c1b06a4e35f11f7aa4fd742a639a2b3d00f63004a5de659f233c5d503b469a744bb6911f9159602a79676789 languageName: node linkType: hard -"@aws-sdk/is-array-buffer@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/is-array-buffer@npm:3.183.0" +"@aws-sdk/invalid-dependency@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/invalid-dependency@npm:3.212.0" dependencies: + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 717e508989821434a2ace4f690fc599f67b3f28ee9de5f06fce65a296fe11a68a5b97f30d66bfbf490d4a83259e4ffa1f0321231e4a54ce2440a9c6fdbacde24 + checksum: c71f32895f2a7760a916d8b8abba8b3d2c40869422d083da13abb096931ed04d67a65695f024ce74efced33745a10c9f904779f1414d2fc9a80be3d1d95284f9 languageName: node linkType: hard -"@aws-sdk/md5-js@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/md5-js@npm:3.183.0" +"@aws-sdk/is-array-buffer@npm:3.201.0": + version: 3.201.0 + resolution: "@aws-sdk/is-array-buffer@npm:3.201.0" dependencies: - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-utf8-browser": 3.183.0 - "@aws-sdk/util-utf8-node": 3.183.0 tslib: ^2.3.1 - checksum: 5bd92465940ace6b3ca13342bd60eab42ffd5d74dfa65655c28e10bbf4aa0c32b2f9b4deb2050f3d092b8586e558bf248559af9c3cffb37810961169ad0797af + checksum: 295450b417a9ab0b734050afff6c53aaed8a33dccd3ede60bf67fdec21f675d14ab8edc24f4e1d12aa4e99f9ccaf794aaaaff270c296c1ee38f73ea7ba7f59ce languageName: node linkType: hard -"@aws-sdk/middleware-content-length@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-content-length@npm:3.183.0" +"@aws-sdk/md5-js@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/md5-js@npm:3.212.0" dependencies: - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-utf8-browser": 3.188.0 + "@aws-sdk/util-utf8-node": 3.208.0 tslib: ^2.3.1 - checksum: bf3ec6872a925dbafd91e57334b6264e8446ca59eba598ee4bc7b37113c466300ac6b77ab314c4aa2859221f1f4e98beabbd365346e0815d591f276e642d1849 + checksum: d49b7eb9b3b78bbc78c247565041ea4119a6c82765e5b989a716d31def4da4de69f5dcbaf1ed32a89f23a1484e233a51a73f90da877952510250ca0a2c7311a1 languageName: node linkType: hard -"@aws-sdk/middleware-host-header@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-host-header@npm:3.183.0" +"@aws-sdk/middleware-content-length@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-content-length@npm:3.212.0" dependencies: - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: b4c4886976cffec614ff368daa995a69c765e737c8d366daa0033d781cf8e7483565bb3627ecd752698edc23dcf578de6657c9e8cb41c0fcd92631d17b7749b8 + checksum: ef6a5467b84205b94ee90761d72cf14d52d49392086fb6358e07940b062a9f6e514b6649a9e0e87f0b3305cf54f5d85b712f394843a4417c590f630abeba2cdd languageName: node linkType: hard -"@aws-sdk/middleware-logger@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-logger@npm:3.183.0" +"@aws-sdk/middleware-endpoint@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-endpoint@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/middleware-serde": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/signature-v4": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/url-parser": 3.212.0 + "@aws-sdk/util-config-provider": 3.208.0 + "@aws-sdk/util-middleware": 3.212.0 tslib: ^2.3.1 - checksum: 10ffb6af0f81b0144fef91af2bbeaeb680df2beff838f2a6ebcc260971fd6252a166e3bf3501c1fc00f6f8f203117ff6e2b4baebb7a6d8cd8504b7e1f392bd6d + checksum: 0b34bd25dfaf05d009ba327abb00916c1a5ffa9c58b1baecaee120468ddf3aa4b7998b8b1ae33fca789494fe95a3bc0921050d667da396552239d542ae030675 languageName: node linkType: hard -"@aws-sdk/middleware-recursion-detection@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-recursion-detection@npm:3.183.0" +"@aws-sdk/middleware-host-header@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.212.0" dependencies: - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 461117e0dc8c38750942035a07163817bc8e89f377211f5fb52d4314acae4e7e6cb49be04193c508112833535b33814c1609196cb65e38cff1122043398ad89e + checksum: c852de97a8763452b31136fc7ece3ba3026603665204a2982781a600139e346f08ca146331735d377b1f04a662bae9d44b78deef797d5bbee366f56ee91d7bc6 languageName: node linkType: hard -"@aws-sdk/middleware-retry@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-retry@npm:3.183.0" +"@aws-sdk/middleware-logger@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-logger@npm:3.212.0" dependencies: - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/service-error-classification": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-middleware": 3.183.0 + "@aws-sdk/types": 3.212.0 + tslib: ^2.3.1 + checksum: 1bc12d88da134e6cfa9ecf102c7a80727f1f00e09e97f06d44f4a6d1da21711a7abf9f0bdf39ab789736925f97f781574cb1e49f275f7ea1112fc0db443753f2 + languageName: node + linkType: hard + +"@aws-sdk/middleware-recursion-detection@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.212.0" + dependencies: + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/types": 3.212.0 + tslib: ^2.3.1 + checksum: 65cb31460dfd88a5997764d84e23003264c639b7848ad9398f9f362a7214ef175431e833dff4bf501bb81cb9b695e146c042b02986dfa6fab8812058bca7f9c7 + languageName: node + linkType: hard + +"@aws-sdk/middleware-retry@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-retry@npm:3.212.0" + dependencies: + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/service-error-classification": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-middleware": 3.212.0 tslib: ^2.3.1 uuid: ^8.3.2 - checksum: 76fa3f9fe5f1c67bbfa792611289edf0afa2d0271073039931ac9ea46b3f432d74692d2db55443f40b7e97014898454f6d4bd8d2f2f5714a4281428b7464efac + checksum: 1e221bf49585d9fea675f83d8b5d18f4b20de5b4096cc94dc51d265bdd579aa205b11a0735954dc9d7990f33fbe1165304342480ab15b79431d7988b74efcf05 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-sqs@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.183.0" +"@aws-sdk/middleware-sdk-sqs@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-hex-encoding": 3.183.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-hex-encoding": 3.201.0 tslib: ^2.3.1 - checksum: 0bb914b2e7a5391f5b22a5262a5c299fa7e3eb22076d00c4cfffb449e97404b4830a77bb742e8f3110d66f15ae9aad81ea740238799cc35bb8d328eaf13f3fe2 + checksum: 12ab5d80d8025a737aa3e5c7f8025bbf824fe2719618356b011fb0901135828f0ee10eb15b7bf0ad81e4de3159c8a6bba97c3b3cfc186e26bd6dbefab16682a5 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-sts@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-sdk-sts@npm:3.183.0" +"@aws-sdk/middleware-sdk-sts@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-sdk-sts@npm:3.212.0" dependencies: - "@aws-sdk/middleware-signing": 3.183.0 - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/signature-v4": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/middleware-signing": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/signature-v4": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 1969d6128d24cc7d8ff199c2900d805281928668f4dc3ffd128065ccfe515f9f7467a265af276592009bd285847cb9298f8ba4b1dfbe33971c3e7de80986b422 + checksum: af3a03802e07ed1375a6d7feb6a9abbec8cba175138c49b8fb4d42843330db65e550a505539765a1f65944a3a4a840259b503d9e4a28f210ea83c0f2683e5e27 languageName: node linkType: hard -"@aws-sdk/middleware-serde@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-serde@npm:3.183.0" +"@aws-sdk/middleware-serde@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-serde@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 7495bb2adceab2c686588697fd9dd50a859706a8d33d98d9907ffc8f9ef78af141f2654cdac8d7b28b1f0544dcf2e52f25d109f5da1348102234ab5b4e79b256 + checksum: 1d9438e1e3a177a30d39787a6493f15a9726dd820552a995ca771ee58df004f6575ed322fd59658a7478c4fe72dfaf40bb61b1fd441a6ec1a245370e4ef17da9 languageName: node linkType: hard -"@aws-sdk/middleware-signing@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-signing@npm:3.183.0" +"@aws-sdk/middleware-signing@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-signing@npm:3.212.0" dependencies: - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/signature-v4": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-middleware": 3.183.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/signature-v4": 3.212.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-middleware": 3.212.0 tslib: ^2.3.1 - checksum: 8025499acfefc072511adbc5084a1ed3cfe9cd9e514c58045425c87538af72ad3adfc134f7d0a059ff52832da392b4679b62d4ad28e477b866a20e78370f0758 + checksum: f9e2c07a9e80bc67ea05766f505c750652a2d355eb98a2f2296707a3af15bb2d591e8257a9aa3683721c47814e49fd3b266746d95ea8cb362eaaf8ab4afba08d languageName: node linkType: hard -"@aws-sdk/middleware-stack@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-stack@npm:3.183.0" +"@aws-sdk/middleware-stack@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-stack@npm:3.212.0" dependencies: tslib: ^2.3.1 - checksum: 03136847b96b0e4baba9b64f53a6c6b5738b1fa2c645edb3f47c1f85082791549b423ea791af33e9da3507cd56fbcbd052f1ba773a9f45ea42a3647277e85264 + checksum: b9ce8fdae58f68f259aea823e39f22b8f893402bad25d4c87f614e0dc14412d66bc3c08607190856b9122f2d431f94a78dd8ddec7700f5afe8d956ff12913a00 languageName: node linkType: hard -"@aws-sdk/middleware-user-agent@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/middleware-user-agent@npm:3.183.0" +"@aws-sdk/middleware-user-agent@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.212.0" dependencies: - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 8d1001d6d30f0f56aa440c541f6dcee9aad9190837e7a9653aef35448ff13e644cb7a441373192134ac86b5741fa64a120add07202cb97674018bd44612acdf7 + checksum: ef18b7b7755190e864946156dd741f7b513efaea160f4a4e2e2088e609d51660bd5dc577fb3e78bebc6105741b3b6e66a06bc77c0e027f4096fca90b918cb8b0 languageName: node linkType: hard -"@aws-sdk/node-config-provider@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/node-config-provider@npm:3.183.0" +"@aws-sdk/node-config-provider@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/node-config-provider@npm:3.212.0" dependencies: - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/shared-ini-file-loader": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/shared-ini-file-loader": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 673fb105de8d11ddd1c3e636c7ed01ef4fbe412a7d96543d9db69149dfad64d33adf5b96b106763c058190841ea497f220e1b75ca438dbda7b34729f00b9e823 + checksum: c8e742c225ea4803d0f3318de8d93f756e5008e76ecc2d88fcb80e6733b29984b40d39a47a08d51fa23c00b0dbf3f0bd3b60eb44768abb48466a2493a61195ed languageName: node linkType: hard -"@aws-sdk/node-http-handler@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/node-http-handler@npm:3.183.0" +"@aws-sdk/node-http-handler@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/node-http-handler@npm:3.212.0" dependencies: - "@aws-sdk/abort-controller": 3.183.0 - "@aws-sdk/protocol-http": 3.183.0 - "@aws-sdk/querystring-builder": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/abort-controller": 3.212.0 + "@aws-sdk/protocol-http": 3.212.0 + "@aws-sdk/querystring-builder": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: a24ee8cc4e3b63fdec0ebb671caa1e634af488befa7c93a28bb733bb532934c221f6c47707db20264af91ca84336a50b98427b425329b89aeac2a6d2be9537ff + checksum: 052e4a3854823bd3877fc2baeca8fae9cdb2083beb96b4734f08eba70cb73c7abaf5dc6487d5ace0757257debc56f656d9da247d227669196d3e20a1fa97563d languageName: node linkType: hard -"@aws-sdk/property-provider@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/property-provider@npm:3.183.0" +"@aws-sdk/property-provider@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/property-provider@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 6e8616e5f7efa9f46bdf62118dfc586e61dcdf0bc99f82305759020ce3d25fc1d2db71c62cfd4417a757a138b1a539cf305b584e6e7ade9bce9975fbe66fd326 + checksum: ac30dd5777f2cce6aaa9c3ac2672bada2f8423330c545ea66913d731b6e0f48a802b9f15c48ccb7620dd0f9e8c4c3a9d27bd445a7c9d72bb30115c1954207234 languageName: node linkType: hard -"@aws-sdk/protocol-http@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/protocol-http@npm:3.183.0" +"@aws-sdk/protocol-http@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/protocol-http@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 13b255bcb37b70853d93374f972d743c80f33ac16188c41b512536c2dbec4d7beb55226bfac6d809981d63bbc87f6bf345334238f187da2faa9feaef5001e0a7 + checksum: 6330d3259a560e952202ae9b964c210ab1b25b9161d4fc27da1bc081600abe2461854659a3b560764c8e2d1e8c1212bcc061250945f1f9c0ee3a19475fb17464 languageName: node linkType: hard -"@aws-sdk/querystring-builder@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/querystring-builder@npm:3.183.0" +"@aws-sdk/querystring-builder@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/querystring-builder@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-uri-escape": 3.183.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-uri-escape": 3.201.0 tslib: ^2.3.1 - checksum: a3de19863ccee4e8c9d5bbd719688ee1ffea02c3d90aad5e9c3eb04bed942ff8e5be0bfbc21ed03a968481abe2a2b1093e93b986b4111c468a9f3d3466b61f83 + checksum: 0bbd748f548c695d040b9ed225a2eb3fbba65b767acdba8dd8e157988b4ea66d22b994721d6eb5afcbfef7c49b105ab3a779534f4b2ae7e0d266f84159511e23 languageName: node linkType: hard -"@aws-sdk/querystring-parser@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/querystring-parser@npm:3.183.0" +"@aws-sdk/querystring-parser@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/querystring-parser@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: f05a03c46317c97f1b775f7c5be19365cf464396edc6b23c93efd5289ff1a8658883f7c9dde2cc7b74bb42b57f257bc212abae5d62100d23cdec5409fbffe468 + checksum: ab73e0d3a4fd94cc5141fe65940ee700bc6d3497f68518687c196b415cac5f7e44fd71039f6b7abb6eafb7777fe85b7d8f7cb006238b8deaf1248af2a53d7e29 languageName: node linkType: hard -"@aws-sdk/service-error-classification@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/service-error-classification@npm:3.183.0" - checksum: 8c015163aee9ec4d789daa8605e5b9424dfda81d3f609a2c34d800cb36ee78f4a3af02d1738c7e75f9a62cd1ef8ec4307f77457eecba3de4c6565a981484bca4 +"@aws-sdk/service-error-classification@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/service-error-classification@npm:3.212.0" + checksum: 844f149d4a03c72ffa77103c8ce12b58ecb20484350633e88242e8d16cde50e3f35ba6db34ea7e180685d4545e0f48367ab8ce86d76dfb80e5d2a959749556be languageName: node linkType: hard -"@aws-sdk/shared-ini-file-loader@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/shared-ini-file-loader@npm:3.183.0" +"@aws-sdk/shared-ini-file-loader@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/shared-ini-file-loader@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 317fd0d8012559754f82b88fb386d2a01c19be1d3a0b6da3a6436f71701ed8cd8fbe3f5aa869fd7bbfb8dfb148cd9de02aa68e93001200c76ba9e0691e135773 + checksum: 2c4f78b16338090d86ddc701f7ee13974387762b5f9c82b573b405a6e08db9bb979ac27809ac321cd8c33a3392c1576860475a47470cd59a1cb6092889d6ac17 languageName: node linkType: hard -"@aws-sdk/signature-v4@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/signature-v4@npm:3.183.0" +"@aws-sdk/signature-v4@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/signature-v4@npm:3.212.0" dependencies: - "@aws-sdk/is-array-buffer": 3.183.0 - "@aws-sdk/types": 3.183.0 - "@aws-sdk/util-hex-encoding": 3.183.0 - "@aws-sdk/util-middleware": 3.183.0 - "@aws-sdk/util-uri-escape": 3.183.0 + "@aws-sdk/is-array-buffer": 3.201.0 + "@aws-sdk/types": 3.212.0 + "@aws-sdk/util-hex-encoding": 3.201.0 + "@aws-sdk/util-middleware": 3.212.0 + "@aws-sdk/util-uri-escape": 3.201.0 tslib: ^2.3.1 - checksum: 6639a4925b194171907fc1cf61101470836f5a8fa363f3fcd7b70bbaa4c756f43e13ad93b575885218a68192c375295d359a7e91db370816bd8f39f9c158ba36 + checksum: dcbdb41fcb53c3bb890c26175716c9edb1da1450333a88c888742cd71b9d47a4d8bd1ce0c183115c5971ce3b152ea420ffd36ab6be6dd8e4ea285d66259a0ae5 languageName: node linkType: hard -"@aws-sdk/smithy-client@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/smithy-client@npm:3.183.0" +"@aws-sdk/smithy-client@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/smithy-client@npm:3.212.0" dependencies: - "@aws-sdk/middleware-stack": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/middleware-stack": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 70866adfd91fb35721bcd8667d9a4638e306de6ddc22be37a5b3ea19dfc2a1404a7325992f7f56d635a3bfd3b0ffa159d32f17df35c47ada4724ad8d5bb8d1ce + checksum: ecaa51cd72e0ab8f92f3989ba7dd5639082ef1db1832b155adc1d6e97d15d7f67ff7d27af165a487d11e862e84010b60e4744561925146dbe1d7307a7f50b00b languageName: node linkType: hard -"@aws-sdk/types@npm:3.183.0, @aws-sdk/types@npm:^3.1.0, @aws-sdk/types@npm:^3.110.0": - version: 3.183.0 - resolution: "@aws-sdk/types@npm:3.183.0" - checksum: ab6e888ef8f6d5f5c047dbe0899ac94ad4aa83b9ae1b1fd7a3b88eacec61ab912f215fda65c71f783418d569d1088641806f6b98c9a28ddae04cc70d0eeccea7 - languageName: node - linkType: hard - -"@aws-sdk/url-parser@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/url-parser@npm:3.183.0" +"@aws-sdk/token-providers@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/token-providers@npm:3.212.0" dependencies: - "@aws-sdk/querystring-parser": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/client-sso-oidc": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/shared-ini-file-loader": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 54c99d47e927368db12b6aa1fe4331a27461502355e0c18e99d8e05d9a114529d0b15965ff885cae8e19e49692ad44bf3c323ffdc535d7129deac900f286d1d5 + checksum: be11407716a1f2891b64201475c085cfb8236df0075c22f3381c3e7ed9abe0f231017826935afd76c6b41ec1907d24e3c5e1e4537fd4c0d9c743ca1e6d127768 languageName: node linkType: hard -"@aws-sdk/util-base64-browser@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-base64-browser@npm:3.183.0" +"@aws-sdk/types@npm:3.212.0, @aws-sdk/types@npm:^3.1.0, @aws-sdk/types@npm:^3.110.0": + version: 3.212.0 + resolution: "@aws-sdk/types@npm:3.212.0" + checksum: ba085aa2f70930adc250b4d142563cf886b9cbd98c011abe9d1d8dbad8b9cc8558a54d34bbeabed62a1a958a871c8d9d6cdaf43d57c31af8fc03a35a93ae5fb4 + languageName: node + linkType: hard + +"@aws-sdk/url-parser@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/url-parser@npm:3.212.0" + dependencies: + "@aws-sdk/querystring-parser": 3.212.0 + "@aws-sdk/types": 3.212.0 + tslib: ^2.3.1 + checksum: cd1c837a255d38a84d453226bab8af749a5a1c7075f70eb80124cc92c3b7301c3bc11eec624ee0d68b3daa68162676eabccfae7d291f0e3ad916c735b531cf87 + languageName: node + linkType: hard + +"@aws-sdk/util-base64@npm:3.208.0": + version: 3.208.0 + resolution: "@aws-sdk/util-base64@npm:3.208.0" + dependencies: + "@aws-sdk/util-buffer-from": 3.208.0 + tslib: ^2.3.1 + checksum: 2ccab3453a3a3636f3f1397441574b3adb984e1ba3865030393108327ed7304cf80c9b31d69691e6aba85cfe6a611a881bbb724e544324240763bb4e96630ed9 + languageName: node + linkType: hard + +"@aws-sdk/util-body-length-browser@npm:3.188.0": + version: 3.188.0 + resolution: "@aws-sdk/util-body-length-browser@npm:3.188.0" dependencies: tslib: ^2.3.1 - checksum: 2c3a4dc7e285b146285ac23de155de5e885d96aa46b29f9c830b6f12541032c6a42be7fd412ec019ddb83e6f155f561d04065b99159515d26d28c0968965dcf2 + checksum: 1b08bd1e63ec843ee336f51d894c49bf3c4c2f96e50d1711a12f7d0c5b6f7a15b490e366fec55b63e77036002994bac12927b29de2eb9ac91e4f152b1af78e58 languageName: node linkType: hard -"@aws-sdk/util-base64-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-base64-node@npm:3.183.0" - dependencies: - "@aws-sdk/util-buffer-from": 3.183.0 - tslib: ^2.3.1 - checksum: d8d0330aa768f816411a7b2343c500728501775d2f8d7c121d52d8484cbdd938102f3049e69e28574c838416ead0cb742494140609287b8d462bfb041218600d - languageName: node - linkType: hard - -"@aws-sdk/util-body-length-browser@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-body-length-browser@npm:3.183.0" +"@aws-sdk/util-body-length-node@npm:3.208.0": + version: 3.208.0 + resolution: "@aws-sdk/util-body-length-node@npm:3.208.0" dependencies: tslib: ^2.3.1 - checksum: 294074e20e4eb46ea992fc9d50fac0b97875d1d09efa706d5298e34f4200226c90bb33bbe96c41c7e91a43fbe44e6cfc0654d09fcdb823fe579545ada65701d9 + checksum: 986b42b358656dec4e75c231213331c4f01785f9ab17c8b87b6e268b6880818a96117f1785cef9786e6c0f7e2c1332c80e8388a43bfd83e8c7224ad059a72733 languageName: node linkType: hard -"@aws-sdk/util-body-length-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-body-length-node@npm:3.183.0" +"@aws-sdk/util-buffer-from@npm:3.208.0": + version: 3.208.0 + resolution: "@aws-sdk/util-buffer-from@npm:3.208.0" + dependencies: + "@aws-sdk/is-array-buffer": 3.201.0 + tslib: ^2.3.1 + checksum: 00bfa4d4494d3a1eb128e19104994d1aca8b3802e9aa218cecafb1ed3ff2ecf5c946485e06aa97ae312458842b0f31a6484dc945232f7cb0e357ba341cb2e53e + languageName: node + linkType: hard + +"@aws-sdk/util-config-provider@npm:3.208.0": + version: 3.208.0 + resolution: "@aws-sdk/util-config-provider@npm:3.208.0" dependencies: tslib: ^2.3.1 - checksum: 60697959c63731d6b4b334bed91d08c4e38e09c0ad95000509641370249c76c5df1ffa0a7cab7418d250a526f49e660f0c67cea28547e442ac47feb7623413b8 + checksum: 97b0414b120b4eb53001f3ab2135ee94937e47bd7bd0d0de7c6a7e00a282eaa78cd84be2bfd3e389340f0c0b2f7ba60da9a403f084721970ee55b779ecf7a451 languageName: node linkType: hard -"@aws-sdk/util-buffer-from@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-buffer-from@npm:3.183.0" +"@aws-sdk/util-defaults-mode-browser@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/util-defaults-mode-browser@npm:3.212.0" dependencies: - "@aws-sdk/is-array-buffer": 3.183.0 - tslib: ^2.3.1 - checksum: 5f16145cf68b0756f0930990cfba57ac343c83c6e9dc48bc8d4f488cd66548a9e186e702fa94ba33b69d852c2d019eedf4116ac676cf0dfb701081e39e0776dd - languageName: node - linkType: hard - -"@aws-sdk/util-config-provider@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-config-provider@npm:3.183.0" - dependencies: - tslib: ^2.3.1 - checksum: 8af6bf5ba31ec0a2fcf994f0a13bde5ff948bcdba02d153cdeea2105ea5eaf934368456bc92f35bfa42a7475cd4d2d2b9a644dfb8b4c2670ae2f103c045dc8b4 - languageName: node - linkType: hard - -"@aws-sdk/util-defaults-mode-browser@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-defaults-mode-browser@npm:3.183.0" - dependencies: - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/types": 3.212.0 bowser: ^2.11.0 tslib: ^2.3.1 - checksum: 8ff54acf1684dd9d82ca13a522c5d2d7ee305511a2748f6e8ef7dc43e668798269e51455ac81b905a5b000a704fd5536f178c84425b8c431500e6a8dc1418135 + checksum: 75fd275426a1708b0d353e497951a78d8b36297aa644342f063fcf26e321e6387944fe3d60beaa4d5fc40fa877b14277db0368a9d6120e6b83d56c84972c36b3 languageName: node linkType: hard -"@aws-sdk/util-defaults-mode-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-defaults-mode-node@npm:3.183.0" +"@aws-sdk/util-defaults-mode-node@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/util-defaults-mode-node@npm:3.212.0" dependencies: - "@aws-sdk/config-resolver": 3.183.0 - "@aws-sdk/credential-provider-imds": 3.183.0 - "@aws-sdk/node-config-provider": 3.183.0 - "@aws-sdk/property-provider": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/config-resolver": 3.212.0 + "@aws-sdk/credential-provider-imds": 3.212.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/property-provider": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 - checksum: 40665d251844c4add23e358d9171add7180e0f1fcd4bd99601f4bdd03daa2259fed2567fa200152e14c4d7b1acc1126997f5b4e74c569ee1032f9fee16e6b582 + checksum: ab2151cce3561378c3f07d78a898cdd627d097cc388785fe9e0df6f836f265bde98307ce800faaf8832a3723eff2258c00db2cc523425713b67c75ddc941b29e languageName: node linkType: hard -"@aws-sdk/util-hex-encoding@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-hex-encoding@npm:3.183.0" +"@aws-sdk/util-endpoints@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/util-endpoints@npm:3.212.0" + dependencies: + "@aws-sdk/types": 3.212.0 + tslib: ^2.3.1 + checksum: 6c1d9e47dee896e35b5a2b11c0c028d71d16ab707929901403997d30ca639a202ed0f9c19d7ff1c5affa2966813226925608c15695d0d540a593294e40afc4ef + languageName: node + linkType: hard + +"@aws-sdk/util-hex-encoding@npm:3.201.0": + version: 3.201.0 + resolution: "@aws-sdk/util-hex-encoding@npm:3.201.0" dependencies: tslib: ^2.3.1 - checksum: 1bf9577d96d01c13a3118e5d767c1e16b1f4f1c574168d78e128e0342edac30c87776340e9e14deb1e9adf539a6b2f953338adabc097fb143e870889698a6308 + checksum: a27f3365dfb1e6ece79ea34fd6e2c4540eb0084536d7300ff0ff42a7334ddf07f21958c6cfd0bbeb71361ee408e16deae2c82b7c7378b048b8e81a52c75f190a languageName: node linkType: hard @@ -1026,67 +1098,67 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-middleware@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-middleware@npm:3.183.0" +"@aws-sdk/util-middleware@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/util-middleware@npm:3.212.0" dependencies: tslib: ^2.3.1 - checksum: e1a433aebae5e74b365ecb4a57e3c583e7b89697f24b0c9edde93b8ba358e92256550329efb7c6924c0a5037e1bad2b7a1c8ed1b6d02e1aaaa35bca502923752 + checksum: d4bd527193fef871b480636aa99ab7b71a576fc2659e0a12f2ed14e48464c5267a4c6a24bb140ba88cd8205f224326479a548dc63de24b1b9b43ff3098041f7c languageName: node linkType: hard -"@aws-sdk/util-uri-escape@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-uri-escape@npm:3.183.0" +"@aws-sdk/util-uri-escape@npm:3.201.0": + version: 3.201.0 + resolution: "@aws-sdk/util-uri-escape@npm:3.201.0" dependencies: tslib: ^2.3.1 - checksum: 9d5f30821cdad15ed6912f7406f83b5b5a45358b208a9374ea944313d9853da8f4ec9881aefa5d6fe78caeaefbb0d103c8485a32f7c25e8b2cae193ea2233bb8 + checksum: 8bd751459eaab75a9b61801f3484cfa5c4e0133381ace6ec901cb9b92b1fee99beb4ef9c0f87ade59425a882ed3a280255d9b2fd8da6a6286e49efb9af8f0d55 languageName: node linkType: hard -"@aws-sdk/util-user-agent-browser@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-user-agent-browser@npm:3.183.0" +"@aws-sdk/util-user-agent-browser@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.212.0" dependencies: - "@aws-sdk/types": 3.183.0 + "@aws-sdk/types": 3.212.0 bowser: ^2.11.0 tslib: ^2.3.1 - checksum: 21349881a4069a09fb0f8c0f5a6d69e6659255008861817c5d9dada61bf032f0bd7d5735f01dbddbc310d228ea843fbec6ada9f83a4197909efd6ebadb364f6b + checksum: e55abbde07b550ff6b061376b00839ffb184e4e3238c307ce85bcf44166941cf74fc59fbd97c2e6923923b88eb5df5bbad482048271b91ca3b46e6988e85be7b languageName: node linkType: hard -"@aws-sdk/util-user-agent-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-user-agent-node@npm:3.183.0" +"@aws-sdk/util-user-agent-node@npm:3.212.0": + version: 3.212.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.212.0" dependencies: - "@aws-sdk/node-config-provider": 3.183.0 - "@aws-sdk/types": 3.183.0 + "@aws-sdk/node-config-provider": 3.212.0 + "@aws-sdk/types": 3.212.0 tslib: ^2.3.1 peerDependencies: aws-crt: ">=1.0.0" peerDependenciesMeta: aws-crt: optional: true - checksum: 727d46842053f8f19789fee1550e235adff399559ef6c9b8dd0fe9edd7ac629a9b5c5849377553bfff37deba1ba9889d0c313f059f27cfc40a33895990c1f894 + checksum: 6d5f0bb7f66c9135c23f6db5f6d58122db130d4b4261278f11a6db627057e03c76e237a98d91eef05aa328202f55e3fb7f70eba690640df025dfa07f948bf678 languageName: node linkType: hard -"@aws-sdk/util-utf8-browser@npm:3.183.0, @aws-sdk/util-utf8-browser@npm:^3.0.0": - version: 3.183.0 - resolution: "@aws-sdk/util-utf8-browser@npm:3.183.0" +"@aws-sdk/util-utf8-browser@npm:3.188.0, @aws-sdk/util-utf8-browser@npm:^3.0.0": + version: 3.188.0 + resolution: "@aws-sdk/util-utf8-browser@npm:3.188.0" dependencies: tslib: ^2.3.1 - checksum: f1c9cbafc3dcf5b04264c3708509117d5e6f6b373fb30712a0cd9d09966a2cdead0a91067a8e9513abac9b082971fd40519e421a2e046b3653ce1ede136197c2 + checksum: dacd27164aa0835888434e080b67f04510e2281560540ff73496f2d0aa73b0b7f830ec08491b35c3a51bf6214615579182aff8727e151e54a74a97a197a2ac31 languageName: node linkType: hard -"@aws-sdk/util-utf8-node@npm:3.183.0": - version: 3.183.0 - resolution: "@aws-sdk/util-utf8-node@npm:3.183.0" +"@aws-sdk/util-utf8-node@npm:3.208.0": + version: 3.208.0 + resolution: "@aws-sdk/util-utf8-node@npm:3.208.0" dependencies: - "@aws-sdk/util-buffer-from": 3.183.0 + "@aws-sdk/util-buffer-from": 3.208.0 tslib: ^2.3.1 - checksum: c37f2bd388783c33c981afcd5a6a80481cc4e019f42bc5ab10ba2dfb039a4684acb4fa30df579cb3a5b509f61b1261870b30cd966c179029adb2a42672c0a871 + checksum: 0f4c1d0d3f6eb40d92bc7163c6dcf97f90b3a8be05df46ba4f9bb7723a3e6acabf2cb0f2486411b03d22e8fcccae9e757ed02afad384ff5f44a3a659a78c5752 languageName: node linkType: hard @@ -19928,7 +20000,7 @@ __metadata: languageName: node linkType: hard -"entities@npm:2.2.0, entities@npm:^2.0.0": +"entities@npm:^2.0.0": version: 2.2.0 resolution: "entities@npm:2.2.0" checksum: 19010dacaf0912c895ea262b4f6128574f9ccf8d4b3b65c7e8334ad0079b3706376360e28d8843ff50a78aabcb8f08f0a32dbfacdc77e47ed77ca08b713669b3 @@ -21422,12 +21494,14 @@ __metadata: languageName: node linkType: hard -"fast-xml-parser@npm:3.19.0": - version: 3.19.0 - resolution: "fast-xml-parser@npm:3.19.0" +"fast-xml-parser@npm:4.0.11": + version: 4.0.11 + resolution: "fast-xml-parser@npm:4.0.11" + dependencies: + strnum: ^1.0.5 bin: - xml2js: cli.js - checksum: d9da9145f73d90c05ee2746d80c78eca4da0249dea8c81ea8f1a6e1245e62988ed4a040dbd1c7229b1e0bdcbf69d33c882e0ac337d10c7eedb159a4dc9779327 + fxparser: src/cli/cli.js + checksum: d8a08e4d5597e0fc00a86735195872eeb03008913e298830941516f3766e16ee555e2d431acc92e1dda887938edc445252ec5b59494aab60a8389888bd13719c languageName: node linkType: hard @@ -34580,6 +34654,13 @@ __metadata: languageName: node linkType: hard +"strnum@npm:^1.0.5": + version: 1.0.5 + resolution: "strnum@npm:1.0.5" + checksum: 651b2031db5da1bf4a77fdd2f116a8ac8055157c5420f5569f64879133825915ad461513e7202a16d7fec63c54fd822410d0962f8ca12385c4334891b9ae6dd2 + languageName: node + linkType: hard + "strtok3@npm:^6.2.4": version: 6.2.4 resolution: "strtok3@npm:6.2.4" From b1c63c9e195c10db30a10403f451ea6b9c688f11 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 15:16:16 +0000 Subject: [PATCH 63/68] Update dependency marked to v4.2.3 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0bc2702bd3..5eebd068e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27030,11 +27030,11 @@ __metadata: linkType: hard "marked@npm:^4.0.10, marked@npm:^4.0.14": - version: 4.1.1 - resolution: "marked@npm:4.1.1" + version: 4.2.3 + resolution: "marked@npm:4.2.3" bin: marked: bin/marked.js - checksum: 717e3357952ee53de831bf0eb110ed075bebca2376c58bcdf7ee523ef540d45308ad6d51b2c933da0968832ea4386f31c142ca65443e77c098e84f6cce73e418 + checksum: 21b9f6cc4e7695b5e1f251b7b6c98fdc29c25f7ee6c8f4d5c51654856ea36d6222e9a5a227a9b89ccba717dcf7a722022f099aa0dc7caad8e041c996906c23d0 languageName: node linkType: hard From c4bf273796fab261644391d21b8de37e8c11b762 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 16:09:15 +0000 Subject: [PATCH 64/68] Update graphqlcodegenerator monorepo Signed-off-by: Renovate Bot --- yarn.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/yarn.lock b/yarn.lock index a45895aabe..95bdb02c39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8442,18 +8442,18 @@ __metadata: linkType: hard "@graphql-codegen/graphql-modules-preset@npm:^2.3.2": - version: 2.5.5 - resolution: "@graphql-codegen/graphql-modules-preset@npm:2.5.5" + version: 2.5.6 + resolution: "@graphql-codegen/graphql-modules-preset@npm:2.5.6" dependencies: "@graphql-codegen/plugin-helpers": ^2.7.2 - "@graphql-codegen/visitor-plugin-common": 2.13.1 + "@graphql-codegen/visitor-plugin-common": 2.13.2 "@graphql-tools/utils": ^8.8.0 change-case-all: 1.0.14 parse-filepath: ^1.0.2 tslib: ~2.4.0 peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: d499001af49d0a829c6adcbc211d252e8d5a42ba1635c32265ad99d187666425150c99815d48e591ca66e811a2b09860a5f59ba02484a3a3f0dcf4dcba8a6fb8 + checksum: e446c4277f66304d34e9883c37dd241559ba47695716d8ef911514de998c927e2f37770d70b87b56238e3fed1bf43eff9ec7bce679e06fb4eb0b759c98f6cb25 languageName: node linkType: hard @@ -8487,39 +8487,39 @@ __metadata: linkType: hard "@graphql-codegen/typescript-resolvers@npm:^2.4.3": - version: 2.7.6 - resolution: "@graphql-codegen/typescript-resolvers@npm:2.7.6" + version: 2.7.7 + resolution: "@graphql-codegen/typescript-resolvers@npm:2.7.7" dependencies: "@graphql-codegen/plugin-helpers": ^2.7.2 - "@graphql-codegen/typescript": ^2.8.1 - "@graphql-codegen/visitor-plugin-common": 2.13.1 + "@graphql-codegen/typescript": ^2.8.2 + "@graphql-codegen/visitor-plugin-common": 2.13.2 "@graphql-tools/utils": ^8.8.0 auto-bind: ~4.0.0 tslib: ~2.4.0 peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: f552f74aa264c0f9024834d6d1c126f9f1a69dccf7278525c7609ad1820daf87607447f30351506fe3728a855690928ba7775edbbbff0fee610a2ce02913a98e + checksum: f7b4fcda7fa3d73a48bf93e32ee73fdd8426d1d89951b22555702a98445b110aeb0715a1ed29dc1cdee7cbe0264510d021e98b57798a51bcf07eab34c4445cf5 languageName: node linkType: hard -"@graphql-codegen/typescript@npm:^2.4.2, @graphql-codegen/typescript@npm:^2.8.1": - version: 2.8.1 - resolution: "@graphql-codegen/typescript@npm:2.8.1" +"@graphql-codegen/typescript@npm:^2.4.2, @graphql-codegen/typescript@npm:^2.8.2": + version: 2.8.2 + resolution: "@graphql-codegen/typescript@npm:2.8.2" dependencies: "@graphql-codegen/plugin-helpers": ^2.7.2 "@graphql-codegen/schema-ast": ^2.5.1 - "@graphql-codegen/visitor-plugin-common": 2.13.1 + "@graphql-codegen/visitor-plugin-common": 2.13.2 auto-bind: ~4.0.0 tslib: ~2.4.0 peerDependencies: graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 2b5466190e385c557eed6210b861fb9e632fb61b90bd0f97ff983f359c0ad8106228667c9ff2201842100417a07cfaffa91794ef03058a151df256d733363fa2 + checksum: 7e3beda7a0a8b9b84ee7c40c54913ef496bd8716ddd68028a55b06743657bc1100c4a30cc2932745520f5e008aa3f386731bf09a88e05cc92005e066b57b21c8 languageName: node linkType: hard -"@graphql-codegen/visitor-plugin-common@npm:2.13.1": - version: 2.13.1 - resolution: "@graphql-codegen/visitor-plugin-common@npm:2.13.1" +"@graphql-codegen/visitor-plugin-common@npm:2.13.2": + version: 2.13.2 + resolution: "@graphql-codegen/visitor-plugin-common@npm:2.13.2" dependencies: "@graphql-codegen/plugin-helpers": ^2.7.2 "@graphql-tools/optimize": ^1.3.0 @@ -8533,7 +8533,7 @@ __metadata: tslib: ~2.4.0 peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 0c329aa6e435602f2f6c1569ec2091b7850f58cc5dca7ac763c38c82588545ec1110c1de587f5f3949b11ff96f94401d1e63e329607d78424583b276fd08f1ae + checksum: b4b00f9d0cd8be221da69480d0d861f025eb7bec13aa9897acb9a3a485b5e0ca04df17745fa9850efcafefe4de54dab2e4d50dafc534df3fecd9095e6122eaa7 languageName: node linkType: hard From 2318245d91cd171f16a324eef29dab5714b3bcf6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 17:07:26 +0000 Subject: [PATCH 65/68] Update CodeMirror Signed-off-by: Renovate Bot --- yarn.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/yarn.lock b/yarn.lock index 39011d8815..f2a5be47b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8017,11 +8017,11 @@ __metadata: linkType: hard "@codemirror/legacy-modes@npm:^6.1.0": - version: 6.2.0 - resolution: "@codemirror/legacy-modes@npm:6.2.0" + version: 6.3.0 + resolution: "@codemirror/legacy-modes@npm:6.3.0" dependencies: "@codemirror/language": ^6.0.0 - checksum: c4449ad4e9b80fad982956ace705af5e21cd8c8bb248930bcb394ec019babf1a7b23e1e2894428b67d28a0c5bee99592320673690d19039392ddbc77075ef132 + checksum: d4da9b338376b72c1936e6780b4b53ec6a9581ac3d47ad2c1ec26a0068183b77489bbffde747837c89a5a92c69e9724ddebedc7790975a22f783fb13ede94403 languageName: node linkType: hard @@ -8047,10 +8047,10 @@ __metadata: languageName: node linkType: hard -"@codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.1.1": - version: 6.1.2 - resolution: "@codemirror/state@npm:6.1.2" - checksum: 7eda4b3eb6a777fecaa5a62e95a162aa163ccff01913d21a88d1157247390d9b57e6bcf54738be0b98aff8ba3a6ec3cb7aeb3b9714b019cb6c05242da9d9fdfe +"@codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.1.1, @codemirror/state@npm:^6.1.4": + version: 6.1.4 + resolution: "@codemirror/state@npm:6.1.4" + checksum: ef6bc495d3b89d2f0202b6b1c0fc3e3610f8d815f4ca91f86153d6093c9b93ddbe2cfb787802d07514a7114f0b200cf2eeb54c1cd34d7e00eff774ecea97d845 languageName: node linkType: hard @@ -8067,13 +8067,13 @@ __metadata: linkType: hard "@codemirror/view@npm:^6.0.0": - version: 6.4.2 - resolution: "@codemirror/view@npm:6.4.2" + version: 6.5.1 + resolution: "@codemirror/view@npm:6.5.1" dependencies: - "@codemirror/state": ^6.0.0 + "@codemirror/state": ^6.1.4 style-mod: ^4.0.0 w3c-keyname: ^2.2.4 - checksum: c79982563ad90adf1d7504b28bb1304a73f669c69634a1630b51521e4023b3630c15601daf2857683b34a86cac4ca24dc5b538a9d944616617f99c1a7da7faa6 + checksum: 1bd66b4f174642c559a521b546e80a67556fdc1b7cc7d1c7baf1cac37fed83e0e63d7a9ff6a88f3991a82d11bbbcfcca6e7b3491dcdd536c4c114de142b0300d languageName: node linkType: hard From 6b37068084ef5ddc0e421434964256a3e84faf2d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 17:08:35 +0000 Subject: [PATCH 66/68] Update dependency @gitbeaker/browser to v35.8.0 Signed-off-by: Renovate Bot --- yarn.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index 39011d8815..b57a29cf54 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8296,28 +8296,28 @@ __metadata: linkType: hard "@gitbeaker/browser@npm:^35.6.0": - version: 35.7.0 - resolution: "@gitbeaker/browser@npm:35.7.0" + version: 35.8.0 + resolution: "@gitbeaker/browser@npm:35.8.0" dependencies: - "@gitbeaker/core": ^35.7.0 - "@gitbeaker/requester-utils": ^35.7.0 + "@gitbeaker/core": ^35.8.0 + "@gitbeaker/requester-utils": ^35.8.0 delay: ^5.0.0 ky: 0.28.7 - checksum: 35b1afbd047ba172a21e548259fb38764c50e4794d64e33e6be7ae16552dc41882dc2107df70da35a3431ccb5014604efd4b189d16db2251349d0d0b01d65618 + checksum: 23fa14d64661108c03a5d141123037aebb68e257d84e26bd8f6241df65a0958eaa79b8b491e0f08159e325cdc3244f79b2426dedcc2354ad106316ea499415ac languageName: node linkType: hard -"@gitbeaker/core@npm:^35.6.0, @gitbeaker/core@npm:^35.7.0": - version: 35.7.0 - resolution: "@gitbeaker/core@npm:35.7.0" +"@gitbeaker/core@npm:^35.6.0, @gitbeaker/core@npm:^35.7.0, @gitbeaker/core@npm:^35.8.0": + version: 35.8.0 + resolution: "@gitbeaker/core@npm:35.8.0" dependencies: - "@gitbeaker/requester-utils": ^35.7.0 + "@gitbeaker/requester-utils": ^35.8.0 form-data: ^4.0.0 li: ^1.3.0 mime: ^3.0.0 query-string: ^7.0.0 xcase: ^2.0.1 - checksum: 5039557ba432a2478edb2cf767cef9d5f5f46f1de78219166eb6ec1bf6ad8e1dbf090085e31526fa58010ae4966bad9232af368474d9765b54bb3334173cb3cb + checksum: b894e1ac86dc8ac85f2db51d83d392899c10bb555b2448900f502ffeb49d9f36c1063c7151128ca96951ac472c11be6320584c4bec873501e96607bd7c25a37b languageName: node linkType: hard @@ -8334,14 +8334,14 @@ __metadata: languageName: node linkType: hard -"@gitbeaker/requester-utils@npm:^35.7.0": - version: 35.7.0 - resolution: "@gitbeaker/requester-utils@npm:35.7.0" +"@gitbeaker/requester-utils@npm:^35.7.0, @gitbeaker/requester-utils@npm:^35.8.0": + version: 35.8.0 + resolution: "@gitbeaker/requester-utils@npm:35.8.0" dependencies: form-data: ^4.0.0 qs: ^6.10.1 xcase: ^2.0.1 - checksum: f928affa8c5cd6b1b2811d77e14e2d60611bc9b93767fce936125b59e75b9786e4d760b5dc624719fbfdb8638905d957817845bcee01b147bbe5aebf60a635c1 + checksum: 6bd49b821fb6f262c6306c06fc4812f41902c864a7d941b0d112381b352f680e50ebd6180840e21a89d761427cb1326d9829b74c98c95bf9fa2a5d53872a400a languageName: node linkType: hard From 6334d5694d1eb4cc45fe97d388bc0b240cd23249 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 18:46:25 +0000 Subject: [PATCH 67/68] Update dependency @gitbeaker/node to v35.8.0 Signed-off-by: Renovate Bot --- yarn.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6b140cae27..bb40384aab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8307,7 +8307,7 @@ __metadata: languageName: node linkType: hard -"@gitbeaker/core@npm:^35.6.0, @gitbeaker/core@npm:^35.7.0, @gitbeaker/core@npm:^35.8.0": +"@gitbeaker/core@npm:^35.6.0, @gitbeaker/core@npm:^35.8.0": version: 35.8.0 resolution: "@gitbeaker/core@npm:35.8.0" dependencies: @@ -8322,19 +8322,19 @@ __metadata: linkType: hard "@gitbeaker/node@npm:^35.1.0": - version: 35.7.0 - resolution: "@gitbeaker/node@npm:35.7.0" + version: 35.8.0 + resolution: "@gitbeaker/node@npm:35.8.0" dependencies: - "@gitbeaker/core": ^35.7.0 - "@gitbeaker/requester-utils": ^35.7.0 + "@gitbeaker/core": ^35.8.0 + "@gitbeaker/requester-utils": ^35.8.0 delay: ^5.0.0 got: ^11.8.3 xcase: ^2.0.1 - checksum: 879595b065f40027b6191f609bddba05988c308c924323aa0cf30220801c21ba01e62c3395fd9cd0072ff6bc330f211e7aadcf63d80e893d7d33149c09ba221e + checksum: af75fa2348ec6fcdd1605f269df10c13bd7d53f9a5c42da7e013db920a1129075072de3a0ada97d800be732575bb838b8a462472faebcfaf9fe243b93d29a464 languageName: node linkType: hard -"@gitbeaker/requester-utils@npm:^35.7.0, @gitbeaker/requester-utils@npm:^35.8.0": +"@gitbeaker/requester-utils@npm:^35.8.0": version: 35.8.0 resolution: "@gitbeaker/requester-utils@npm:35.8.0" dependencies: From f1816019406cf9464dd289ce8e2ef7fc68b5da58 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 18:47:36 +0000 Subject: [PATCH 68/68] Update dependency @google-cloud/container to v4.4.0 Signed-off-by: Renovate Bot --- yarn.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6b140cae27..7a4980f0bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8346,11 +8346,11 @@ __metadata: linkType: hard "@google-cloud/container@npm:^4.0.0": - version: 4.3.0 - resolution: "@google-cloud/container@npm:4.3.0" + version: 4.4.0 + resolution: "@google-cloud/container@npm:4.4.0" dependencies: - google-gax: ^3.3.0 - checksum: cc1dc2ca7f6b47bac0d374013e78b05372f25dce7a3f84d799899af274d26fa22f23d2554a66ca1db528c3bbbec1cfbac86dfa9fc72cc78f175bcb737900b3ed + google-gax: ^3.5.2 + checksum: 39e7ba3b8cd7e50908315eee5c42461b8cee909c5653ffc817503757e867d63815c5ca8f540b7566f38be9ae0454ed89d2efe95b64961a8692a9348ddf92074f languageName: node linkType: hard @@ -22609,9 +22609,9 @@ __metadata: languageName: node linkType: hard -"google-gax@npm:^3.3.0, google-gax@npm:^3.5.1": - version: 3.5.1 - resolution: "google-gax@npm:3.5.1" +"google-gax@npm:^3.5.1, google-gax@npm:^3.5.2": + version: 3.5.2 + resolution: "google-gax@npm:3.5.2" dependencies: "@grpc/grpc-js": ~1.7.0 "@grpc/proto-loader": ^0.7.0 @@ -22624,13 +22624,13 @@ __metadata: node-fetch: ^2.6.1 object-hash: ^3.0.0 proto3-json-serializer: ^1.0.0 - protobufjs: 7.1.1 + protobufjs: 7.1.2 protobufjs-cli: 1.0.2 retry-request: ^5.0.0 bin: compileProtos: build/tools/compileProtos.js minifyProtoJson: build/tools/minify.js - checksum: 81ecd10981554944a117ace753b2fc8f6d82b26b244db005d01fc5e127181571de9a71ec02b28aa2e233c6fe696108617512a8b090c8304e753913f1ca441767 + checksum: 619a961acba742c30eaba85f5e0fb5903f8fc6713e26bf1736549cbbd43d7fac3ba3379dbc96850117263199ea8e3e74635b078e9464c7fadbbcba8822949135 languageName: node linkType: hard @@ -31127,9 +31127,9 @@ __metadata: languageName: node linkType: hard -"protobufjs@npm:7.1.1, protobufjs@npm:^7.0.0": - version: 7.1.1 - resolution: "protobufjs@npm:7.1.1" +"protobufjs@npm:7.1.2, protobufjs@npm:^7.0.0": + version: 7.1.2 + resolution: "protobufjs@npm:7.1.2" dependencies: "@protobufjs/aspromise": ^1.1.2 "@protobufjs/base64": ^1.1.2 @@ -31143,7 +31143,7 @@ __metadata: "@protobufjs/utf8": ^1.1.0 "@types/node": ">=13.7.0" long: ^5.0.0 - checksum: f6b0e7cec29056cefb83902009b39d24ac14a6117b52bc9b291b29778dad010e42d5563a20045e4de89b880114f4214f984396c8296a0d1b22f5dc7d9fe6705e + checksum: ae41669b1b0372fb1d49f506f2d1f2b0fb3dc3cece85987b17bcb544e4cef7c8d27f480486cdec324146ad0a5d22a327166a7ea864a9b3e49cc3c92a5d3f6500 languageName: node linkType: hard