@backstage/frontend-test-utils 0.4.5 → 0.4.6-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -8
- package/dist/app/createExtensionTester.esm.js +8 -3
- package/dist/app/createExtensionTester.esm.js.map +1 -1
- package/dist/app/renderInTestApp.esm.js +7 -2
- package/dist/app/renderInTestApp.esm.js.map +1 -1
- package/dist/app/renderTestApp.esm.js +7 -2
- package/dist/app/renderTestApp.esm.js.map +1 -1
- package/dist/core-app-api/src/apis/system/ApiAggregator.esm.js +18 -0
- package/dist/core-app-api/src/apis/system/ApiAggregator.esm.js.map +1 -0
- package/dist/core-app-api/src/apis/system/ApiProvider.esm.js +26 -0
- package/dist/core-app-api/src/apis/system/ApiProvider.esm.js.map +1 -0
- package/dist/frontend-app-api/src/tree/instantiateAppNodeTree.esm.js +48 -17
- package/dist/frontend-app-api/src/tree/instantiateAppNodeTree.esm.js.map +1 -1
- package/dist/frontend-app-api/src/tree/resolveAppNodeSpecs.esm.js +11 -5
- package/dist/frontend-app-api/src/tree/resolveAppNodeSpecs.esm.js.map +1 -1
- package/dist/frontend-app-api/src/tree/resolveAppTree.esm.js +3 -0
- package/dist/frontend-app-api/src/tree/resolveAppTree.esm.js.map +1 -1
- package/dist/frontend-app-api/src/wiring/createErrorCollector.esm.js.map +1 -1
- package/dist/frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js.map +1 -1
- package/dist/index.d.ts +143 -9
- package/dist/index.esm.js +2 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/utils/TestApiProvider.esm.js +52 -0
- package/dist/utils/TestApiProvider.esm.js.map +1 -0
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,21 +1,68 @@
|
|
|
1
1
|
# @backstage/frontend-test-utils
|
|
2
2
|
|
|
3
|
-
## 0.4.
|
|
3
|
+
## 0.4.6-next.1
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
|
|
7
|
+
- 22864b7: Added an `apis` option to `createExtensionTester`, `renderInTestApp`, and `renderTestApp` to override APIs when testing extensions. Use the `mockApis` helpers to create mock implementations:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { identityApiRef } from '@backstage/frontend-plugin-api';
|
|
11
|
+
import { mockApis } from '@backstage/frontend-test-utils';
|
|
12
|
+
|
|
13
|
+
// Override APIs in createExtensionTester
|
|
14
|
+
const tester = createExtensionTester(myExtension, {
|
|
15
|
+
apis: [
|
|
16
|
+
[
|
|
17
|
+
identityApiRef,
|
|
18
|
+
mockApis.identity({ userEntityRef: 'user:default/guest' }),
|
|
19
|
+
],
|
|
20
|
+
],
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Override APIs in renderInTestApp
|
|
24
|
+
renderInTestApp(<MyComponent />, {
|
|
25
|
+
apis: [
|
|
26
|
+
[
|
|
27
|
+
identityApiRef,
|
|
28
|
+
mockApis.identity({ userEntityRef: 'user:default/guest' }),
|
|
29
|
+
],
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Override APIs in renderTestApp
|
|
34
|
+
renderTestApp({
|
|
35
|
+
extensions: [myExtension],
|
|
36
|
+
apis: [
|
|
37
|
+
[
|
|
38
|
+
identityApiRef,
|
|
39
|
+
mockApis.identity({ userEntityRef: 'user:default/guest' }),
|
|
40
|
+
],
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
9
45
|
- Updated dependencies
|
|
10
|
-
- @backstage/frontend-app-api@0.
|
|
11
|
-
- @backstage/frontend-plugin-api@0.
|
|
12
|
-
- @backstage/plugin-app@0.
|
|
46
|
+
- @backstage/frontend-app-api@0.15.0-next.1
|
|
47
|
+
- @backstage/frontend-plugin-api@0.14.0-next.1
|
|
48
|
+
- @backstage/plugin-app@0.4.0-next.1
|
|
49
|
+
- @backstage/test-utils@1.7.15-next.1
|
|
13
50
|
|
|
14
|
-
## 0.4.
|
|
51
|
+
## 0.4.5-next.0
|
|
15
52
|
|
|
16
53
|
### Patch Changes
|
|
17
54
|
|
|
18
|
-
-
|
|
55
|
+
- d7dd5bd: Fixed Router deprecation warning and switched to using new `RouterBlueprint` from `@backstage/plugin-app-api`.
|
|
56
|
+
- 69d880e: Bump to latest zod to ensure it has the latest features
|
|
57
|
+
- Updated dependencies
|
|
58
|
+
- @backstage/frontend-app-api@0.14.1-next.0
|
|
59
|
+
- @backstage/frontend-plugin-api@0.14.0-next.0
|
|
60
|
+
- @backstage/plugin-app@0.4.0-next.0
|
|
61
|
+
- @backstage/plugin-app-react@0.1.1-next.0
|
|
62
|
+
- @backstage/config@1.3.6
|
|
63
|
+
- @backstage/test-utils@1.7.15-next.0
|
|
64
|
+
- @backstage/types@1.2.2
|
|
65
|
+
- @backstage/version-bridge@1.0.11
|
|
19
66
|
|
|
20
67
|
## 0.4.3
|
|
21
68
|
|
|
@@ -6,7 +6,7 @@ import { resolveAppNodeSpecs } from '../frontend-app-api/src/tree/resolveAppNode
|
|
|
6
6
|
import { instantiateAppNodeTree } from '../frontend-app-api/src/tree/instantiateAppNodeTree.esm.js';
|
|
7
7
|
import { readAppExtensionsConfig } from '../frontend-app-api/src/tree/readAppExtensionsConfig.esm.js';
|
|
8
8
|
import { createErrorCollector } from '../frontend-app-api/src/wiring/createErrorCollector.esm.js';
|
|
9
|
-
import { TestApiRegistry } from '
|
|
9
|
+
import { TestApiRegistry } from '../utils/TestApiProvider.esm.js';
|
|
10
10
|
import { OpaqueExtensionDefinition } from '../frontend-internal/src/wiring/InternalExtensionDefinition.esm.js';
|
|
11
11
|
|
|
12
12
|
class ExtensionQuery {
|
|
@@ -33,12 +33,16 @@ class ExtensionQuery {
|
|
|
33
33
|
class ExtensionTester {
|
|
34
34
|
/** @internal */
|
|
35
35
|
static forSubject(subject, options) {
|
|
36
|
-
const tester = new ExtensionTester();
|
|
36
|
+
const tester = new ExtensionTester(options?.apis);
|
|
37
37
|
tester.add(subject, options);
|
|
38
38
|
return tester;
|
|
39
39
|
}
|
|
40
40
|
#tree;
|
|
41
|
+
#apis;
|
|
41
42
|
#extensions = new Array();
|
|
43
|
+
constructor(apis) {
|
|
44
|
+
this.#apis = apis;
|
|
45
|
+
}
|
|
42
46
|
add(extension, options) {
|
|
43
47
|
if (this.#tree) {
|
|
44
48
|
throw new Error(
|
|
@@ -117,7 +121,8 @@ class ExtensionTester {
|
|
|
117
121
|
}),
|
|
118
122
|
collector
|
|
119
123
|
);
|
|
120
|
-
|
|
124
|
+
const apiHolder = this.#apis ? TestApiRegistry.from(...this.#apis) : TestApiRegistry.from();
|
|
125
|
+
instantiateAppNodeTree(tree.root, apiHolder, collector);
|
|
121
126
|
const errors = collector.collectErrors();
|
|
122
127
|
if (errors) {
|
|
123
128
|
throw new Error(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createExtensionTester.esm.js","sources":["../../src/app/createExtensionTester.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AppNode,\n AppTree,\n Extension,\n ExtensionDataRef,\n ExtensionDefinition,\n ExtensionDefinitionParameters,\n coreExtensionData,\n} from '@backstage/frontend-plugin-api';\nimport { Config, ConfigReader } from '@backstage/config';\nimport { JsonArray, JsonObject, JsonValue } from '@backstage/types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveAppTree } from '../../../frontend-app-api/src/tree/resolveAppTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveAppNodeSpecs } from '../../../frontend-app-api/src/tree/resolveAppNodeSpecs';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { instantiateAppNodeTree } from '../../../frontend-app-api/src/tree/instantiateAppNodeTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { readAppExtensionsConfig } from '../../../frontend-app-api/src/tree/readAppExtensionsConfig';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { createErrorCollector } from '../../../frontend-app-api/src/wiring/createErrorCollector';\nimport { TestApiRegistry } from '@backstage/test-utils';\nimport { OpaqueExtensionDefinition } from '@internal/frontend';\n\n/** @public */\nexport class ExtensionQuery<UOutput extends ExtensionDataRef> {\n #node: AppNode;\n\n constructor(node: AppNode) {\n this.#node = node;\n }\n\n get node() {\n return this.#node;\n }\n\n get instance() {\n const instance = this.#node.instance;\n if (!instance) {\n throw new Error(\n `Unable to access the instance of extension with ID '${\n this.#node.spec.id\n }'`,\n );\n }\n return instance;\n }\n\n get<TId extends UOutput['id']>(\n ref: ExtensionDataRef<any, TId, any>,\n ): UOutput extends ExtensionDataRef<infer IData, TId, infer IConfig>\n ? IConfig['optional'] extends true\n ? IData | undefined\n : IData\n : never {\n return this.instance.getData(ref);\n }\n}\n\n/** @public */\nexport class ExtensionTester<UOutput extends ExtensionDataRef> {\n /** @internal */\n static forSubject<T extends ExtensionDefinitionParameters>(\n subject: ExtensionDefinition<T>,\n options?: { config?: T['configInput'] },\n ): ExtensionTester<NonNullable<T['output']>> {\n const tester = new ExtensionTester();\n tester.add(subject, options as T['configInput'] & {});\n return tester;\n }\n\n #tree?: AppTree;\n\n readonly #extensions = new Array<{\n id: string;\n extension: Extension<any>;\n definition: ExtensionDefinition;\n config?: JsonValue;\n }>();\n\n add<T extends ExtensionDefinitionParameters>(\n extension: ExtensionDefinition<T>,\n options?: { config?: T['configInput'] },\n ): ExtensionTester<UOutput> {\n if (this.#tree) {\n throw new Error(\n 'Cannot add more extensions accessing the extension tree',\n );\n }\n\n const { name, namespace } = OpaqueExtensionDefinition.toInternal(extension);\n\n const definition = {\n ...extension,\n // setting name \"test\" as fallback\n name: !namespace && !name ? 'test' : name,\n };\n\n const resolvedExtension = resolveExtensionDefinition(definition);\n\n this.#extensions.push({\n id: resolvedExtension.id,\n extension: resolvedExtension,\n definition,\n config: options?.config as JsonValue,\n });\n\n return this;\n }\n\n get<TId extends UOutput['id']>(\n ref: ExtensionDataRef<any, TId, any>,\n ): UOutput extends ExtensionDataRef<infer IData, TId, infer IConfig>\n ? IConfig['optional'] extends true\n ? IData | undefined\n : IData\n : never {\n const tree = this.#resolveTree();\n\n return new ExtensionQuery(tree.root).get(ref);\n }\n\n query<T extends ExtensionDefinitionParameters>(\n extension: ExtensionDefinition<T>,\n ): ExtensionQuery<NonNullable<T['output']>> {\n const tree = this.#resolveTree();\n\n // Same fallback logic as in .add\n const { name, namespace } = OpaqueExtensionDefinition.toInternal(extension);\n const definition = {\n ...extension,\n name: !namespace && !name ? 'test' : name,\n };\n const actualId = resolveExtensionDefinition(definition).id;\n\n const node = tree.nodes.get(actualId);\n\n if (!node) {\n throw new Error(\n `Extension with ID '${actualId}' not found, please make sure it's added to the tester.`,\n );\n } else if (!node.instance) {\n throw new Error(\n `Extension with ID '${actualId}' has not been instantiated, because it is not part of the test subject's extension tree.`,\n );\n }\n return new ExtensionQuery(node);\n }\n\n reactElement(): JSX.Element {\n const tree = this.#resolveTree();\n\n const element = new ExtensionQuery(tree.root).get(\n coreExtensionData.reactElement,\n );\n\n if (!element) {\n throw new Error(\n 'No element found. Make sure the extension has a `coreExtensionData.reactElement` output, or use the `.get(...)` to access output data directly instead',\n );\n }\n\n return element;\n }\n\n #resolveTree() {\n if (this.#tree) {\n return this.#tree;\n }\n\n const [subject] = this.#extensions;\n if (!subject) {\n throw new Error(\n 'No subject found. At least one extension should be added to the tester.',\n );\n }\n\n const collector = createErrorCollector();\n\n const tree = resolveAppTree(\n subject.id,\n resolveAppNodeSpecs({\n features: [],\n builtinExtensions: this.#extensions.map(_ => _.extension),\n parameters: readAppExtensionsConfig(this.#getConfig()),\n collector,\n }),\n collector,\n );\n\n instantiateAppNodeTree(tree.root, TestApiRegistry.from(), collector);\n\n const errors = collector.collectErrors();\n if (errors) {\n throw new Error(\n `Failed to resolve the extension tree: ${errors\n .map(e => e.message)\n .join(', ')}`,\n );\n }\n\n this.#tree = tree;\n\n return tree;\n }\n\n #getConfig(additionalConfig?: JsonObject): Config {\n const [subject, ...rest] = this.#extensions;\n\n const extensionsConfig: JsonArray = [\n ...rest.flatMap(extension =>\n extension.config\n ? [\n {\n [extension.id]: {\n config: extension.config,\n },\n },\n ]\n : [],\n ),\n {\n [subject.id]: {\n config: subject.config,\n disabled: false,\n },\n },\n ];\n\n return ConfigReader.fromConfigs([\n { context: 'render-config', data: additionalConfig ?? {} },\n {\n context: 'test',\n data: {\n app: {\n extensions: extensionsConfig,\n },\n },\n },\n ]);\n }\n}\n\n/** @public */\nexport function createExtensionTester<T extends ExtensionDefinitionParameters>(\n subject: ExtensionDefinition<T>,\n options?: { config?: T['configInput'] },\n): ExtensionTester<NonNullable<T['output']>> {\n return ExtensionTester.forSubject(subject, options);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createExtensionTester.esm.js","sources":["../../src/app/createExtensionTester.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AppNode,\n AppTree,\n Extension,\n ExtensionDataRef,\n ExtensionDefinition,\n ExtensionDefinitionParameters,\n coreExtensionData,\n} from '@backstage/frontend-plugin-api';\nimport { Config, ConfigReader } from '@backstage/config';\nimport { JsonArray, JsonObject, JsonValue } from '@backstage/types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveAppTree } from '../../../frontend-app-api/src/tree/resolveAppTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveAppNodeSpecs } from '../../../frontend-app-api/src/tree/resolveAppNodeSpecs';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { instantiateAppNodeTree } from '../../../frontend-app-api/src/tree/instantiateAppNodeTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { readAppExtensionsConfig } from '../../../frontend-app-api/src/tree/readAppExtensionsConfig';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { createErrorCollector } from '../../../frontend-app-api/src/wiring/createErrorCollector';\nimport { OpaqueExtensionDefinition } from '@internal/frontend';\nimport { TestApiRegistry, type TestApiPairs } from '../utils';\n\n/** @public */\nexport class ExtensionQuery<UOutput extends ExtensionDataRef> {\n #node: AppNode;\n\n constructor(node: AppNode) {\n this.#node = node;\n }\n\n get node() {\n return this.#node;\n }\n\n get instance() {\n const instance = this.#node.instance;\n if (!instance) {\n throw new Error(\n `Unable to access the instance of extension with ID '${\n this.#node.spec.id\n }'`,\n );\n }\n return instance;\n }\n\n get<TId extends UOutput['id']>(\n ref: ExtensionDataRef<any, TId, any>,\n ): UOutput extends ExtensionDataRef<infer IData, TId, infer IConfig>\n ? IConfig['optional'] extends true\n ? IData | undefined\n : IData\n : never {\n return this.instance.getData(ref);\n }\n}\n\n/** @public */\nexport class ExtensionTester<UOutput extends ExtensionDataRef> {\n /** @internal */\n static forSubject<\n T extends ExtensionDefinitionParameters,\n TApiPairs extends any[],\n >(\n subject: ExtensionDefinition<T>,\n options?: {\n config?: T['configInput'];\n apis?: readonly [...TestApiPairs<TApiPairs>];\n },\n ): ExtensionTester<NonNullable<T['output']>> {\n const tester = new ExtensionTester(options?.apis);\n tester.add(subject, options as T['configInput'] & {});\n return tester;\n }\n\n #tree?: AppTree;\n #apis?: readonly any[];\n\n readonly #extensions = new Array<{\n id: string;\n extension: Extension<any>;\n definition: ExtensionDefinition;\n config?: JsonValue;\n }>();\n\n private constructor(apis?: readonly any[]) {\n this.#apis = apis;\n }\n\n add<T extends ExtensionDefinitionParameters>(\n extension: ExtensionDefinition<T>,\n options?: { config?: T['configInput'] },\n ): ExtensionTester<UOutput> {\n if (this.#tree) {\n throw new Error(\n 'Cannot add more extensions accessing the extension tree',\n );\n }\n\n const { name, namespace } = OpaqueExtensionDefinition.toInternal(extension);\n\n const definition = {\n ...extension,\n // setting name \"test\" as fallback\n name: !namespace && !name ? 'test' : name,\n };\n\n const resolvedExtension = resolveExtensionDefinition(definition);\n\n this.#extensions.push({\n id: resolvedExtension.id,\n extension: resolvedExtension,\n definition,\n config: options?.config as JsonValue,\n });\n\n return this;\n }\n\n get<TId extends UOutput['id']>(\n ref: ExtensionDataRef<any, TId, any>,\n ): UOutput extends ExtensionDataRef<infer IData, TId, infer IConfig>\n ? IConfig['optional'] extends true\n ? IData | undefined\n : IData\n : never {\n const tree = this.#resolveTree();\n\n return new ExtensionQuery(tree.root).get(ref);\n }\n\n query<T extends ExtensionDefinitionParameters>(\n extension: ExtensionDefinition<T>,\n ): ExtensionQuery<NonNullable<T['output']>> {\n const tree = this.#resolveTree();\n\n // Same fallback logic as in .add\n const { name, namespace } = OpaqueExtensionDefinition.toInternal(extension);\n const definition = {\n ...extension,\n name: !namespace && !name ? 'test' : name,\n };\n const actualId = resolveExtensionDefinition(definition).id;\n\n const node = tree.nodes.get(actualId);\n\n if (!node) {\n throw new Error(\n `Extension with ID '${actualId}' not found, please make sure it's added to the tester.`,\n );\n } else if (!node.instance) {\n throw new Error(\n `Extension with ID '${actualId}' has not been instantiated, because it is not part of the test subject's extension tree.`,\n );\n }\n return new ExtensionQuery(node);\n }\n\n reactElement(): JSX.Element {\n const tree = this.#resolveTree();\n\n const element = new ExtensionQuery(tree.root).get(\n coreExtensionData.reactElement,\n );\n\n if (!element) {\n throw new Error(\n 'No element found. Make sure the extension has a `coreExtensionData.reactElement` output, or use the `.get(...)` to access output data directly instead',\n );\n }\n\n return element;\n }\n\n #resolveTree() {\n if (this.#tree) {\n return this.#tree;\n }\n\n const [subject] = this.#extensions;\n if (!subject) {\n throw new Error(\n 'No subject found. At least one extension should be added to the tester.',\n );\n }\n\n const collector = createErrorCollector();\n\n const tree = resolveAppTree(\n subject.id,\n resolveAppNodeSpecs({\n features: [],\n builtinExtensions: this.#extensions.map(_ => _.extension),\n parameters: readAppExtensionsConfig(this.#getConfig()),\n collector,\n }),\n collector,\n );\n\n const apiHolder = this.#apis\n ? TestApiRegistry.from(...this.#apis)\n : TestApiRegistry.from();\n\n instantiateAppNodeTree(tree.root, apiHolder, collector);\n\n const errors = collector.collectErrors();\n if (errors) {\n throw new Error(\n `Failed to resolve the extension tree: ${errors\n .map(e => e.message)\n .join(', ')}`,\n );\n }\n\n this.#tree = tree;\n\n return tree;\n }\n\n #getConfig(additionalConfig?: JsonObject): Config {\n const [subject, ...rest] = this.#extensions;\n\n const extensionsConfig: JsonArray = [\n ...rest.flatMap(extension =>\n extension.config\n ? [\n {\n [extension.id]: {\n config: extension.config,\n },\n },\n ]\n : [],\n ),\n {\n [subject.id]: {\n config: subject.config,\n disabled: false,\n },\n },\n ];\n\n return ConfigReader.fromConfigs([\n { context: 'render-config', data: additionalConfig ?? {} },\n {\n context: 'test',\n data: {\n app: {\n extensions: extensionsConfig,\n },\n },\n },\n ]);\n }\n}\n\n/** @public */\nexport function createExtensionTester<\n T extends ExtensionDefinitionParameters,\n TApiPairs extends any[] = any[],\n>(\n subject: ExtensionDefinition<T>,\n options?: {\n config?: T['configInput'];\n apis?: readonly [...TestApiPairs<TApiPairs>];\n },\n): ExtensionTester<NonNullable<T['output']>> {\n return ExtensionTester.forSubject(subject, options);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { Link, MemoryRouter } from 'react-router-dom';
|
|
|
4
4
|
import { createSpecializedApp } from '@backstage/frontend-app-api';
|
|
5
5
|
import { render } from '@testing-library/react';
|
|
6
6
|
import { ConfigReader } from '@backstage/config';
|
|
7
|
-
import { coreExtensionData, NavItemBlueprint, useRouteRef, createExtension, createFrontendModule, createFrontendPlugin } from '@backstage/frontend-plugin-api';
|
|
7
|
+
import { coreExtensionData, NavItemBlueprint, useRouteRef, createExtension, createFrontendModule, createFrontendPlugin, createApiFactory } from '@backstage/frontend-plugin-api';
|
|
8
8
|
import { RouterBlueprint } from '@backstage/plugin-app-react';
|
|
9
9
|
import appPlugin from '@backstage/plugin-app';
|
|
10
10
|
|
|
@@ -118,7 +118,12 @@ function renderInTestApp(element, options) {
|
|
|
118
118
|
context: "render-config",
|
|
119
119
|
data: options?.config ?? DEFAULT_MOCK_CONFIG
|
|
120
120
|
}
|
|
121
|
-
])
|
|
121
|
+
]),
|
|
122
|
+
__internal: options?.apis && {
|
|
123
|
+
apiFactoryOverrides: options.apis.map(
|
|
124
|
+
([apiRef, implementation]) => createApiFactory(apiRef, implementation)
|
|
125
|
+
)
|
|
126
|
+
}
|
|
122
127
|
});
|
|
123
128
|
return render(
|
|
124
129
|
app.tree.root.instance.getData(coreExtensionData.reactElement)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderInTestApp.esm.js","sources":["../../src/app/renderInTestApp.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Fragment } from 'react';\nimport { Link, MemoryRouter } from 'react-router-dom';\nimport { createSpecializedApp } from '@backstage/frontend-app-api';\nimport { RenderResult, render } from '@testing-library/react';\nimport { ConfigReader } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\nimport {\n createExtension,\n ExtensionDefinition,\n coreExtensionData,\n RouteRef,\n useRouteRef,\n IconComponent,\n NavItemBlueprint,\n createFrontendPlugin,\n FrontendFeature,\n createFrontendModule,\n} from '@backstage/frontend-plugin-api';\nimport { RouterBlueprint } from '@backstage/plugin-app-react';\nimport appPlugin from '@backstage/plugin-app';\n\nconst DEFAULT_MOCK_CONFIG = {\n app: { baseUrl: 'http://localhost:3000' },\n backend: { baseUrl: 'http://localhost:7007' },\n};\n\n/**\n * Options to customize the behavior of the test app.\n * @public\n */\nexport type TestAppOptions = {\n /**\n * An object of paths to mount route ref on, with the key being the path and the value\n * being the RouteRef that the path will be bound to. This allows the route refs to be\n * used by `useRouteRef` in the rendered elements.\n *\n * @example\n * ```ts\n * renderInTestApp(<MyComponent />, {\n * mountedRoutes: {\n * '/my-path': myRouteRef,\n * }\n * })\n * // ...\n * const link = useRouteRef(myRouteRef)\n * ```\n */\n mountedRoutes?: { [path: string]: RouteRef };\n\n /**\n * Additional configuration passed to the app when rendering elements inside it.\n */\n config?: JsonObject;\n\n /**\n * Additional features to add to the test app.\n */\n features?: FrontendFeature[];\n\n /**\n * Initial route entries to use for the router.\n */\n initialRouteEntries?: string[];\n};\n\nconst NavItem = (props: {\n routeRef: RouteRef<undefined>;\n title: string;\n icon: IconComponent;\n}) => {\n const { routeRef, title, icon: Icon } = props;\n const link = useRouteRef(routeRef);\n if (!link) {\n return null;\n }\n return (\n <li>\n <Link to={link()}>\n <Icon /> {title}\n </Link>\n </li>\n );\n};\n\nconst appPluginOverride = appPlugin.withOverrides({\n extensions: [\n appPlugin.getExtension('sign-in-page:app').override({\n disabled: true,\n }),\n appPlugin.getExtension('app/layout').override({\n disabled: true,\n }),\n appPlugin.getExtension('app/routes').override({\n disabled: true,\n }),\n appPlugin.getExtension('app/nav').override({\n output: [coreExtensionData.reactElement],\n factory(_originalFactory, { inputs }) {\n return [\n coreExtensionData.reactElement(\n <nav>\n <ul>\n {inputs.items.map((item, index) => {\n const { icon, title, routeRef } = item.get(\n NavItemBlueprint.dataRefs.target,\n );\n\n return (\n <NavItem\n key={index}\n icon={icon}\n title={title}\n routeRef={routeRef}\n />\n );\n })}\n </ul>\n </nav>,\n ),\n ];\n },\n }),\n ],\n});\n\n/**\n * @public\n * Renders the given element in a test app, for use in unit tests.\n */\nexport function renderInTestApp(\n element: JSX.Element,\n options?: TestAppOptions,\n): RenderResult {\n const extensions: Array<ExtensionDefinition> = [\n createExtension({\n attachTo: { id: 'app/root', input: 'children' },\n output: [coreExtensionData.reactElement],\n factory: () => {\n return [coreExtensionData.reactElement(element)];\n },\n }),\n ];\n\n if (options?.mountedRoutes) {\n for (const [path, routeRef] of Object.entries(options.mountedRoutes)) {\n // TODO(Rugvip): add support for external route refs\n extensions.push(\n createExtension({\n kind: 'test-route',\n name: path,\n attachTo: { id: 'app/root', input: 'elements' },\n output: [\n coreExtensionData.reactElement,\n coreExtensionData.routePath,\n coreExtensionData.routeRef,\n ],\n factory: () => [\n coreExtensionData.reactElement(<Fragment />),\n coreExtensionData.routePath(path),\n coreExtensionData.routeRef(routeRef),\n ],\n }),\n );\n }\n }\n\n const features: FrontendFeature[] = [\n createFrontendModule({\n pluginId: 'app',\n extensions: [\n RouterBlueprint.make({\n params: {\n component: ({ children }) => (\n <MemoryRouter initialEntries={options?.initialRouteEntries}>\n {children}\n </MemoryRouter>\n ),\n },\n }),\n ],\n }),\n createFrontendPlugin({\n pluginId: 'test',\n extensions,\n }),\n appPluginOverride,\n ];\n\n if (options?.features) {\n features.push(...options.features);\n }\n\n const app = createSpecializedApp({\n features,\n config: ConfigReader.fromConfigs([\n {\n context: 'render-config',\n data: options?.config ?? DEFAULT_MOCK_CONFIG,\n },\n ]),\n });\n\n return render(\n app.tree.root.instance!.getData(coreExtensionData.reactElement),\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"renderInTestApp.esm.js","sources":["../../src/app/renderInTestApp.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Fragment } from 'react';\nimport { Link, MemoryRouter } from 'react-router-dom';\nimport { createSpecializedApp } from '@backstage/frontend-app-api';\nimport { RenderResult, render } from '@testing-library/react';\nimport { ConfigReader } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\nimport {\n createExtension,\n ExtensionDefinition,\n coreExtensionData,\n RouteRef,\n useRouteRef,\n IconComponent,\n NavItemBlueprint,\n createFrontendPlugin,\n FrontendFeature,\n createFrontendModule,\n createApiFactory,\n} from '@backstage/frontend-plugin-api';\nimport { RouterBlueprint } from '@backstage/plugin-app-react';\nimport appPlugin from '@backstage/plugin-app';\nimport { type TestApiPairs } from '../utils';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport type { CreateSpecializedAppInternalOptions } from '../../../frontend-app-api/src/wiring/createSpecializedApp';\n\nconst DEFAULT_MOCK_CONFIG = {\n app: { baseUrl: 'http://localhost:3000' },\n backend: { baseUrl: 'http://localhost:7007' },\n};\n\n/**\n * Options to customize the behavior of the test app.\n * @public\n */\nexport type TestAppOptions<TApiPairs extends any[] = any[]> = {\n /**\n * An object of paths to mount route ref on, with the key being the path and the value\n * being the RouteRef that the path will be bound to. This allows the route refs to be\n * used by `useRouteRef` in the rendered elements.\n *\n * @example\n * ```ts\n * renderInTestApp(<MyComponent />, {\n * mountedRoutes: {\n * '/my-path': myRouteRef,\n * }\n * })\n * // ...\n * const link = useRouteRef(myRouteRef)\n * ```\n */\n mountedRoutes?: { [path: string]: RouteRef };\n\n /**\n * Additional configuration passed to the app when rendering elements inside it.\n */\n config?: JsonObject;\n\n /**\n * Additional features to add to the test app.\n */\n features?: FrontendFeature[];\n\n /**\n * Initial route entries to use for the router.\n */\n initialRouteEntries?: string[];\n\n /**\n * API overrides to provide to the test app. Use `mockApis` helpers\n * from `@backstage/frontend-test-utils` to create mock implementations.\n *\n * @example\n * ```ts\n * import { identityApiRef } from '@backstage/frontend-plugin-api';\n * import { mockApis } from '@backstage/frontend-test-utils';\n *\n * renderInTestApp(<MyComponent />, {\n * apis: [[identityApiRef, mockApis.identity({ userEntityRef: 'user:default/guest' })]],\n * })\n * ```\n */\n apis?: readonly [...TestApiPairs<TApiPairs>];\n};\n\nconst NavItem = (props: {\n routeRef: RouteRef<undefined>;\n title: string;\n icon: IconComponent;\n}) => {\n const { routeRef, title, icon: Icon } = props;\n const link = useRouteRef(routeRef);\n if (!link) {\n return null;\n }\n return (\n <li>\n <Link to={link()}>\n <Icon /> {title}\n </Link>\n </li>\n );\n};\n\nconst appPluginOverride = appPlugin.withOverrides({\n extensions: [\n appPlugin.getExtension('sign-in-page:app').override({\n disabled: true,\n }),\n appPlugin.getExtension('app/layout').override({\n disabled: true,\n }),\n appPlugin.getExtension('app/routes').override({\n disabled: true,\n }),\n appPlugin.getExtension('app/nav').override({\n output: [coreExtensionData.reactElement],\n factory(_originalFactory, { inputs }) {\n return [\n coreExtensionData.reactElement(\n <nav>\n <ul>\n {inputs.items.map((item, index) => {\n const { icon, title, routeRef } = item.get(\n NavItemBlueprint.dataRefs.target,\n );\n\n return (\n <NavItem\n key={index}\n icon={icon}\n title={title}\n routeRef={routeRef}\n />\n );\n })}\n </ul>\n </nav>,\n ),\n ];\n },\n }),\n ],\n});\n\n/**\n * @public\n * Renders the given element in a test app, for use in unit tests.\n */\nexport function renderInTestApp<TApiPairs extends any[] = any[]>(\n element: JSX.Element,\n options?: TestAppOptions<TApiPairs>,\n): RenderResult {\n const extensions: Array<ExtensionDefinition> = [\n createExtension({\n attachTo: { id: 'app/root', input: 'children' },\n output: [coreExtensionData.reactElement],\n factory: () => {\n return [coreExtensionData.reactElement(element)];\n },\n }),\n ];\n\n if (options?.mountedRoutes) {\n for (const [path, routeRef] of Object.entries(options.mountedRoutes)) {\n // TODO(Rugvip): add support for external route refs\n extensions.push(\n createExtension({\n kind: 'test-route',\n name: path,\n attachTo: { id: 'app/root', input: 'elements' },\n output: [\n coreExtensionData.reactElement,\n coreExtensionData.routePath,\n coreExtensionData.routeRef,\n ],\n factory: () => [\n coreExtensionData.reactElement(<Fragment />),\n coreExtensionData.routePath(path),\n coreExtensionData.routeRef(routeRef),\n ],\n }),\n );\n }\n }\n\n const features: FrontendFeature[] = [\n createFrontendModule({\n pluginId: 'app',\n extensions: [\n RouterBlueprint.make({\n params: {\n component: ({ children }) => (\n <MemoryRouter initialEntries={options?.initialRouteEntries}>\n {children}\n </MemoryRouter>\n ),\n },\n }),\n ],\n }),\n createFrontendPlugin({\n pluginId: 'test',\n extensions,\n }),\n appPluginOverride,\n ];\n\n if (options?.features) {\n features.push(...options.features);\n }\n\n const app = createSpecializedApp({\n features,\n config: ConfigReader.fromConfigs([\n {\n context: 'render-config',\n data: options?.config ?? DEFAULT_MOCK_CONFIG,\n },\n ]),\n __internal: options?.apis && {\n apiFactoryOverrides: options.apis.map(([apiRef, implementation]) =>\n createApiFactory(apiRef, implementation),\n ),\n },\n } as CreateSpecializedAppInternalOptions);\n\n return render(\n app.tree.root.instance!.getData(coreExtensionData.reactElement),\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { createSpecializedApp } from '@backstage/frontend-app-api';
|
|
3
|
-
import { createFrontendModule, createFrontendPlugin, coreExtensionData } from '@backstage/frontend-plugin-api';
|
|
3
|
+
import { createFrontendModule, createFrontendPlugin, createApiFactory, coreExtensionData } from '@backstage/frontend-plugin-api';
|
|
4
4
|
import { render } from '@testing-library/react';
|
|
5
5
|
import appPlugin from '@backstage/plugin-app';
|
|
6
6
|
import { ConfigReader } from '@backstage/config';
|
|
@@ -47,7 +47,12 @@ function renderTestApp(options) {
|
|
|
47
47
|
context: "render-config",
|
|
48
48
|
data: options?.config ?? DEFAULT_MOCK_CONFIG
|
|
49
49
|
}
|
|
50
|
-
])
|
|
50
|
+
]),
|
|
51
|
+
__internal: options?.apis && {
|
|
52
|
+
apiFactoryOverrides: options.apis.map(
|
|
53
|
+
([apiRef, implementation]) => createApiFactory(apiRef, implementation)
|
|
54
|
+
)
|
|
55
|
+
}
|
|
51
56
|
});
|
|
52
57
|
return render(
|
|
53
58
|
app.tree.root.instance.getData(coreExtensionData.reactElement)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderTestApp.esm.js","sources":["../../src/app/renderTestApp.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createSpecializedApp } from '@backstage/frontend-app-api';\nimport {\n coreExtensionData,\n createFrontendModule,\n createFrontendPlugin,\n ExtensionDefinition,\n FrontendFeature,\n} from '@backstage/frontend-plugin-api';\nimport { render } from '@testing-library/react';\nimport appPlugin from '@backstage/plugin-app';\nimport { JsonObject } from '@backstage/types';\nimport { ConfigReader } from '@backstage/config';\nimport { MemoryRouter } from 'react-router-dom';\nimport { RouterBlueprint } from '@backstage/plugin-app-react';\n\nconst DEFAULT_MOCK_CONFIG = {\n app: { baseUrl: 'http://localhost:3000' },\n backend: { baseUrl: 'http://localhost:7007' },\n};\n\n/**\n * Options for `renderTestApp`.\n *\n * @public\n */\nexport type RenderTestAppOptions = {\n /**\n * Additional configuration passed to the app when rendering elements inside it.\n */\n config?: JsonObject;\n /**\n * Additional extensions to add to the test app.\n */\n extensions?: ExtensionDefinition<any>[];\n\n /**\n * Additional features to add to the test app.\n */\n features?: FrontendFeature[];\n\n /**\n * Initial route entries to use for the router.\n */\n initialRouteEntries?: string[];\n};\n\nconst appPluginOverride = appPlugin.withOverrides({\n extensions: [\n appPlugin.getExtension('sign-in-page:app').override({\n disabled: true,\n }),\n ],\n});\n\n/**\n * Renders the provided extensions inside a Backstage app, returning the same\n * utilities as `@testing-library/react` `render` function.\n *\n * @public\n */\nexport function renderTestApp(options: RenderTestAppOptions) {\n const extensions = [...(options.extensions ?? [])];\n\n const features: FrontendFeature[] = [\n createFrontendModule({\n pluginId: 'app',\n extensions: [\n RouterBlueprint.make({\n params: {\n component: ({ children }) => (\n <MemoryRouter initialEntries={options.initialRouteEntries}>\n {children}\n </MemoryRouter>\n ),\n },\n }),\n ],\n }),\n createFrontendPlugin({\n pluginId: 'test',\n extensions,\n }),\n appPluginOverride,\n ];\n\n if (options.features) {\n features.push(...options.features);\n }\n\n const app = createSpecializedApp({\n features,\n config: ConfigReader.fromConfigs([\n {\n context: 'render-config',\n data: options?.config ?? DEFAULT_MOCK_CONFIG,\n },\n ]),\n });\n\n return render(\n app.tree.root.instance!.getData(coreExtensionData.reactElement),\n );\n}\n"],"names":[],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"renderTestApp.esm.js","sources":["../../src/app/renderTestApp.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createSpecializedApp } from '@backstage/frontend-app-api';\nimport {\n coreExtensionData,\n createApiFactory,\n createFrontendModule,\n createFrontendPlugin,\n ExtensionDefinition,\n FrontendFeature,\n} from '@backstage/frontend-plugin-api';\nimport { render } from '@testing-library/react';\nimport appPlugin from '@backstage/plugin-app';\nimport { JsonObject } from '@backstage/types';\nimport { ConfigReader } from '@backstage/config';\nimport { MemoryRouter } from 'react-router-dom';\nimport { RouterBlueprint } from '@backstage/plugin-app-react';\nimport { type TestApiPairs } from '../utils';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport type { CreateSpecializedAppInternalOptions } from '../../../frontend-app-api/src/wiring/createSpecializedApp';\n\nconst DEFAULT_MOCK_CONFIG = {\n app: { baseUrl: 'http://localhost:3000' },\n backend: { baseUrl: 'http://localhost:7007' },\n};\n\n/**\n * Options for `renderTestApp`.\n *\n * @public\n */\nexport type RenderTestAppOptions<TApiPairs extends any[] = any[]> = {\n /**\n * Additional configuration passed to the app when rendering elements inside it.\n */\n config?: JsonObject;\n /**\n * Additional extensions to add to the test app.\n */\n extensions?: ExtensionDefinition<any>[];\n\n /**\n * Additional features to add to the test app.\n */\n features?: FrontendFeature[];\n\n /**\n * Initial route entries to use for the router.\n */\n initialRouteEntries?: string[];\n\n /**\n * API overrides to provide to the test app. Use `mockApis` helpers\n * from `@backstage/frontend-test-utils` to create mock implementations.\n *\n * @example\n * ```ts\n * import { identityApiRef } from '@backstage/frontend-plugin-api';\n * import { mockApis } from '@backstage/frontend-test-utils';\n *\n * renderTestApp({\n * apis: [[identityApiRef, mockApis.identity({ userEntityRef: 'user:default/guest' })]],\n * extensions: [...],\n * })\n * ```\n */\n apis?: readonly [...TestApiPairs<TApiPairs>];\n};\n\nconst appPluginOverride = appPlugin.withOverrides({\n extensions: [\n appPlugin.getExtension('sign-in-page:app').override({\n disabled: true,\n }),\n ],\n});\n\n/**\n * Renders the provided extensions inside a Backstage app, returning the same\n * utilities as `@testing-library/react` `render` function.\n *\n * @public\n */\nexport function renderTestApp<TApiPairs extends any[] = any[]>(\n options: RenderTestAppOptions<TApiPairs>,\n) {\n const extensions = [...(options.extensions ?? [])];\n\n const features: FrontendFeature[] = [\n createFrontendModule({\n pluginId: 'app',\n extensions: [\n RouterBlueprint.make({\n params: {\n component: ({ children }) => (\n <MemoryRouter initialEntries={options.initialRouteEntries}>\n {children}\n </MemoryRouter>\n ),\n },\n }),\n ],\n }),\n createFrontendPlugin({\n pluginId: 'test',\n extensions,\n }),\n appPluginOverride,\n ];\n\n if (options.features) {\n features.push(...options.features);\n }\n\n const app = createSpecializedApp({\n features,\n config: ConfigReader.fromConfigs([\n {\n context: 'render-config',\n data: options?.config ?? DEFAULT_MOCK_CONFIG,\n },\n ]),\n __internal: options?.apis && {\n apiFactoryOverrides: options.apis.map(([apiRef, implementation]) =>\n createApiFactory(apiRef, implementation),\n ),\n },\n } as CreateSpecializedAppInternalOptions);\n\n return render(\n app.tree.root.instance!.getData(coreExtensionData.reactElement),\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AAmCA,MAAM,mBAAA,GAAsB;AAAA,EAC1B,GAAA,EAAK,EAAE,OAAA,EAAS,uBAAA,EAAwB;AAAA,EACxC,OAAA,EAAS,EAAE,OAAA,EAAS,uBAAA;AACtB,CAAA;AA6CA,MAAM,iBAAA,GAAoB,UAAU,aAAA,CAAc;AAAA,EAChD,UAAA,EAAY;AAAA,IACV,SAAA,CAAU,YAAA,CAAa,kBAAkB,CAAA,CAAE,QAAA,CAAS;AAAA,MAClD,QAAA,EAAU;AAAA,KACX;AAAA;AAEL,CAAC,CAAA;AAQM,SAAS,cACd,OAAA,EACA;AACA,EAAA,MAAM,aAAa,CAAC,GAAI,OAAA,CAAQ,UAAA,IAAc,EAAG,CAAA;AAEjD,EAAA,MAAM,QAAA,GAA8B;AAAA,IAClC,oBAAA,CAAqB;AAAA,MACnB,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,QACV,gBAAgB,IAAA,CAAK;AAAA,UACnB,MAAA,EAAQ;AAAA,YACN,SAAA,EAAW,CAAC,EAAE,QAAA,EAAS,yBACpB,YAAA,EAAA,EAAa,cAAA,EAAgB,OAAA,CAAQ,mBAAA,EACnC,QAAA,EACH;AAAA;AAEJ,SACD;AAAA;AACH,KACD,CAAA;AAAA,IACD,oBAAA,CAAqB;AAAA,MACnB,QAAA,EAAU,MAAA;AAAA,MACV;AAAA,KACD,CAAA;AAAA,IACD;AAAA,GACF;AAEA,EAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,IAAA,QAAA,CAAS,IAAA,CAAK,GAAG,OAAA,CAAQ,QAAQ,CAAA;AAAA,EACnC;AAEA,EAAA,MAAM,MAAM,oBAAA,CAAqB;AAAA,IAC/B,QAAA;AAAA,IACA,MAAA,EAAQ,aAAa,WAAA,CAAY;AAAA,MAC/B;AAAA,QACE,OAAA,EAAS,eAAA;AAAA,QACT,IAAA,EAAM,SAAS,MAAA,IAAU;AAAA;AAC3B,KACD,CAAA;AAAA,IACD,UAAA,EAAY,SAAS,IAAA,IAAQ;AAAA,MAC3B,mBAAA,EAAqB,QAAQ,IAAA,CAAK,GAAA;AAAA,QAAI,CAAC,CAAC,MAAA,EAAQ,cAAc,CAAA,KAC5D,gBAAA,CAAiB,QAAQ,cAAc;AAAA;AACzC;AACF,GACsC,CAAA;AAExC,EAAA,OAAO,MAAA;AAAA,IACL,IAAI,IAAA,CAAK,IAAA,CAAK,QAAA,CAAU,OAAA,CAAQ,kBAAkB,YAAY;AAAA,GAChE;AACF;;;;"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class ApiAggregator {
|
|
2
|
+
holders;
|
|
3
|
+
constructor(...holders) {
|
|
4
|
+
this.holders = holders;
|
|
5
|
+
}
|
|
6
|
+
get(apiRef) {
|
|
7
|
+
for (const holder of this.holders) {
|
|
8
|
+
const api = holder.get(apiRef);
|
|
9
|
+
if (api) {
|
|
10
|
+
return api;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { ApiAggregator };
|
|
18
|
+
//# sourceMappingURL=ApiAggregator.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiAggregator.esm.js","sources":["../../../../../../core-app-api/src/apis/system/ApiAggregator.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ApiRef, ApiHolder } from '@backstage/core-plugin-api';\n\n/**\n * An ApiHolder that queries multiple other holders from for\n * an Api implementation, returning the first one encountered..\n */\nexport class ApiAggregator implements ApiHolder {\n private readonly holders: ApiHolder[];\n\n constructor(...holders: ApiHolder[]) {\n this.holders = holders;\n }\n\n get<T>(apiRef: ApiRef<T>): T | undefined {\n for (const holder of this.holders) {\n const api = holder.get(apiRef);\n if (api) {\n return api;\n }\n }\n return undefined;\n }\n}\n"],"names":[],"mappings":"AAsBO,MAAM,aAAA,CAAmC;AAAA,EAC7B,OAAA;AAAA,EAEjB,eAAe,OAAA,EAAsB;AACnC,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EACjB;AAAA,EAEA,IAAO,MAAA,EAAkC;AACvC,IAAA,KAAA,MAAW,MAAA,IAAU,KAAK,OAAA,EAAS;AACjC,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,GAAA,CAAI,MAAM,CAAA;AAC7B,MAAA,IAAI,GAAA,EAAK;AACP,QAAA,OAAO,GAAA;AAAA,MACT;AAAA,IACF;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AACF;;;;"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { ApiAggregator } from './ApiAggregator.esm.js';
|
|
5
|
+
import { createVersionedContext, createVersionedValueMap } from '@backstage/version-bridge';
|
|
6
|
+
|
|
7
|
+
const ApiContext = createVersionedContext("api-context");
|
|
8
|
+
const ApiProvider = (props) => {
|
|
9
|
+
const { apis, children } = props;
|
|
10
|
+
const parentHolder = useContext(ApiContext)?.atVersion(1);
|
|
11
|
+
const holder = parentHolder ? new ApiAggregator(apis, parentHolder) : apis;
|
|
12
|
+
return /* @__PURE__ */ jsx(
|
|
13
|
+
ApiContext.Provider,
|
|
14
|
+
{
|
|
15
|
+
value: createVersionedValueMap({ 1: holder }),
|
|
16
|
+
children
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
ApiProvider.propTypes = {
|
|
21
|
+
apis: PropTypes.shape({ get: PropTypes.func.isRequired }).isRequired,
|
|
22
|
+
children: PropTypes.node
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export { ApiProvider };
|
|
26
|
+
//# sourceMappingURL=ApiProvider.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiProvider.esm.js","sources":["../../../../../../core-app-api/src/apis/system/ApiProvider.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useContext, ReactNode, PropsWithChildren } from 'react';\nimport PropTypes from 'prop-types';\nimport { ApiHolder } from '@backstage/core-plugin-api';\nimport { ApiAggregator } from './ApiAggregator';\nimport {\n createVersionedValueMap,\n createVersionedContext,\n} from '@backstage/version-bridge';\n\n/**\n * Prop types for the ApiProvider component.\n * @public\n */\nexport type ApiProviderProps = {\n apis: ApiHolder;\n children: ReactNode;\n};\n\nconst ApiContext = createVersionedContext<{ 1: ApiHolder }>('api-context');\n\n/**\n * Provides an {@link @backstage/core-plugin-api#ApiHolder} for consumption in\n * the React tree.\n *\n * @public\n */\nexport const ApiProvider = (props: PropsWithChildren<ApiProviderProps>) => {\n const { apis, children } = props;\n const parentHolder = useContext(ApiContext)?.atVersion(1);\n const holder = parentHolder ? new ApiAggregator(apis, parentHolder) : apis;\n\n return (\n <ApiContext.Provider\n value={createVersionedValueMap({ 1: holder })}\n children={children}\n />\n );\n};\n\nApiProvider.propTypes = {\n apis: PropTypes.shape({ get: PropTypes.func.isRequired }).isRequired,\n children: PropTypes.node,\n};\n"],"names":[],"mappings":";;;;;;AAkCA,MAAM,UAAA,GAAa,uBAAyC,aAAa,CAAA;AAQlE,MAAM,WAAA,GAAc,CAAC,KAAA,KAA+C;AACzE,EAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAS,GAAI,KAAA;AAC3B,EAAA,MAAM,YAAA,GAAe,UAAA,CAAW,UAAU,CAAA,EAAG,UAAU,CAAC,CAAA;AACxD,EAAA,MAAM,SAAS,YAAA,GAAe,IAAI,aAAA,CAAc,IAAA,EAAM,YAAY,CAAA,GAAI,IAAA;AAEtE,EAAA,uBACE,GAAA;AAAA,IAAC,UAAA,CAAW,QAAA;AAAA,IAAX;AAAA,MACC,KAAA,EAAO,uBAAA,CAAwB,EAAE,CAAA,EAAG,QAAQ,CAAA;AAAA,MAC5C;AAAA;AAAA,GACF;AAEJ;AAEA,WAAA,CAAY,SAAA,GAAY;AAAA,EACtB,IAAA,EAAM,UAAU,KAAA,CAAM,EAAE,KAAK,SAAA,CAAU,IAAA,CAAK,UAAA,EAAY,CAAA,CAAE,UAAA;AAAA,EAC1D,UAAU,SAAA,CAAU;AACtB,CAAA;;;;"}
|
|
@@ -7,21 +7,21 @@ import '../../../frontend-internal/src/wiring/InternalFrontendPlugin.esm.js';
|
|
|
7
7
|
import '../../../frontend-internal/src/wiring/InternalSwappableComponentRef.esm.js';
|
|
8
8
|
|
|
9
9
|
const INSTANTIATION_FAILED = new Error("Instantiation failed");
|
|
10
|
-
function mapWithFailures(iterable, callback) {
|
|
10
|
+
function mapWithFailures(iterable, callback, options) {
|
|
11
11
|
let failed = false;
|
|
12
|
-
const results =
|
|
12
|
+
const results = [];
|
|
13
|
+
for (const item of iterable) {
|
|
13
14
|
try {
|
|
14
|
-
|
|
15
|
+
results.push(callback(item));
|
|
15
16
|
} catch (error) {
|
|
16
17
|
if (error === INSTANTIATION_FAILED) {
|
|
17
18
|
failed = true;
|
|
18
19
|
} else {
|
|
19
20
|
throw error;
|
|
20
21
|
}
|
|
21
|
-
return null;
|
|
22
22
|
}
|
|
23
|
-
}
|
|
24
|
-
if (failed) {
|
|
23
|
+
}
|
|
24
|
+
if (failed && !options?.ignoreFailures) {
|
|
25
25
|
throw INSTANTIATION_FAILED;
|
|
26
26
|
}
|
|
27
27
|
return results;
|
|
@@ -55,7 +55,7 @@ function resolveInputDataContainer(extensionData, attachment, inputName, collect
|
|
|
55
55
|
if (value === void 0 && !ref.config.optional) {
|
|
56
56
|
const expected = extensionData.filter((r) => !r.config.optional).map((r) => `'${r.id}'`).join(", ");
|
|
57
57
|
const provided = [...attachment.instance?.getDataRefs() ?? []].map((r) => `'${r.id}'`).join(", ");
|
|
58
|
-
collector.report({
|
|
58
|
+
collector.child({ node: attachment }).report({
|
|
59
59
|
code: "EXTENSION_INPUT_DATA_MISSING",
|
|
60
60
|
message: `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`
|
|
61
61
|
});
|
|
@@ -139,10 +139,26 @@ function resolveV1Inputs(inputMap, attachments) {
|
|
|
139
139
|
})
|
|
140
140
|
);
|
|
141
141
|
}
|
|
142
|
-
function resolveV2Inputs(inputMap, attachments, parentCollector) {
|
|
142
|
+
function resolveV2Inputs(inputMap, attachments, parentCollector, node) {
|
|
143
143
|
return mapValues(inputMap, (input, inputName) => {
|
|
144
|
-
const
|
|
144
|
+
const allAttachedNodes = attachments.get(inputName) ?? [];
|
|
145
145
|
const collector = parentCollector.child({ inputName });
|
|
146
|
+
const inputPluginId = node.spec.plugin.pluginId;
|
|
147
|
+
const attachedNodes = input.config.internal ? allAttachedNodes.filter((attachment) => {
|
|
148
|
+
const attachmentPluginId = attachment.spec.plugin.pluginId;
|
|
149
|
+
if (attachmentPluginId !== inputPluginId) {
|
|
150
|
+
collector.report({
|
|
151
|
+
code: "EXTENSION_INPUT_INTERNAL_IGNORED",
|
|
152
|
+
message: `extension '${attachment.spec.id}' from plugin '${attachmentPluginId}' attached to input '${inputName}' on '${node.spec.id}' was ignored, the input is marked as internal and attached extensions must therefore be provided via an override or a module for the '${inputPluginId}' plugin, not the '${attachmentPluginId}' plugin`,
|
|
153
|
+
context: {
|
|
154
|
+
extensionId: attachment.spec.id,
|
|
155
|
+
plugin: attachment.spec.plugin
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}) : allAttachedNodes;
|
|
146
162
|
if (input.config.singleton) {
|
|
147
163
|
if (attachedNodes.length > 1) {
|
|
148
164
|
const attachedNodeIds = attachedNodes.map((e) => e.spec.id).join("', '");
|
|
@@ -161,12 +177,25 @@ function resolveV2Inputs(inputMap, attachments, parentCollector) {
|
|
|
161
177
|
}
|
|
162
178
|
return void 0;
|
|
163
179
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
180
|
+
try {
|
|
181
|
+
return resolveInputDataContainer(
|
|
182
|
+
input.extensionData,
|
|
183
|
+
attachedNodes[0],
|
|
184
|
+
inputName,
|
|
185
|
+
collector
|
|
186
|
+
);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
if (error === INSTANTIATION_FAILED) {
|
|
189
|
+
if (input.config.optional) {
|
|
190
|
+
return void 0;
|
|
191
|
+
}
|
|
192
|
+
collector.report({
|
|
193
|
+
code: "EXTENSION_ATTACHMENT_MISSING",
|
|
194
|
+
message: `input '${inputName}' is required but it failed to be instantiated`
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
170
199
|
}
|
|
171
200
|
return mapWithFailures(
|
|
172
201
|
attachedNodes,
|
|
@@ -175,7 +204,8 @@ function resolveV2Inputs(inputMap, attachments, parentCollector) {
|
|
|
175
204
|
attachment,
|
|
176
205
|
inputName,
|
|
177
206
|
collector
|
|
178
|
-
)
|
|
207
|
+
),
|
|
208
|
+
{ ignoreFailures: true }
|
|
179
209
|
);
|
|
180
210
|
});
|
|
181
211
|
}
|
|
@@ -228,7 +258,8 @@ function createAppNodeInstance(options) {
|
|
|
228
258
|
inputs: resolveV2Inputs(
|
|
229
259
|
internalExtension.inputs,
|
|
230
260
|
attachments,
|
|
231
|
-
collector
|
|
261
|
+
collector,
|
|
262
|
+
node
|
|
232
263
|
)
|
|
233
264
|
};
|
|
234
265
|
const outputDataValues = options.extensionFactoryMiddleware ? createExtensionDataContainer(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instantiateAppNodeTree.esm.js","sources":["../../../../../frontend-app-api/src/tree/instantiateAppNodeTree.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ApiHolder,\n ExtensionDataContainer,\n ExtensionDataRef,\n ExtensionFactoryMiddleware,\n ExtensionInput,\n ResolvedExtensionInputs,\n} from '@backstage/frontend-plugin-api';\nimport mapValues from 'lodash/mapValues';\nimport { AppNode, AppNodeInstance } from '@backstage/frontend-plugin-api';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\nimport { createExtensionDataContainer } from '@internal/frontend';\nimport { ErrorCollector } from '../wiring/createErrorCollector';\n\nconst INSTANTIATION_FAILED = new Error('Instantiation failed');\n\n/**\n * Like `array.map`, but if `INSTANTIATION_FAILED` is thrown, the iteration will continue but afterwards re-throw `INSTANTIATION_FAILED`\n * @returns\n */\nfunction mapWithFailures<T, U>(\n iterable: Iterable<T>,\n callback: (item: T) => U,\n): U[] {\n let failed = false;\n const results = Array.from(iterable).map(item => {\n try {\n return callback(item);\n } catch (error) {\n if (error === INSTANTIATION_FAILED) {\n failed = true;\n } else {\n throw error;\n }\n return null as any;\n }\n });\n if (failed) {\n throw INSTANTIATION_FAILED;\n }\n return results;\n}\n\ntype Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\nfunction resolveV1InputDataMap(\n dataMap: {\n [name in string]: ExtensionDataRef;\n },\n attachment: AppNode,\n inputName: string,\n) {\n return Object.fromEntries(\n mapWithFailures(Object.entries(dataMap), ([key, ref]) => {\n const value = attachment.instance?.getData(ref);\n if (value === undefined && !ref.config.optional) {\n const expected = Object.values(dataMap)\n .filter(r => !r.config.optional)\n .map(r => `'${r.id}'`)\n .join(', ');\n\n const provided = [...(attachment.instance?.getDataRefs() ?? [])]\n .map(r => `'${r.id}'`)\n .join(', ');\n\n throw new Error(\n `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`,\n );\n }\n return [key, value];\n }),\n );\n}\n\nfunction resolveInputDataContainer(\n extensionData: Array<ExtensionDataRef>,\n attachment: AppNode,\n inputName: string,\n collector: ErrorCollector<{ node: AppNode; inputName: string }>,\n): { node: AppNode } & ExtensionDataContainer<ExtensionDataRef> {\n const dataMap = new Map<string, unknown>();\n\n mapWithFailures(extensionData, ref => {\n if (dataMap.has(ref.id)) {\n collector.report({\n code: 'EXTENSION_INPUT_DATA_IGNORED',\n message: `Unexpected duplicate input data '${ref.id}'`,\n });\n return;\n }\n\n const value = attachment.instance?.getData(ref);\n if (value === undefined && !ref.config.optional) {\n const expected = extensionData\n .filter(r => !r.config.optional)\n .map(r => `'${r.id}'`)\n .join(', ');\n\n const provided = [...(attachment.instance?.getDataRefs() ?? [])]\n .map(r => `'${r.id}'`)\n .join(', ');\n\n collector.report({\n code: 'EXTENSION_INPUT_DATA_MISSING',\n message: `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`,\n });\n throw INSTANTIATION_FAILED;\n }\n\n dataMap.set(ref.id, value);\n });\n\n return {\n node: attachment,\n get(ref) {\n return dataMap.get(ref.id);\n },\n *[Symbol.iterator]() {\n for (const [id, value] of dataMap) {\n // TODO: Would be better to be able to create a new instance using the ref here instead\n yield {\n $$type: '@backstage/ExtensionDataValue',\n id,\n value,\n };\n }\n },\n } as { node: AppNode } & ExtensionDataContainer<ExtensionDataRef>;\n}\n\nfunction reportUndeclaredAttachments(\n id: string,\n inputMap: { [name in string]: unknown },\n attachments: ReadonlyMap<string, AppNode[]>,\n) {\n const undeclaredAttachments = Array.from(attachments.entries()).filter(\n ([inputName]) => inputMap[inputName] === undefined,\n );\n\n const inputNames = Object.keys(inputMap);\n\n for (const [name, nodes] of undeclaredAttachments) {\n const pl = nodes.length > 1;\n // eslint-disable-next-line no-console\n console.warn(\n [\n `The extension${pl ? 's' : ''} '${nodes\n .map(n => n.spec.id)\n .join(\"', '\")}' ${pl ? 'are' : 'is'}`,\n `attached to the input '${name}' of the extension '${id}', but it`,\n inputNames.length === 0\n ? 'has no inputs'\n : `has no such input (candidates are '${inputNames.join(\"', '\")}')`,\n ].join(' '),\n );\n }\n}\n\nfunction resolveV1Inputs(\n inputMap: {\n [inputName in string]: {\n $$type: '@backstage/ExtensionInput';\n extensionData: {\n [name in string]: ExtensionDataRef;\n };\n config: { optional: boolean; singleton: boolean };\n };\n },\n attachments: ReadonlyMap<string, AppNode[]>,\n) {\n return Object.fromEntries(\n mapWithFailures(Object.entries(inputMap), ([inputName, input]) => {\n const attachedNodes = attachments.get(inputName) ?? [];\n\n if (input.config.singleton) {\n if (attachedNodes.length > 1) {\n const attachedNodeIds = attachedNodes.map(e => e.spec.id);\n throw Error(\n `expected ${\n input.config.optional ? 'at most' : 'exactly'\n } one '${inputName}' input but received multiple: '${attachedNodeIds.join(\n \"', '\",\n )}'`,\n );\n } else if (attachedNodes.length === 0) {\n if (input.config.optional) {\n return [inputName, undefined];\n }\n throw Error(`input '${inputName}' is required but was not received`);\n }\n return [\n inputName,\n {\n node: attachedNodes[0],\n output: resolveV1InputDataMap(\n input.extensionData,\n attachedNodes[0],\n inputName,\n ),\n },\n ];\n }\n\n return [\n inputName,\n attachedNodes.map(attachment => ({\n node: attachment,\n output: resolveV1InputDataMap(\n input.extensionData,\n attachment,\n inputName,\n ),\n })),\n ];\n }),\n ) as {\n [inputName in string]: {\n node: AppNode;\n output: {\n [name in string]: unknown;\n };\n };\n };\n}\n\nfunction resolveV2Inputs(\n inputMap: { [inputName in string]: ExtensionInput },\n attachments: ReadonlyMap<string, AppNode[]>,\n parentCollector: ErrorCollector<{ node: AppNode }>,\n): ResolvedExtensionInputs<{ [inputName in string]: ExtensionInput }> {\n return mapValues(inputMap, (input, inputName) => {\n const attachedNodes = attachments.get(inputName) ?? [];\n const collector = parentCollector.child({ inputName });\n\n if (input.config.singleton) {\n if (attachedNodes.length > 1) {\n const attachedNodeIds = attachedNodes.map(e => e.spec.id).join(\"', '\");\n collector.report({\n code: 'EXTENSION_ATTACHMENT_CONFLICT',\n message: `expected ${\n input.config.optional ? 'at most' : 'exactly'\n } one '${inputName}' input but received multiple: '${attachedNodeIds}'`,\n });\n throw INSTANTIATION_FAILED;\n } else if (attachedNodes.length === 0) {\n if (!input.config.optional) {\n collector.report({\n code: 'EXTENSION_ATTACHMENT_MISSING',\n message: `input '${inputName}' is required but was not received`,\n });\n throw INSTANTIATION_FAILED;\n }\n return undefined;\n }\n return resolveInputDataContainer(\n input.extensionData,\n attachedNodes[0],\n inputName,\n collector,\n );\n }\n\n return mapWithFailures(attachedNodes, attachment =>\n resolveInputDataContainer(\n input.extensionData,\n attachment,\n inputName,\n collector,\n ),\n );\n }) as ResolvedExtensionInputs<{ [inputName in string]: ExtensionInput }>;\n}\n\n/** @internal */\nexport function createAppNodeInstance(options: {\n extensionFactoryMiddleware?: ExtensionFactoryMiddleware;\n node: AppNode;\n apis: ApiHolder;\n attachments: ReadonlyMap<string, AppNode[]>;\n collector: ErrorCollector;\n}): AppNodeInstance | undefined {\n const { node, apis, attachments } = options;\n const collector = options.collector.child({ node });\n const { id, extension, config } = node.spec;\n const extensionData = new Map<string, unknown>();\n const extensionDataRefs = new Set<ExtensionDataRef<unknown>>();\n\n let parsedConfig: { [x: string]: any };\n try {\n parsedConfig = extension.configSchema?.parse(config ?? {}) as {\n [x: string]: any;\n };\n } catch (e) {\n collector.report({\n code: 'EXTENSION_CONFIGURATION_INVALID',\n message: `Invalid configuration for extension '${id}'; caused by ${e}`,\n });\n return undefined;\n }\n\n try {\n const internalExtension = toInternalExtension(extension);\n\n if (process.env.NODE_ENV !== 'production') {\n reportUndeclaredAttachments(id, internalExtension.inputs, attachments);\n }\n\n if (internalExtension.version === 'v1') {\n const namedOutputs = internalExtension.factory({\n node,\n apis,\n config: parsedConfig,\n inputs: resolveV1Inputs(internalExtension.inputs, attachments),\n });\n\n for (const [name, output] of Object.entries(namedOutputs)) {\n const ref = internalExtension.output[name];\n if (!ref) {\n throw new Error(`unknown output provided via '${name}'`);\n }\n if (extensionData.has(ref.id)) {\n throw new Error(\n `duplicate extension data '${ref.id}' received via output '${name}'`,\n );\n }\n extensionData.set(ref.id, output);\n extensionDataRefs.add(ref);\n }\n } else if (internalExtension.version === 'v2') {\n const context = {\n node,\n apis,\n config: parsedConfig,\n inputs: resolveV2Inputs(\n internalExtension.inputs,\n attachments,\n collector,\n ),\n };\n const outputDataValues = options.extensionFactoryMiddleware\n ? createExtensionDataContainer(\n options.extensionFactoryMiddleware(overrideContext => {\n return createExtensionDataContainer(\n internalExtension.factory({\n node: context.node,\n apis: context.apis,\n inputs: context.inputs,\n config: overrideContext?.config ?? context.config,\n }),\n 'extension factory',\n );\n }, context),\n 'extension factory middleware',\n )\n : internalExtension.factory(context);\n\n if (\n typeof outputDataValues !== 'object' ||\n !outputDataValues?.[Symbol.iterator]\n ) {\n throw new Error('extension factory did not provide an iterable object');\n }\n\n const outputDataMap = new Map<string, unknown>();\n mapWithFailures(outputDataValues, value => {\n if (outputDataMap.has(value.id)) {\n collector.report({\n code: 'EXTENSION_OUTPUT_CONFLICT',\n message: `extension factory output duplicate data '${value.id}'`,\n context: {\n dataRefId: value.id,\n },\n });\n throw INSTANTIATION_FAILED;\n } else {\n outputDataMap.set(value.id, value.value);\n }\n });\n\n for (const ref of internalExtension.output) {\n const value = outputDataMap.get(ref.id);\n outputDataMap.delete(ref.id);\n if (value === undefined) {\n if (!ref.config.optional) {\n collector.report({\n code: 'EXTENSION_OUTPUT_MISSING',\n message: `missing required extension data output '${ref.id}'`,\n context: {\n dataRefId: ref.id,\n },\n });\n throw INSTANTIATION_FAILED;\n }\n } else {\n extensionData.set(ref.id, value);\n extensionDataRefs.add(ref);\n }\n }\n\n if (outputDataMap.size > 0) {\n for (const dataRefId of outputDataMap.keys()) {\n // TODO: Make this a warning\n collector.report({\n code: 'EXTENSION_OUTPUT_IGNORED',\n message: `unexpected output '${dataRefId}'`,\n context: {\n dataRefId: dataRefId,\n },\n });\n }\n }\n } else {\n collector.report({\n code: 'EXTENSION_INVALID',\n message: `unexpected extension version '${\n (internalExtension as any).version\n }'`,\n });\n throw INSTANTIATION_FAILED;\n }\n } catch (e) {\n if (e !== INSTANTIATION_FAILED) {\n collector.report({\n code: 'EXTENSION_FACTORY_ERROR',\n message: `Failed to instantiate extension '${id}'${\n e.name === 'Error' ? `, ${e.message}` : `; caused by ${e}`\n }`,\n });\n }\n return undefined;\n }\n\n return {\n getDataRefs() {\n return extensionDataRefs.values();\n },\n getData<T>(ref: ExtensionDataRef<T>): T | undefined {\n return extensionData.get(ref.id) as T | undefined;\n },\n };\n}\n\n/**\n * Starting at the provided node, instantiate all reachable nodes in the tree that have not been disabled.\n * @internal\n */\nexport function instantiateAppNodeTree(\n rootNode: AppNode,\n apis: ApiHolder,\n collector: ErrorCollector,\n extensionFactoryMiddleware?: ExtensionFactoryMiddleware,\n): boolean {\n function createInstance(node: AppNode): AppNodeInstance | undefined {\n if (node.instance) {\n return node.instance;\n }\n if (node.spec.disabled) {\n return undefined;\n }\n\n const instantiatedAttachments = new Map<string, AppNode[]>();\n\n for (const [input, children] of node.edges.attachments) {\n const instantiatedChildren = children.flatMap(child => {\n const childInstance = createInstance(child);\n if (!childInstance) {\n return [];\n }\n return [child];\n });\n if (instantiatedChildren.length > 0) {\n instantiatedAttachments.set(input, instantiatedChildren);\n }\n }\n\n (node as Mutable<AppNode>).instance = createAppNodeInstance({\n extensionFactoryMiddleware,\n node,\n apis,\n attachments: instantiatedAttachments,\n collector,\n });\n\n return node.instance;\n }\n\n return createInstance(rootNode) !== undefined;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"instantiateAppNodeTree.esm.js","sources":["../../../../../frontend-app-api/src/tree/instantiateAppNodeTree.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ApiHolder,\n ExtensionDataContainer,\n ExtensionDataRef,\n ExtensionFactoryMiddleware,\n ExtensionInput,\n ResolvedExtensionInputs,\n} from '@backstage/frontend-plugin-api';\nimport mapValues from 'lodash/mapValues';\nimport { AppNode, AppNodeInstance } from '@backstage/frontend-plugin-api';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\nimport { createExtensionDataContainer } from '@internal/frontend';\nimport { ErrorCollector } from '../wiring/createErrorCollector';\n\nconst INSTANTIATION_FAILED = new Error('Instantiation failed');\n\n/**\n * Like `array.map`, but if `INSTANTIATION_FAILED` is thrown, the iteration will continue but afterwards re-throw `INSTANTIATION_FAILED`\n * @returns\n */\nfunction mapWithFailures<T, U>(\n iterable: Iterable<T>,\n callback: (item: T) => U,\n options?: { ignoreFailures?: boolean },\n): U[] {\n let failed = false;\n const results = [];\n for (const item of iterable) {\n try {\n results.push(callback(item));\n } catch (error) {\n if (error === INSTANTIATION_FAILED) {\n failed = true;\n } else {\n throw error;\n }\n }\n }\n if (failed && !options?.ignoreFailures) {\n throw INSTANTIATION_FAILED;\n }\n return results;\n}\n\ntype Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\nfunction resolveV1InputDataMap(\n dataMap: {\n [name in string]: ExtensionDataRef;\n },\n attachment: AppNode,\n inputName: string,\n) {\n return Object.fromEntries(\n mapWithFailures(Object.entries(dataMap), ([key, ref]) => {\n const value = attachment.instance?.getData(ref);\n if (value === undefined && !ref.config.optional) {\n const expected = Object.values(dataMap)\n .filter(r => !r.config.optional)\n .map(r => `'${r.id}'`)\n .join(', ');\n\n const provided = [...(attachment.instance?.getDataRefs() ?? [])]\n .map(r => `'${r.id}'`)\n .join(', ');\n\n throw new Error(\n `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`,\n );\n }\n return [key, value];\n }),\n );\n}\n\nfunction resolveInputDataContainer(\n extensionData: Array<ExtensionDataRef>,\n attachment: AppNode,\n inputName: string,\n collector: ErrorCollector<{ node: AppNode; inputName: string }>,\n): { node: AppNode } & ExtensionDataContainer<ExtensionDataRef> {\n const dataMap = new Map<string, unknown>();\n\n mapWithFailures(extensionData, ref => {\n if (dataMap.has(ref.id)) {\n collector.report({\n code: 'EXTENSION_INPUT_DATA_IGNORED',\n message: `Unexpected duplicate input data '${ref.id}'`,\n });\n return;\n }\n\n const value = attachment.instance?.getData(ref);\n if (value === undefined && !ref.config.optional) {\n const expected = extensionData\n .filter(r => !r.config.optional)\n .map(r => `'${r.id}'`)\n .join(', ');\n\n const provided = [...(attachment.instance?.getDataRefs() ?? [])]\n .map(r => `'${r.id}'`)\n .join(', ');\n\n collector.child({ node: attachment }).report({\n code: 'EXTENSION_INPUT_DATA_MISSING',\n message: `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`,\n });\n throw INSTANTIATION_FAILED;\n }\n\n dataMap.set(ref.id, value);\n });\n\n return {\n node: attachment,\n get(ref) {\n return dataMap.get(ref.id);\n },\n *[Symbol.iterator]() {\n for (const [id, value] of dataMap) {\n // TODO: Would be better to be able to create a new instance using the ref here instead\n yield {\n $$type: '@backstage/ExtensionDataValue',\n id,\n value,\n };\n }\n },\n } as { node: AppNode } & ExtensionDataContainer<ExtensionDataRef>;\n}\n\nfunction reportUndeclaredAttachments(\n id: string,\n inputMap: { [name in string]: unknown },\n attachments: ReadonlyMap<string, AppNode[]>,\n) {\n const undeclaredAttachments = Array.from(attachments.entries()).filter(\n ([inputName]) => inputMap[inputName] === undefined,\n );\n\n const inputNames = Object.keys(inputMap);\n\n for (const [name, nodes] of undeclaredAttachments) {\n const pl = nodes.length > 1;\n // eslint-disable-next-line no-console\n console.warn(\n [\n `The extension${pl ? 's' : ''} '${nodes\n .map(n => n.spec.id)\n .join(\"', '\")}' ${pl ? 'are' : 'is'}`,\n `attached to the input '${name}' of the extension '${id}', but it`,\n inputNames.length === 0\n ? 'has no inputs'\n : `has no such input (candidates are '${inputNames.join(\"', '\")}')`,\n ].join(' '),\n );\n }\n}\n\nfunction resolveV1Inputs(\n inputMap: {\n [inputName in string]: {\n $$type: '@backstage/ExtensionInput';\n extensionData: {\n [name in string]: ExtensionDataRef;\n };\n config: { optional: boolean; singleton: boolean };\n };\n },\n attachments: ReadonlyMap<string, AppNode[]>,\n) {\n return Object.fromEntries(\n mapWithFailures(Object.entries(inputMap), ([inputName, input]) => {\n const attachedNodes = attachments.get(inputName) ?? [];\n\n if (input.config.singleton) {\n if (attachedNodes.length > 1) {\n const attachedNodeIds = attachedNodes.map(e => e.spec.id);\n throw Error(\n `expected ${\n input.config.optional ? 'at most' : 'exactly'\n } one '${inputName}' input but received multiple: '${attachedNodeIds.join(\n \"', '\",\n )}'`,\n );\n } else if (attachedNodes.length === 0) {\n if (input.config.optional) {\n return [inputName, undefined];\n }\n throw Error(`input '${inputName}' is required but was not received`);\n }\n return [\n inputName,\n {\n node: attachedNodes[0],\n output: resolveV1InputDataMap(\n input.extensionData,\n attachedNodes[0],\n inputName,\n ),\n },\n ];\n }\n\n return [\n inputName,\n attachedNodes.map(attachment => ({\n node: attachment,\n output: resolveV1InputDataMap(\n input.extensionData,\n attachment,\n inputName,\n ),\n })),\n ];\n }),\n ) as {\n [inputName in string]: {\n node: AppNode;\n output: {\n [name in string]: unknown;\n };\n };\n };\n}\n\nfunction resolveV2Inputs(\n inputMap: { [inputName in string]: ExtensionInput },\n attachments: ReadonlyMap<string, AppNode[]>,\n parentCollector: ErrorCollector<{ node: AppNode }>,\n node: AppNode,\n): ResolvedExtensionInputs<{ [inputName in string]: ExtensionInput }> {\n return mapValues(inputMap, (input, inputName) => {\n const allAttachedNodes = attachments.get(inputName) ?? [];\n const collector = parentCollector.child({ inputName });\n const inputPluginId = node.spec.plugin.pluginId;\n\n const attachedNodes = input.config.internal\n ? allAttachedNodes.filter(attachment => {\n const attachmentPluginId = attachment.spec.plugin.pluginId;\n if (attachmentPluginId !== inputPluginId) {\n collector.report({\n code: 'EXTENSION_INPUT_INTERNAL_IGNORED',\n message:\n `extension '${attachment.spec.id}' from plugin '${attachmentPluginId}' attached to input '${inputName}' on '${node.spec.id}' was ignored, ` +\n `the input is marked as internal and attached extensions must therefore be provided via an override or a module for the '${inputPluginId}' plugin, not the '${attachmentPluginId}' plugin`,\n context: {\n extensionId: attachment.spec.id,\n plugin: attachment.spec.plugin,\n },\n });\n return false;\n }\n return true;\n })\n : allAttachedNodes;\n\n if (input.config.singleton) {\n if (attachedNodes.length > 1) {\n const attachedNodeIds = attachedNodes.map(e => e.spec.id).join(\"', '\");\n collector.report({\n code: 'EXTENSION_ATTACHMENT_CONFLICT',\n message: `expected ${\n input.config.optional ? 'at most' : 'exactly'\n } one '${inputName}' input but received multiple: '${attachedNodeIds}'`,\n });\n throw INSTANTIATION_FAILED;\n } else if (attachedNodes.length === 0) {\n if (!input.config.optional) {\n collector.report({\n code: 'EXTENSION_ATTACHMENT_MISSING',\n message: `input '${inputName}' is required but was not received`,\n });\n throw INSTANTIATION_FAILED;\n }\n return undefined;\n }\n try {\n return resolveInputDataContainer(\n input.extensionData,\n attachedNodes[0],\n inputName,\n collector,\n );\n } catch (error) {\n if (error === INSTANTIATION_FAILED) {\n if (input.config.optional) {\n return undefined;\n }\n collector.report({\n code: 'EXTENSION_ATTACHMENT_MISSING',\n message: `input '${inputName}' is required but it failed to be instantiated`,\n });\n }\n throw error;\n }\n }\n\n return mapWithFailures(\n attachedNodes,\n attachment =>\n resolveInputDataContainer(\n input.extensionData,\n attachment,\n inputName,\n collector,\n ),\n { ignoreFailures: true },\n );\n }) as ResolvedExtensionInputs<{ [inputName in string]: ExtensionInput }>;\n}\n\n/** @internal */\nexport function createAppNodeInstance(options: {\n extensionFactoryMiddleware?: ExtensionFactoryMiddleware;\n node: AppNode;\n apis: ApiHolder;\n attachments: ReadonlyMap<string, AppNode[]>;\n collector: ErrorCollector;\n}): AppNodeInstance | undefined {\n const { node, apis, attachments } = options;\n const collector = options.collector.child({ node });\n const { id, extension, config } = node.spec;\n const extensionData = new Map<string, unknown>();\n const extensionDataRefs = new Set<ExtensionDataRef<unknown>>();\n\n let parsedConfig: { [x: string]: any };\n try {\n parsedConfig = extension.configSchema?.parse(config ?? {}) as {\n [x: string]: any;\n };\n } catch (e) {\n collector.report({\n code: 'EXTENSION_CONFIGURATION_INVALID',\n message: `Invalid configuration for extension '${id}'; caused by ${e}`,\n });\n return undefined;\n }\n\n try {\n const internalExtension = toInternalExtension(extension);\n\n if (process.env.NODE_ENV !== 'production') {\n reportUndeclaredAttachments(id, internalExtension.inputs, attachments);\n }\n\n if (internalExtension.version === 'v1') {\n const namedOutputs = internalExtension.factory({\n node,\n apis,\n config: parsedConfig,\n inputs: resolveV1Inputs(internalExtension.inputs, attachments),\n });\n\n for (const [name, output] of Object.entries(namedOutputs)) {\n const ref = internalExtension.output[name];\n if (!ref) {\n throw new Error(`unknown output provided via '${name}'`);\n }\n if (extensionData.has(ref.id)) {\n throw new Error(\n `duplicate extension data '${ref.id}' received via output '${name}'`,\n );\n }\n extensionData.set(ref.id, output);\n extensionDataRefs.add(ref);\n }\n } else if (internalExtension.version === 'v2') {\n const context = {\n node,\n apis,\n config: parsedConfig,\n inputs: resolveV2Inputs(\n internalExtension.inputs,\n attachments,\n collector,\n node,\n ),\n };\n const outputDataValues = options.extensionFactoryMiddleware\n ? createExtensionDataContainer(\n options.extensionFactoryMiddleware(overrideContext => {\n return createExtensionDataContainer(\n internalExtension.factory({\n node: context.node,\n apis: context.apis,\n inputs: context.inputs,\n config: overrideContext?.config ?? context.config,\n }),\n 'extension factory',\n );\n }, context),\n 'extension factory middleware',\n )\n : internalExtension.factory(context);\n\n if (\n typeof outputDataValues !== 'object' ||\n !outputDataValues?.[Symbol.iterator]\n ) {\n throw new Error('extension factory did not provide an iterable object');\n }\n\n const outputDataMap = new Map<string, unknown>();\n mapWithFailures(outputDataValues, value => {\n if (outputDataMap.has(value.id)) {\n collector.report({\n code: 'EXTENSION_OUTPUT_CONFLICT',\n message: `extension factory output duplicate data '${value.id}'`,\n context: {\n dataRefId: value.id,\n },\n });\n throw INSTANTIATION_FAILED;\n } else {\n outputDataMap.set(value.id, value.value);\n }\n });\n\n for (const ref of internalExtension.output) {\n const value = outputDataMap.get(ref.id);\n outputDataMap.delete(ref.id);\n if (value === undefined) {\n if (!ref.config.optional) {\n collector.report({\n code: 'EXTENSION_OUTPUT_MISSING',\n message: `missing required extension data output '${ref.id}'`,\n context: {\n dataRefId: ref.id,\n },\n });\n throw INSTANTIATION_FAILED;\n }\n } else {\n extensionData.set(ref.id, value);\n extensionDataRefs.add(ref);\n }\n }\n\n if (outputDataMap.size > 0) {\n for (const dataRefId of outputDataMap.keys()) {\n // TODO: Make this a warning\n collector.report({\n code: 'EXTENSION_OUTPUT_IGNORED',\n message: `unexpected output '${dataRefId}'`,\n context: {\n dataRefId: dataRefId,\n },\n });\n }\n }\n } else {\n collector.report({\n code: 'EXTENSION_INVALID',\n message: `unexpected extension version '${\n (internalExtension as any).version\n }'`,\n });\n throw INSTANTIATION_FAILED;\n }\n } catch (e) {\n if (e !== INSTANTIATION_FAILED) {\n collector.report({\n code: 'EXTENSION_FACTORY_ERROR',\n message: `Failed to instantiate extension '${id}'${\n e.name === 'Error' ? `, ${e.message}` : `; caused by ${e}`\n }`,\n });\n }\n return undefined;\n }\n\n return {\n getDataRefs() {\n return extensionDataRefs.values();\n },\n getData<T>(ref: ExtensionDataRef<T>): T | undefined {\n return extensionData.get(ref.id) as T | undefined;\n },\n };\n}\n\n/**\n * Starting at the provided node, instantiate all reachable nodes in the tree that have not been disabled.\n * @internal\n */\nexport function instantiateAppNodeTree(\n rootNode: AppNode,\n apis: ApiHolder,\n collector: ErrorCollector,\n extensionFactoryMiddleware?: ExtensionFactoryMiddleware,\n): boolean {\n function createInstance(node: AppNode): AppNodeInstance | undefined {\n if (node.instance) {\n return node.instance;\n }\n if (node.spec.disabled) {\n return undefined;\n }\n\n const instantiatedAttachments = new Map<string, AppNode[]>();\n\n for (const [input, children] of node.edges.attachments) {\n const instantiatedChildren = children.flatMap(child => {\n const childInstance = createInstance(child);\n if (!childInstance) {\n return [];\n }\n return [child];\n });\n if (instantiatedChildren.length > 0) {\n instantiatedAttachments.set(input, instantiatedChildren);\n }\n }\n\n (node as Mutable<AppNode>).instance = createAppNodeInstance({\n extensionFactoryMiddleware,\n node,\n apis,\n attachments: instantiatedAttachments,\n collector,\n });\n\n return node.instance;\n }\n\n return createInstance(rootNode) !== undefined;\n}\n"],"names":[],"mappings":"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"}
|