From fd43ad8144f8bff34e8c0d0837c7289b0fec8123 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 24 Oct 2023 14:33:10 +0200 Subject: [PATCH 1/9] docs/frontend-system: add introduction Signed-off-by: Patrik Oldsberg --- docs/frontend-system/index.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 docs/frontend-system/index.md diff --git a/docs/frontend-system/index.md b/docs/frontend-system/index.md new file mode 100644 index 0000000000..3970c09f00 --- /dev/null +++ b/docs/frontend-system/index.md @@ -0,0 +1,15 @@ +--- +id: index +title: The Frontend System +sidebar_label: Introduction +# prettier-ignore +description: The Frontend System +--- + +> **NOTE: The new frontend system is in a highly experimental phase** + +## Status + +The new frontend system is in an experimental phase and we do not recommend any plugins or apps to migrate. + +You can find an example app setup in [the `app-next` package](https://github.com/backstage/backstage/tree/master/packages/app-next). From b21d5d2f69ccbdb8992723be9fbd6904c2623174 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Oct 2023 10:55:43 +0200 Subject: [PATCH 2/9] docs/frontend-system: add architecture intro doc Signed-off-by: Patrik Oldsberg --- .../architecture-building-blocks.drawio.svg | 331 ++++++++++++++++++ docs/frontend-system/architecture/01-index.md | 51 +++ 2 files changed, 382 insertions(+) create mode 100644 docs/assets/frontend-system/architecture-building-blocks.drawio.svg create mode 100644 docs/frontend-system/architecture/01-index.md diff --git a/docs/assets/frontend-system/architecture-building-blocks.drawio.svg b/docs/assets/frontend-system/architecture-building-blocks.drawio.svg new file mode 100644 index 0000000000..5be95e354d --- /dev/null +++ b/docs/assets/frontend-system/architecture-building-blocks.drawio.svg @@ -0,0 +1,331 @@ + + + + + + + +
+
+
+ App +
+
+
+
+ + App + +
+
+ + + + +
+
+
+ Extensions +
+
+
+
+ + Extensions + +
+
+ + + + + +
+
+
+ Provide +
+
+
+
+ + Provide + +
+
+ + + + +
+
+
+ Plugins +
+
+
+
+ + Plugins + +
+
+ + + + + +
+
+
+ Instantiate +
+
+
+
+ + Instantiate + +
+
+ + + + + +
+
+
+ Install +
+
+
+
+ + Install + +
+
+ + + + +
+
+
+ Utility APIs +
+
+
+
+ + Utility APIs + +
+
+ + + + + +
+
+
+ Instantiate +
+
+
+
+ + Instantiate + +
+
+ + + + + +
+
+
+ Provide & Use +
+
+
+
+ + Provide & Use + +
+
+ + + + +
+
+
+ Extension Overrides +
+
+
+
+ + Extension Ove... + +
+
+ + + + +
+
+
+ Routes +
+
+
+
+ + Routes + +
+
+ + + + + +
+
+
+ Use +
+
+
+
+ + Use + +
+
+ + + + + +
+
+
+ Provide +
+
+
+
+ + Provide + +
+
+ + + + + +
+
+
+ Resolve +
+
+
+
+ + Resolve + +
+
+ + + + + +
+
+
+ Provide +
+
+
+
+ + Provide + +
+
+ + + + + +
+
+
+ Install +
+
+
+
+ + Install + +
+
+ + + + + +
+
+
+ Override +
+
+
+
+ + Override + +
+
+ + + + + +
+
+
+ Provide +
+
+
+
+ + Provide + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
diff --git a/docs/frontend-system/architecture/01-index.md b/docs/frontend-system/architecture/01-index.md new file mode 100644 index 0000000000..972bd39a4d --- /dev/null +++ b/docs/frontend-system/architecture/01-index.md @@ -0,0 +1,51 @@ +--- +id: index +title: Frontend System Architecture +sidebar_label: Overview +# prettier-ignore +description: The structure and architecture of the new Frontend System +--- + +> **NOTE: The new frontend system is in a highly experimental phase** + +## Building Blocks + +This section introduces the high-level building blocks upon which this new +system is built. Most of these concepts exist in our current system too, although +in some cases you need to squint quite a lot to see the similarity. + +Regardless of whether you are setting up your own backstage instance, +developing plugins, or extending plugins with new features, it is +important to understand these concepts. + +The diagram below provides an overview of the different building blocks, and the other blocks that each of them interact with. + +![frontend system building blocks diagram](../../assets/frontend-system/architecture-building-blocks.drawio.svg) + +### App + +This is the app instance itself that you create and use as the root of your Backstage frontend application. It does not have any direct functionality in and of itself, but is simply responsible for wiring things together. + +### Plugins + +Plugins provide the actual features inside an app. The size of a plugin can range from a tiny component to an entire new system in which other plugins can be composed and integrated. Plugins can be completely standalone, or build on top of each other to extend existing plugins and augment their features. Plugins can communicate with each other by composing their extensions, or by sharing APIs and routes. + +### Extensions + +Extensions are the building blocks that build out both the visual and non-visual structure of the application. There are both built-in extensions provided by the app itself, as well as extensions provided by plugins. Each extension is attached to a parent with which it shares data, and can have any number of children of its own. It is up to the app to wire together all extensions into a single tree known as the app extension tree. It is from this structure that the entire app can then be instantiated and rendered. + +### Extension Overrides + +In addition to the built-in extensions and extensions provided by plugins, it is also possible to install extension overrides. This is a collection of extensions with high priority that can replace existing extensions. They can for example be used to override an individual extension provided by a plugin, or install a completely new extensions, such as a new app theme. + +### Utility APIs + +Utility APIs provide functionality that makes it easier to build plugins, make it possible for plugins to share functionality with other plugins, as well as serve as a customization point for integrators to change the behavior of the app. Each Utility API is defined by a TypeScript interface as well as a reference used to access the implementations. The implementations of Utility APIs are defined by extensions that are provided and can be overridden the same as any other extension. + +### Routes + +The Backstage routing system adds a layer of indirection that makes it possible for plugins to route to each other's extensions without explicit knowledge of what URL paths the extension are rendered at or if they even exist at all. It makes it possible for plugins to share routes with each other and dynamically generate concrete links at runtime. It is the responsibility of the app to resolve these links to actual URLs, but it is also possible for integrators to define their own route bindings that decide how the links should be resolved. The routing system also lets plugins define internal routes, aiding in the linking to different content in the same plugin. + +## Package structure + +TODO From 0434ba01a0bde1b953ec4df850ab817668119c76 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Oct 2023 10:59:41 +0200 Subject: [PATCH 3/9] docs/frontend-system: initial architecture docs structure Signed-off-by: Patrik Oldsberg --- docs/frontend-system/architecture/02-app.md | 9 +++++++++ docs/frontend-system/architecture/03-plugins.md | 9 +++++++++ docs/frontend-system/architecture/04-extensions.md | 9 +++++++++ .../architecture/05-extension-overrides.md | 9 +++++++++ docs/frontend-system/architecture/06-utility-apis.md | 11 +++++++++++ docs/frontend-system/architecture/07-routes.md | 11 +++++++++++ 6 files changed, 58 insertions(+) create mode 100644 docs/frontend-system/architecture/02-app.md create mode 100644 docs/frontend-system/architecture/03-plugins.md create mode 100644 docs/frontend-system/architecture/04-extensions.md create mode 100644 docs/frontend-system/architecture/05-extension-overrides.md create mode 100644 docs/frontend-system/architecture/06-utility-apis.md create mode 100644 docs/frontend-system/architecture/07-routes.md diff --git a/docs/frontend-system/architecture/02-app.md b/docs/frontend-system/architecture/02-app.md new file mode 100644 index 0000000000..f0c0fd355e --- /dev/null +++ b/docs/frontend-system/architecture/02-app.md @@ -0,0 +1,9 @@ +--- +id: apps +title: App Instances +sidebar_label: App +# prettier-ignore +description: App instances +--- + +> **NOTE: The new frontend system is in a highly experimental phase** diff --git a/docs/frontend-system/architecture/03-plugins.md b/docs/frontend-system/architecture/03-plugins.md new file mode 100644 index 0000000000..a1e85f8407 --- /dev/null +++ b/docs/frontend-system/architecture/03-plugins.md @@ -0,0 +1,9 @@ +--- +id: plugins +title: Frontend Plugins +sidebar_label: Plugins +# prettier-ignore +description: Frontend plugins +--- + +> **NOTE: The new frontend system is in a highly experimental phase** diff --git a/docs/frontend-system/architecture/04-extensions.md b/docs/frontend-system/architecture/04-extensions.md new file mode 100644 index 0000000000..8df91f2b54 --- /dev/null +++ b/docs/frontend-system/architecture/04-extensions.md @@ -0,0 +1,9 @@ +--- +id: extensions +title: Frontend Extensions +sidebar_label: Extensions +# prettier-ignore +description: Frontend extensions +--- + +> **NOTE: The new frontend system is in a highly experimental phase** diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md new file mode 100644 index 0000000000..743a5ea59e --- /dev/null +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -0,0 +1,9 @@ +--- +id: extension overrides +title: Frontend Extension Overrides +sidebar_label: Extension Overrides +# prettier-ignore +description: Frontend extension overrides +--- + +> **NOTE: The new frontend system is in a highly experimental phase** diff --git a/docs/frontend-system/architecture/06-utility-apis.md b/docs/frontend-system/architecture/06-utility-apis.md new file mode 100644 index 0000000000..135f60744a --- /dev/null +++ b/docs/frontend-system/architecture/06-utility-apis.md @@ -0,0 +1,11 @@ +--- +id: utility-apis +title: Utility APIs +sidebar_label: Utility APIs +# prettier-ignore +description: Utility APIs +--- + +> **NOTE: The new frontend system is in a highly experimental phase** + +See [Utility APIs docs](../../api/utility-apis.md). diff --git a/docs/frontend-system/architecture/07-routes.md b/docs/frontend-system/architecture/07-routes.md new file mode 100644 index 0000000000..354eaeda13 --- /dev/null +++ b/docs/frontend-system/architecture/07-routes.md @@ -0,0 +1,11 @@ +--- +id: routes +title: Frontend Routes +sidebar_label: Routes +# prettier-ignore +description: Frontend routes +--- + +> **NOTE: The new frontend system is in a highly experimental phase** + +See [routing system docs](../../plugins/composability.md#routing-system) From f1f658ddddd71620fde3794f3b09c80b348644bd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Oct 2023 14:36:59 +0200 Subject: [PATCH 4/9] docs/frontend-system: initial app instance architecture Signed-off-by: Patrik Oldsberg --- .../architecture-app.drawio.svg | 125 ++++++++++++++++++ docs/frontend-system/architecture/02-app.md | 35 +++++ 2 files changed, 160 insertions(+) create mode 100644 docs/assets/frontend-system/architecture-app.drawio.svg diff --git a/docs/assets/frontend-system/architecture-app.drawio.svg b/docs/assets/frontend-system/architecture-app.drawio.svg new file mode 100644 index 0000000000..ed414dde97 --- /dev/null +++ b/docs/assets/frontend-system/architecture-app.drawio.svg @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/frontend-system/architecture/02-app.md b/docs/frontend-system/architecture/02-app.md index f0c0fd355e..4bbcec225e 100644 --- a/docs/frontend-system/architecture/02-app.md +++ b/docs/frontend-system/architecture/02-app.md @@ -7,3 +7,38 @@ description: App instances --- > **NOTE: The new frontend system is in a highly experimental phase** + +## The App Instance + +The app instance is main entry point for creating a frontend app. It doesn't do much on its own, but is instead responsible for wiring things together that have been provided as features from other parts of the system. + +Below is a simple example of how to create and render an app instance: + +```ts +import ReactDOM from 'react-dom/client'; +import { createApp } from '@backstage/frontend-app-api'; + +// Create your app instance +const app = createApp({ + // Features such as plugins can be installed explicitly, but we will explore other options later on + features: [catalogPlugin], +}); + +// This creates a React element that renders the entire app +const root = app.createRoot(); + +// Just like any other React we need a root element. No server side rendering is used. +const rootEl = document.getElementById('root')!; + +ReactDOM.createRoot(rootEl).render(app); +``` + +We call `createApp` to create a new app instance, which is responsible for wiring together all of the features that we provide to the app. It also provides a set of built-in [Extensions](./04-extensions.md) that help build out the foundations of the app, as well as defaults for many other systems such as [Utility API](./06-utility-apis.md) implementations, components, icons, themes, and how to load configuration. No real work is done at the point of creating the app though, it's all deferred to the rendering of the element returned from `app.createRoot()`. + +It is possible to explicitly install features when creating the app, although typically these will instead be discovered automatically which we'll explore later on. Nevertheless these features are what build out the actual functionality of the app by providing [Extensions](./04-extensions.md). These extensions are wired together by the app into a tree structure known as the app extension tree. Each node in this tree receives data from its child nodes, and pass along data to its parent. The following diagram illustrates the shape of a small app extension tree. + +![frontend system app structure diagram](../../assets/frontend-system/architecture-app.drawio.svg) + +Each node in this tree is an extension with a parent node, children. The colored shapes represent extension data inputs and output, where each color is one unique type of data. You can see that there are both extensions that output data that is ignored by the parent, as well as extensions that accept inputs but do not have any children. There are a couple of different tools at your disposal when creating and extension that lets you define different requirements for your inputs and output, which we will cover in greater details in the [Extensions](./04-extensions.md) section. + +A common type of data that is shared between extensions is React elements and components. These can in turn be rendered by each other in their own React components, which ends up forming a parallel tree of React components that is similar in shape to that of the app extension tree. At the top of the app extension tree is a built-in root extension that among other things outputs a React element. This element also ends up being the root of the parallel React tree, and is rendered by the React element returned by `app.createRoot()`. From bb2ca14a7449458377c9d4fc4b9ae14f7a4c03b8 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Oct 2023 14:48:17 +0200 Subject: [PATCH 5/9] docs/frontend-system: switch order between plugins and extensions Signed-off-by: Patrik Oldsberg --- docs/frontend-system/architecture/01-index.md | 8 ++++---- docs/frontend-system/architecture/02-app.md | 6 +++--- .../architecture/{04-extensions.md => 03-extensions.md} | 0 .../architecture/{03-plugins.md => 04-plugins.md} | 0 4 files changed, 7 insertions(+), 7 deletions(-) rename docs/frontend-system/architecture/{04-extensions.md => 03-extensions.md} (100%) rename docs/frontend-system/architecture/{03-plugins.md => 04-plugins.md} (100%) diff --git a/docs/frontend-system/architecture/01-index.md b/docs/frontend-system/architecture/01-index.md index 972bd39a4d..757a3c338e 100644 --- a/docs/frontend-system/architecture/01-index.md +++ b/docs/frontend-system/architecture/01-index.md @@ -26,14 +26,14 @@ The diagram below provides an overview of the different building blocks, and the This is the app instance itself that you create and use as the root of your Backstage frontend application. It does not have any direct functionality in and of itself, but is simply responsible for wiring things together. -### Plugins - -Plugins provide the actual features inside an app. The size of a plugin can range from a tiny component to an entire new system in which other plugins can be composed and integrated. Plugins can be completely standalone, or build on top of each other to extend existing plugins and augment their features. Plugins can communicate with each other by composing their extensions, or by sharing APIs and routes. - ### Extensions Extensions are the building blocks that build out both the visual and non-visual structure of the application. There are both built-in extensions provided by the app itself, as well as extensions provided by plugins. Each extension is attached to a parent with which it shares data, and can have any number of children of its own. It is up to the app to wire together all extensions into a single tree known as the app extension tree. It is from this structure that the entire app can then be instantiated and rendered. +### Plugins + +Plugins provide the actual features inside an app. The size of a plugin can range from a tiny component to an entire new system in which other plugins can be composed and integrated. Plugins can be completely standalone, or build on top of each other to extend existing plugins and augment their features. Plugins can communicate with each other by composing their extensions, or by sharing Utility APIs and routes. + ### Extension Overrides In addition to the built-in extensions and extensions provided by plugins, it is also possible to install extension overrides. This is a collection of extensions with high priority that can replace existing extensions. They can for example be used to override an individual extension provided by a plugin, or install a completely new extensions, such as a new app theme. diff --git a/docs/frontend-system/architecture/02-app.md b/docs/frontend-system/architecture/02-app.md index 4bbcec225e..6fa8242061 100644 --- a/docs/frontend-system/architecture/02-app.md +++ b/docs/frontend-system/architecture/02-app.md @@ -33,12 +33,12 @@ const rootEl = document.getElementById('root')!; ReactDOM.createRoot(rootEl).render(app); ``` -We call `createApp` to create a new app instance, which is responsible for wiring together all of the features that we provide to the app. It also provides a set of built-in [Extensions](./04-extensions.md) that help build out the foundations of the app, as well as defaults for many other systems such as [Utility API](./06-utility-apis.md) implementations, components, icons, themes, and how to load configuration. No real work is done at the point of creating the app though, it's all deferred to the rendering of the element returned from `app.createRoot()`. +We call `createApp` to create a new app instance, which is responsible for wiring together all of the features that we provide to the app. It also provides a set of built-in [Extensions](./03-extensions.md) that help build out the foundations of the app, as well as defaults for many other systems such as [Utility API](./06-utility-apis.md) implementations, components, icons, themes, and how to load configuration. No real work is done at the point of creating the app though, it's all deferred to the rendering of the element returned from `app.createRoot()`. -It is possible to explicitly install features when creating the app, although typically these will instead be discovered automatically which we'll explore later on. Nevertheless these features are what build out the actual functionality of the app by providing [Extensions](./04-extensions.md). These extensions are wired together by the app into a tree structure known as the app extension tree. Each node in this tree receives data from its child nodes, and pass along data to its parent. The following diagram illustrates the shape of a small app extension tree. +It is possible to explicitly install features when creating the app, although typically these will instead be discovered automatically which we'll explore later on. Nevertheless these features are what build out the actual functionality of the app by providing [Extensions](./03-extensions.md). These extensions are wired together by the app into a tree structure known as the app extension tree. Each node in this tree receives data from its child nodes, and pass along data to its parent. The following diagram illustrates the shape of a small app extension tree. ![frontend system app structure diagram](../../assets/frontend-system/architecture-app.drawio.svg) -Each node in this tree is an extension with a parent node, children. The colored shapes represent extension data inputs and output, where each color is one unique type of data. You can see that there are both extensions that output data that is ignored by the parent, as well as extensions that accept inputs but do not have any children. There are a couple of different tools at your disposal when creating and extension that lets you define different requirements for your inputs and output, which we will cover in greater details in the [Extensions](./04-extensions.md) section. +Each node in this tree is an extension with a parent node, children. The colored shapes represent extension data inputs and output, where each color is one unique type of data. You can see that there are both extensions that output data that is ignored by the parent, as well as extensions that accept inputs but do not have any children. There are a couple of different tools at your disposal when creating and extension that lets you define different requirements for your inputs and output, which we will cover in greater details in the [Extensions](./03-extensions.md) section. A common type of data that is shared between extensions is React elements and components. These can in turn be rendered by each other in their own React components, which ends up forming a parallel tree of React components that is similar in shape to that of the app extension tree. At the top of the app extension tree is a built-in root extension that among other things outputs a React element. This element also ends up being the root of the parallel React tree, and is rendered by the React element returned by `app.createRoot()`. diff --git a/docs/frontend-system/architecture/04-extensions.md b/docs/frontend-system/architecture/03-extensions.md similarity index 100% rename from docs/frontend-system/architecture/04-extensions.md rename to docs/frontend-system/architecture/03-extensions.md diff --git a/docs/frontend-system/architecture/03-plugins.md b/docs/frontend-system/architecture/04-plugins.md similarity index 100% rename from docs/frontend-system/architecture/03-plugins.md rename to docs/frontend-system/architecture/04-plugins.md From e733e6da5689eb9c627bd51afb0ae597108444c3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Oct 2023 17:48:49 +0200 Subject: [PATCH 6/9] docs/frontend-system: initial extension architecture with extension structure Signed-off-by: Patrik Oldsberg --- .../architecture-extension.drawio.svg | 332 ++++++++++++++++++ .../architecture/03-extensions.md | 60 ++++ 2 files changed, 392 insertions(+) create mode 100644 docs/assets/frontend-system/architecture-extension.drawio.svg diff --git a/docs/assets/frontend-system/architecture-extension.drawio.svg b/docs/assets/frontend-system/architecture-extension.drawio.svg new file mode 100644 index 0000000000..d7bbf43ad0 --- /dev/null +++ b/docs/assets/frontend-system/architecture-extension.drawio.svg @@ -0,0 +1,332 @@ + + + + + + + + + + +
+
+
+ Extension +
+
+
+
+ + Extension + +
+
+ + + + +
+
+
+ Output +
+
+
+
+ + Output + +
+
+ + + + + +
+
+
+ Input 1 +
+
+
+
+ + Input 1 + +
+
+ + + + + +
+
+
+ Input 2 +
+
+
+
+ + Input 2 + +
+
+ + + + +
+
+
+ disabled +
+
+
+
+ + disabled + +
+
+ + + + + + + + + + + + +
+
+
+ Output Data +
+
+
+
+ + Output Data + +
+
+ + + + + +
+
+
+ Input Data +
+
+
+
+ + Input Data + +
+
+ + + + + + + + + +
+
+
+ id +
+
+
+
+ + id + +
+
+ + + + +
+
+
+ config schema +
+
+
+
+ + config sch... + +
+
+ + + + +
+
+
+ factory +
+
+
+
+ + factory + +
+
+ + + + + + + + + + +
+
+
+ attachTo +
+
+
+
+ + attachTo + +
+
+ + + + +
+
+
+ config +
+
+
+
+ + config + +
+
+ + + + +
+
+
+ Static +
+
+
+
+ + Static + +
+
+ + + + + +
+
+
+ Configurable +
+
+
+
+ + Configurable + +
+
+ + + + + + +
+
+
+ Extension Data A +
+
+
+
+ + Extension Data A + +
+
+ + + + + +
+
+
+ Extension Data B +
+
+
+
+ + Extension Data B + +
+
+ + + + + +
+
+
+ Extension Data C +
+
+
+
+ + Extension Data C + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
diff --git a/docs/frontend-system/architecture/03-extensions.md b/docs/frontend-system/architecture/03-extensions.md index 8df91f2b54..9f12bc2d1b 100644 --- a/docs/frontend-system/architecture/03-extensions.md +++ b/docs/frontend-system/architecture/03-extensions.md @@ -7,3 +7,63 @@ description: Frontend extensions --- > **NOTE: The new frontend system is in a highly experimental phase** + +As mentioned in the [previous section](./02-app.md), Backstage apps are built up from a tree of extensions. This section will go into more detail about what extensions are, how to create and use them, and how to create your own extensibility patterns. + +## Extension Structure + +Each extensions has a number of different properties that define how it behaves and how it interacts with other extensions and the rest of the app. Some of these properties are fixed, while others can be customized by integrators. The diagram below illustrates the structure of an extension. + +![frontend extension structure diagram](../../assets/frontend-system/architecture-extension.drawio.svg) + +### ID + +The ID of an extension is used to uniquely identity it, and it should ideally by unique across the entire Backstage ecosystem. For each frontend app instance there can only be a single extension for any given ID. Installing multiple extensions with the same ID will either result in an error or one of the extensions will override the others. The ID is also used to reference the extensions from other extensions, in configuration, and in other places such as developer tools and analytics. + +### Output + +The output of an extension is the data that it provides to its parent extension, and ultimately its contribution to the app. The output itself comes in the form of a collection of arbitrary values, anything that can be represented as a TypeScript type. However, each individual output value must be associated with a shared reference known as an extension data reference. You must also use these same references to be able to access individual output values of an extension. + +### Inputs + +The inputs of an extension define the data that it received from its children. Each extension can have multiple different inputs identified by an input name. These inputs each have their own set of data that they expect, which is defined as a collection of extension data references. An extension will only have access to the data that it has explicitly requested from each input. + +### Attachment Point + +The attachment point of an extension decides where in the app extension tree it will be located. It is defined by the ID of the parent extension, as well as the name of the input to attach to. Through the attachment point the extension will share its own output as inputs to the parent extension. An extension can only be attached to an input that matches its own output, it is an error to try to attach an extension to an input the requires data that the extension does not provide in its output. + +The attachment point is one of the configurable properties of an extension, and can be overridden by integrators. In doing so, care must be taken to make sure that one doesn't attach an extension to an incompatible input. Extensions can also only be attached to a single input and parent at a time. This means that the app extension tree can not contain any cycles, as the extension ancestry will either be terminated at the root, or be detached from it. + +### Disabled + +Each extension in the app can be disabled, meaning it will not be instantiated and its parent will effectively not see it in its inputs. When creating an extension you can also specify whether extensions should be disabled by default. This makes it possible to for example install multiple extensions in an app, but only choose to enable one or a few of them depending on the environment. + +The ordering of extensions is sometimes very important, as it may for example affect in which order they show up in the UI. When an extension is toggled from disabled to enabled through configuration it resets the ordering of the extension, pushing it to the end of the list. It is generally recommended to leave extensions as disabled by default if their order is important, allowing for the order in which their are enabled in the configuration to determine their order in the app. + +### Configuration & Configuration Schema + +Each extension can define a configuration schema that describes the configuration that it accepts. This schema is used to validate the configuration provided by integrators, but also to fill in default configuration values. The configuration itself is provided by integrators in order to customize the extension. It is not possible to provide a default configuration of an extension, this must instead be done through defaults in the configuration schema. This allows for a simpler configuration logic where multiple configurations of the same extensions completely replace each other rather than being merged. + +### Factory + +The extension factory is the implementation of the extension itself. It is a function that is provided with any inputs and configuration that the extension received, and must produce the output that it defined. When an app instance starts up it will call the factory function of each extension that is part of the app, starting at leaf nodes and working its way up to the root of the app extension tree. The factory will only be called for active extensions, which is and extension that is not disabled and has an active parent. Extension factories should be lean and not do any heavy lifting or async work, as they are called during the initialization of the app, that should instead be deferred to the values shared through the extension outputs. + +## Creating an Extensions + +TODO + +## Extension Data + +TODO + +## Extension Inputs + +TODO + +## Configuration Schema + +TODO + +## Extension Creators + +TODO From 6adadd1522f7391d4b796e1362c3a3de0667e777 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Oct 2023 20:51:21 +0200 Subject: [PATCH 7/9] docs/frontend-system: some more WIP extension docs Signed-off-by: Patrik Oldsberg --- docs/frontend-system/architecture/02-app.md | 4 +++ .../architecture/03-extensions.md | 35 ++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/02-app.md b/docs/frontend-system/architecture/02-app.md index 6fa8242061..08fc63b480 100644 --- a/docs/frontend-system/architecture/02-app.md +++ b/docs/frontend-system/architecture/02-app.md @@ -42,3 +42,7 @@ It is possible to explicitly install features when creating the app, although ty Each node in this tree is an extension with a parent node, children. The colored shapes represent extension data inputs and output, where each color is one unique type of data. You can see that there are both extensions that output data that is ignored by the parent, as well as extensions that accept inputs but do not have any children. There are a couple of different tools at your disposal when creating and extension that lets you define different requirements for your inputs and output, which we will cover in greater details in the [Extensions](./03-extensions.md) section. A common type of data that is shared between extensions is React elements and components. These can in turn be rendered by each other in their own React components, which ends up forming a parallel tree of React components that is similar in shape to that of the app extension tree. At the top of the app extension tree is a built-in root extension that among other things outputs a React element. This element also ends up being the root of the parallel React tree, and is rendered by the React element returned by `app.createRoot()`. + +## Feature Discovery + +TODO diff --git a/docs/frontend-system/architecture/03-extensions.md b/docs/frontend-system/architecture/03-extensions.md index 9f12bc2d1b..086b8fa815 100644 --- a/docs/frontend-system/architecture/03-extensions.md +++ b/docs/frontend-system/architecture/03-extensions.md @@ -50,7 +50,32 @@ The extension factory is the implementation of the extension itself. It is a fun ## Creating an Extensions -TODO +Extensions are created using the `createExtension` function from `@backstage/frontend-plugin-api`. At minimum you need to provide an ID, attachment point, output definition, and a factory function. The following example shows the creation of a minimal extension: + +```tsx +const extension = createExtension({ + id: 'my-extension', + // This is the attachment point, `id` is the ID of the parent extension, + // while `input` is the name of the input to attach to. + attachTo: { id: 'my-parent', input: 'content' }, + // The output map defines the outputs of the extension. The object keys + // are only used internally to map the outputs of the factory and do + // not need to match the keys of the input. + output: { + element: coreExtensionData.reactElement, + }, + // This factory is called to instantiate the extensions and produce its output. + factory({ bind }) { + bind({ + element:
Hello World
, + }); + }, +}); +``` + +Note that while the `createExtension` is public API and used in many places, it is not typically what you use when building plugins and features. Instead there are many extension creator functions exported by both the core APIs and plugins that make it easier to create extensions for more specific usages. + +... TODO ... ## Extension Data @@ -60,6 +85,10 @@ TODO TODO +## Configuration + +TODO + ## Configuration Schema TODO @@ -67,3 +96,7 @@ TODO ## Extension Creators TODO + +## Extension Boundary + +TODO From 77509d2353beb4df347c08ffc89da54aa3305293 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 6 Nov 2023 12:06:32 -0600 Subject: [PATCH 8/9] Apply suggestions from code review Co-authored-by: Phil Kuang Signed-off-by: Patrik Oldsberg --- docs/frontend-system/architecture/01-index.md | 2 +- docs/frontend-system/architecture/02-app.md | 2 +- docs/frontend-system/architecture/03-extensions.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/frontend-system/architecture/01-index.md b/docs/frontend-system/architecture/01-index.md index 757a3c338e..380ebaa335 100644 --- a/docs/frontend-system/architecture/01-index.md +++ b/docs/frontend-system/architecture/01-index.md @@ -36,7 +36,7 @@ Plugins provide the actual features inside an app. The size of a plugin can rang ### Extension Overrides -In addition to the built-in extensions and extensions provided by plugins, it is also possible to install extension overrides. This is a collection of extensions with high priority that can replace existing extensions. They can for example be used to override an individual extension provided by a plugin, or install a completely new extensions, such as a new app theme. +In addition to the built-in extensions and extensions provided by plugins, it is also possible to install extension overrides. This is a collection of extensions with high priority that can replace existing extensions. They can for example be used to override an individual extension provided by a plugin, or install a completely new extension, such as a new app theme. ### Utility APIs diff --git a/docs/frontend-system/architecture/02-app.md b/docs/frontend-system/architecture/02-app.md index 08fc63b480..52fd20faee 100644 --- a/docs/frontend-system/architecture/02-app.md +++ b/docs/frontend-system/architecture/02-app.md @@ -39,7 +39,7 @@ It is possible to explicitly install features when creating the app, although ty ![frontend system app structure diagram](../../assets/frontend-system/architecture-app.drawio.svg) -Each node in this tree is an extension with a parent node, children. The colored shapes represent extension data inputs and output, where each color is one unique type of data. You can see that there are both extensions that output data that is ignored by the parent, as well as extensions that accept inputs but do not have any children. There are a couple of different tools at your disposal when creating and extension that lets you define different requirements for your inputs and output, which we will cover in greater details in the [Extensions](./03-extensions.md) section. +Each node in this tree is an extension with a parent node and children. The colored shapes represent extension data inputs and output, where each color is one unique type of data. You can see that there are both extensions that output data that is ignored by the parent, as well as extensions that accept inputs but do not have any children. There are a couple of different tools at your disposal when creating and extension that lets you define different requirements for your inputs and output, which we will cover in greater details in the [Extensions](./03-extensions.md) section. A common type of data that is shared between extensions is React elements and components. These can in turn be rendered by each other in their own React components, which ends up forming a parallel tree of React components that is similar in shape to that of the app extension tree. At the top of the app extension tree is a built-in root extension that among other things outputs a React element. This element also ends up being the root of the parallel React tree, and is rendered by the React element returned by `app.createRoot()`. diff --git a/docs/frontend-system/architecture/03-extensions.md b/docs/frontend-system/architecture/03-extensions.md index 086b8fa815..deb4d8985b 100644 --- a/docs/frontend-system/architecture/03-extensions.md +++ b/docs/frontend-system/architecture/03-extensions.md @@ -42,11 +42,11 @@ The ordering of extensions is sometimes very important, as it may for example af ### Configuration & Configuration Schema -Each extension can define a configuration schema that describes the configuration that it accepts. This schema is used to validate the configuration provided by integrators, but also to fill in default configuration values. The configuration itself is provided by integrators in order to customize the extension. It is not possible to provide a default configuration of an extension, this must instead be done through defaults in the configuration schema. This allows for a simpler configuration logic where multiple configurations of the same extensions completely replace each other rather than being merged. +Each extension can define a configuration schema that describes the configuration that it accepts. This schema is used to validate the configuration provided by integrators, but also to fill in default configuration values. The configuration itself is provided by integrators in order to customize the extension. It is not possible to provide a default configuration of an extension, this must instead be done through defaults in the configuration schema. This allows for a simpler configuration logic where multiple configurations of the same extension completely replace each other rather than being merged. ### Factory -The extension factory is the implementation of the extension itself. It is a function that is provided with any inputs and configuration that the extension received, and must produce the output that it defined. When an app instance starts up it will call the factory function of each extension that is part of the app, starting at leaf nodes and working its way up to the root of the app extension tree. The factory will only be called for active extensions, which is and extension that is not disabled and has an active parent. Extension factories should be lean and not do any heavy lifting or async work, as they are called during the initialization of the app, that should instead be deferred to the values shared through the extension outputs. +The extension factory is the implementation of the extension itself. It is a function that is provided with any inputs and configuration that the extension received, and must produce the output that it defined. When an app instance starts up it will call the factory function of each extension that is part of the app, starting at leaf nodes and working its way up to the root of the app extension tree. The factory will only be called for active extensions, which is an extension that is not disabled and has an active parent. Extension factories should be lean and not do any heavy lifting or async work, as they are called during the initialization of the app, that should instead be deferred to the values shared through the extension outputs. ## Creating an Extensions From 1a0b4ab9ad345aad546b4e68d0d2b91797fcf4ca Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 6 Nov 2023 12:21:59 -0600 Subject: [PATCH 9/9] docs/frontend-system: bit more clear example of passing callbacks as output Signed-off-by: Patrik Oldsberg --- docs/frontend-system/architecture/03-extensions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/03-extensions.md b/docs/frontend-system/architecture/03-extensions.md index deb4d8985b..3d08bf1721 100644 --- a/docs/frontend-system/architecture/03-extensions.md +++ b/docs/frontend-system/architecture/03-extensions.md @@ -46,7 +46,9 @@ Each extension can define a configuration schema that describes the configuratio ### Factory -The extension factory is the implementation of the extension itself. It is a function that is provided with any inputs and configuration that the extension received, and must produce the output that it defined. When an app instance starts up it will call the factory function of each extension that is part of the app, starting at leaf nodes and working its way up to the root of the app extension tree. The factory will only be called for active extensions, which is an extension that is not disabled and has an active parent. Extension factories should be lean and not do any heavy lifting or async work, as they are called during the initialization of the app, that should instead be deferred to the values shared through the extension outputs. +The extension factory is the implementation of the extension itself. It is a function that is provided with any inputs and configuration that the extension received, and must produce the output that it defined. When an app instance starts up it will call the factory function of each extension that is part of the app, starting at leaf nodes and working its way up to the root of the app extension tree. The factory will only be called for active extensions, which is an extension that is not disabled and has an active parent. + +Extension factories should be lean and not do any heavy lifting or async work, as they are called during the initialization of the app. For example, if you need to do an expensive computation to generate your output, then prefer outputting a callback that does the computation instead. This allows the parent extension to defer the computation for later so that you avoid blocking the app startup. ## Creating an Extensions